Implement Landlord and Apartment CRUD features with secure endpoints, coverage tests, and responsive UI

This commit is contained in:
nessar committed 2026-08-17 14:18:14 +02:00
1 parent 60f0377f97
commit 62a0bd5db3
18 files changed
+1419 -31

No files matched your search

+114 -3
View File
@@ -1,19 +1,49 @@
import { Component, signal, inject } from '@angular/core';
import { Component, signal, inject, OnInit, effect } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ApartmentService, Apartment } from './apartment.service';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [],
imports: [FormsModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
export class App implements OnInit {
protected readonly title = signal('LMNP Organizer');
readonly helloMessage = signal<string>('');
// Tab & CRUD States
readonly activeTab = signal<'dashboard' | 'apartments'>('dashboard');
readonly apartments = signal<Apartment[]>([]);
readonly showForm = signal<boolean>(false);
readonly editingApartment = signal<Apartment | null>(null);
// Form Fields
formName = '';
formAddress = '';
formPurchasePrice = 0;
formDescription = '';
formError = '';
private readonly oauthService = inject(OAuthService);
private readonly http = inject(HttpClient);
private readonly apartmentService = inject(ApartmentService);
constructor() {
effect(() => {
if (this.isLoggedIn) {
this.loadApartments();
}
});
}
ngOnInit() {
if (this.isLoggedIn) {
this.loadApartments();
}
}
get isLoggedIn(): boolean {
return this.oauthService.hasValidAccessToken();
@@ -51,4 +81,85 @@ export class App {
}
});
}
// Apartment CRUD operations
loadApartments() {
this.apartmentService.getApartments().subscribe({
next: (data) => this.apartments.set(data),
error: (err) => console.error('Error loading apartments', err)
});
}
openAddForm() {
this.editingApartment.set(null);
this.formName = '';
this.formAddress = '';
this.formPurchasePrice = 0;
this.formDescription = '';
this.formError = '';
this.showForm.set(true);
}
openEditForm(apartment: Apartment) {
this.editingApartment.set(apartment);
this.formName = apartment.name;
this.formAddress = apartment.address;
this.formPurchasePrice = apartment.purchasePrice;
this.formDescription = apartment.description;
this.formError = '';
this.showForm.set(true);
}
closeForm() {
this.showForm.set(false);
this.editingApartment.set(null);
}
saveApartment() {
if (!this.formName.trim() || !this.formAddress.trim() || this.formPurchasePrice <= 0) {
this.formError = 'Veuillez remplir tous les champs obligatoires (Nom, Adresse, Prix > 0).';
return;
}
const apartmentData: Apartment = {
name: this.formName.trim(),
address: this.formAddress.trim(),
purchasePrice: this.formPurchasePrice,
description: this.formDescription.trim()
};
const editMode = this.editingApartment();
if (editMode && editMode.id) {
this.apartmentService.updateApartment(editMode.id, apartmentData).subscribe({
next: () => {
this.loadApartments();
this.closeForm();
},
error: (err) => {
console.error(err);
this.formError = 'Erreur lors de la modification.';
}
});
} else {
this.apartmentService.createApartment(apartmentData).subscribe({
next: () => {
this.loadApartments();
this.closeForm();
},
error: (err) => {
console.error(err);
this.formError = 'Erreur lors de la création.';
}
});
}
}
deleteApartment(id: string) {
if (confirm('Êtes-vous sûr de vouloir supprimer cet appartement ?')) {
this.apartmentService.deleteApartment(id).subscribe({
next: () => this.loadApartments(),
error: (err) => console.error('Error deleting apartment', err)
});
}
}
}