diff --git a/src/app/advertisement-status/advertisementStatus.ts b/src/app/advertisement-status/advertisementStatus.ts new file mode 100644 index 0000000..70a38c4 --- /dev/null +++ b/src/app/advertisement-status/advertisementStatus.ts @@ -0,0 +1,12 @@ + +import { HateoasResource, Resource } from "@lagoshny/ngx-hateoas-client"; + +@HateoasResource('advertisement-status') +export class AdvertisementStatus extends Resource { + id: number = 0; + name: string = ''; + constructor(values: object = {}) { + super(); + Object.assign(this , values); + } +} diff --git a/src/app/advertisement/advertisement-delete/advertisement-delete.component.html b/src/app/advertisement/advertisement-delete/advertisement-delete.component.html new file mode 100644 index 0000000..1ad9e35 --- /dev/null +++ b/src/app/advertisement/advertisement-delete/advertisement-delete.component.html @@ -0,0 +1,5 @@ +
+

Delete Advertisement

+

Are you sure you want to delete this advertisement?

+ +
diff --git a/src/app/advertisement/advertisement-delete/advertisement-delete.component.spec.ts b/src/app/advertisement/advertisement-delete/advertisement-delete.component.spec.ts new file mode 100644 index 0000000..61448c3 --- /dev/null +++ b/src/app/advertisement/advertisement-delete/advertisement-delete.component.spec.ts @@ -0,0 +1,71 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DeleteAdvertisementComponent } from './advertisement-delete.component'; +import { AdvertisementService } from '../advertisement.service'; +import { of, throwError } from 'rxjs'; + + +class AdvertisementServiceMock { + deleteAdvertisement() { + return of(void 0); + } +} + +describe('DeleteAdvertisementComponent', () => { + let component: DeleteAdvertisementComponent; + let fixture: ComponentFixture; + let advertisementService: AdvertisementServiceMock; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DeleteAdvertisementComponent], + providers: [ + { provide: AdvertisementService, useClass: AdvertisementServiceMock } + ] + }).compileComponents(); + + fixture = TestBed.createComponent(DeleteAdvertisementComponent); + component = fixture.componentInstance; + advertisementService = TestBed.inject(AdvertisementService) as unknown as AdvertisementServiceMock; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call deleteAdvertisement on confirmation', () => { + spyOn(window, 'confirm').and.returnValue(true); + spyOn(advertisementService, 'deleteAdvertisement').and.callThrough(); + + const advertisementId = 1; + component.advertisementId = advertisementId; + + component.deleteAdvertisement(); + + expect(advertisementService.deleteAdvertisement).toHaveBeenCalled(); + + }); + + it('should not call deleteAdvertisement if user cancels', () => { + spyOn(window, 'confirm').and.returnValue(false); + + const advertisementId = 1; + component.advertisementId = advertisementId; + + component.deleteAdvertisement(); + + expect(advertisementService.deleteAdvertisement).not.toHaveBeenCalled(); + }); + + it('should handle errors correctly', () => { + spyOn(window, 'confirm').and.returnValue(true); + + spyOn(advertisementService, 'deleteAdvertisement').and.returnValue(throwError(() => new Error('Deletion failed'))); + + const advertisementId = 1; + component.advertisementId = advertisementId; + + component.deleteAdvertisement(); + + }); +}); diff --git a/src/app/advertisement/advertisement-delete/advertisement-delete.component.ts b/src/app/advertisement/advertisement-delete/advertisement-delete.component.ts new file mode 100644 index 0000000..a5acf5e --- /dev/null +++ b/src/app/advertisement/advertisement-delete/advertisement-delete.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { AdvertisementService } from '../advertisement.service'; + +@Component({ + selector: 'app-advertisement-delete', + templateUrl: './advertisement-delete.component.html', + styleUrls: [], +}) +export class DeleteAdvertisementComponent implements OnInit { + advertisementId!: number; + + constructor( + private route: ActivatedRoute, + private advertisementService: AdvertisementService + ) {} + + ngOnInit(): void { + this.advertisementId = +this.route.snapshot.paramMap.get('id')!; + console.log('Advertisement ID:', this.advertisementId); + } + + + deleteAdvertisement(): void { + if (confirm('Are you sure you want to delete this advertisement?')) { + console.log('Attempting to delete advertisement with ID:', this.advertisementId); + this.advertisementService.deleteAdvertisement(this.advertisementId).subscribe({ + next: () => { + console.log('Advertisement deleted successfully.'); + alert('Advertisement deleted successfully.'); + }, + error: (err) => { + console.error('Failed to delete advertisement:', err); + alert('Failed to delete the advertisement. Please try again.'); + }, + }); + } else { + console.log('Deletion cancelled.'); + } + } + +} diff --git a/src/app/advertisement/advertisement-delete/advertisment-delete.component.css b/src/app/advertisement/advertisement-delete/advertisment-delete.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/advertisement/advertisement.service.ts b/src/app/advertisement/advertisement.service.ts new file mode 100644 index 0000000..f10bc72 --- /dev/null +++ b/src/app/advertisement/advertisement.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { HateoasResourceOperation, PagedResourceCollection, ResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import { Advertisement } from './advertisement'; + +@Injectable({ + providedIn: 'root' +}) +export class AdvertisementService extends HateoasResourceOperation { + constructor(private http: HttpClient) { + super(Advertisement); + } + + public findByTitle(query: string): Observable> { + return this.searchCollection('findByTitle', { params: { text: query } }); + } + + public getAllAdvertisements(): Observable> { + return this.getPage(); + } + + public getAdvertisementById(advertisementId: number): Observable { + return this.getResource(advertisementId); + } + + public getAdvertisementByUrl(advertisementUrl: string): Observable { + return this.http.get(advertisementUrl); + } + + public deleteAdvertisement(advertisementId: number): Observable { + const advertisement = new Advertisement(); + advertisement.id = advertisementId; + const deleteUrl = `http://localhost:8080/advertisements/${advertisementId}`; + console.log('Deleting advertisement from URL: ', deleteUrl); + advertisement.uri = deleteUrl; + advertisement['_links'] = { + self: { href: advertisement.uri } + }; + + return this.deleteResource(advertisement); + } + + + + + +} diff --git a/src/app/advertisement/advertisement.ts b/src/app/advertisement/advertisement.ts new file mode 100644 index 0000000..0a0210d --- /dev/null +++ b/src/app/advertisement/advertisement.ts @@ -0,0 +1,25 @@ +import { HateoasResource, Resource } from "@lagoshny/ngx-hateoas-client"; +import { Apartment } from "../apartment/apartment"; +import { AdvertisementStatus } from "../advertisement-status/advertisementStatus"; + +@HateoasResource('advertisements') +export class Advertisement extends Resource { + id: number = 0; + title: string = ''; + description: string = ''; + price: number = 0.01; + zipCode: string = ''; + country: string = ''; + address: string = ''; + creationDate: Date = new Date(); + expirationDate?: Date; + uri?: string; + + adStatus: AdvertisementStatus | number = 0; + apartment: Apartment | number = 0; + + constructor(values: object = {}) { + super(); + Object.assign(this , values); + } +} diff --git a/src/app/advertisement/advertisment-list/advertisement-list.component.css b/src/app/advertisement/advertisment-list/advertisement-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/advertisement/advertisment-list/advertisement-list.component.html b/src/app/advertisement/advertisment-list/advertisement-list.component.html new file mode 100644 index 0000000..2679155 --- /dev/null +++ b/src/app/advertisement/advertisment-list/advertisement-list.component.html @@ -0,0 +1,41 @@ +
+
+
+
+
+
Advertisement
+
+
+
+
Title
+

{{ ad.title }}

+
+
+
Price
+

{{ ad.price | currency }}

+
+
+
Zip Code
+

{{ ad.zipCode }}

+
+
+
Country
+

{{ ad.country }}

+
+
+
Address
+

{{ ad.address }}

+
+
+
Creation Date
+

{{ ad.creationDate | date: 'dd/MM/yyyy HH:mm:ss' }}

+
+
+
Description
+

{{ ad.description }}

+
+
+
+
+
+
diff --git a/src/app/advertisement/advertisment-list/advertisement-list.component.spec.ts b/src/app/advertisement/advertisment-list/advertisement-list.component.spec.ts new file mode 100644 index 0000000..266db25 --- /dev/null +++ b/src/app/advertisement/advertisment-list/advertisement-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvertisementListComponent } from './advertisement-list.component'; + +describe('AdvertismentListComponent', () => { + let component: AdvertisementListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AdvertisementListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AdvertisementListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/advertisement/advertisment-list/advertisement-list.component.ts b/src/app/advertisement/advertisment-list/advertisement-list.component.ts new file mode 100644 index 0000000..c70d500 --- /dev/null +++ b/src/app/advertisement/advertisment-list/advertisement-list.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { Advertisement } from '../advertisement'; +import { AdvertisementService } from '../advertisement.service'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-advertisement-list', + standalone: true, + imports:[CommonModule], + templateUrl: './advertisement-list.component.html', + styleUrls: ['./advertisement-list.component.css'] +}) +export class AdvertisementListComponent implements OnInit { + advertisements: Advertisement[] = []; + + constructor(private advertisementService: AdvertisementService) {} + + ngOnInit() { + this.loadAdvertisements(); + } + + loadAdvertisements() { + this.advertisementService.getAllAdvertisements().subscribe({ + next: (response) => { + this.advertisements = response.resources; + }, + error: (err) => { + console.error('Error al cargar los anuncios:', err); + } + }); + } +} diff --git a/src/app/advertisement/create-advertisement/create-advertisement.component.css b/src/app/advertisement/create-advertisement/create-advertisement.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/advertisement/create-advertisement/create-advertisement.component.html b/src/app/advertisement/create-advertisement/create-advertisement.component.html new file mode 100644 index 0000000..206bc4b --- /dev/null +++ b/src/app/advertisement/create-advertisement/create-advertisement.component.html @@ -0,0 +1,10 @@ +
+ +
+
+ +
\ No newline at end of file diff --git a/src/app/advertisement/create-advertisement/create-advertisement.component.spec.ts b/src/app/advertisement/create-advertisement/create-advertisement.component.spec.ts new file mode 100644 index 0000000..6bad92d --- /dev/null +++ b/src/app/advertisement/create-advertisement/create-advertisement.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateAdvertisementComponent } from './create-advertisement.component'; + +describe('CreateAdvertisementComponent', () => { + let component: CreateAdvertisementComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CreateAdvertisementComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateAdvertisementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/advertisement/create-advertisement/create-advertisement.component.ts b/src/app/advertisement/create-advertisement/create-advertisement.component.ts new file mode 100644 index 0000000..8705084 --- /dev/null +++ b/src/app/advertisement/create-advertisement/create-advertisement.component.ts @@ -0,0 +1,34 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { User } from '../../login-basic/user'; +import { Apartment } from '../../apartment/apartment'; +import { AuthenticationBasicService } from '../../login-basic/authentication-basic.service'; +import { ErrorMessageService } from '../../error-handler/error-message.service'; + +@Component({ + selector: 'app-create-advertisement', + standalone: true,imports: [FormsModule, CommonModule], + templateUrl: './create-advertisement.component.html', + styleUrl: './create-advertisement.component.css' +}) +export class CreateAdvertisementComponent implements OnInit{ + public apartment: Apartment = new Apartment(); + public user: User = new User(); + public isAuthorized: boolean = false; + + constructor( + private authenticationService: AuthenticationBasicService, + private errorMessageService: ErrorMessageService, + ) {} + + ngOnInit(): void { + this.user = this.authenticationService.getCurrentUser(); + this.isAuthorized = this.isAuthorised(); + } + + private isAuthorised(): boolean { + return this.user.getRoles().includes('admin') || this.user.getRoles().includes('owner'); + } + +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 92c1ed8..7d70c31 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -8,6 +8,9 @@ import { UserDetailComponent } from './user/user-detail/user-detail.component'; import { UserRegisterComponent } from './user/user-register/user-register.component'; import { UserEditComponent } from './user/user-edit/user-edit.component'; import { UserDeleteComponent } from './user/user-delete/user-delete.component'; +import { CreateAdvertisementComponent } from './advertisement/create-advertisement/create-advertisement.component'; +import { AdvertisementListComponent } from './advertisement/advertisment-list/advertisement-list.component'; +import { DeleteAdvertisementComponent } from './advertisement/advertisement-delete/advertisement-delete.component'; import { ApartmentListComponent } from './apartment/apartment-list/apartment-list.component'; import { ApartmentCreateComponent } from './apartment/apartment-create/apartment-create.component'; import { ApartmentUpdateComponent } from './apartment/apartment-update/apartment-update.component'; @@ -22,6 +25,9 @@ const routes: Routes = [ { path: 'users/:id', component: UserDetailComponent, canActivate: [LoggedInGuard]}, { path: 'users', component: UserListComponent, canActivate: [LoggedInGuard]}, { path: 'about', component: AboutComponent}, + { path: 'advertisement/create', component: CreateAdvertisementComponent}, + { path: 'advertisements', component: AdvertisementListComponent}, + { path: 'advertisement/:id/delete', component: DeleteAdvertisementComponent, canActivate: [LoggedInGuard] }, { path: 'apartments', component: ApartmentListComponent}, { path: 'apartment/create', component: ApartmentCreateComponent}, { path: 'apartment/:id/update', component: ApartmentUpdateComponent}, @@ -29,7 +35,7 @@ const routes: Routes = [ { path: 'visit/:id/status', component: VisitStatusComponent}, { path: 'rooms', component: RoomListComponent}, { path: '404', component: NotFoundComponent}, - { path: '', redirectTo: 'about', pathMatch: 'full'}, + { path: '', redirectTo: 'about', pathMatch: 'full'} ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5e9029e..2f945ea 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -23,6 +23,7 @@ import {HttpErrorInterceptor} from './error-handler/http-error-interceptor'; import {AuthenticationBasicService} from './login-basic/authentication-basic.service'; import {LoggedInGuard} from './login-basic/loggedin.guard'; import {UserService} from './user/user.service'; +import {DeleteAdvertisementComponent} from './advertisement/advertisement-delete/advertisement-delete.component'; @NgModule({ declarations: [ AppComponent, @@ -34,7 +35,8 @@ import {UserService} from './user/user.service'; UserRegisterComponent, UserEditComponent, UserDeleteComponent, - UserSearchComponent + UserSearchComponent, + DeleteAdvertisementComponent ], bootstrap: [AppComponent], imports: [BrowserModule, FormsModule,