import { Component, inject, signal } from '@angular/core'; import { ActivatedRoute, RouterLink } from '@angular/router'; import { finalize as rxFinalize } from 'rxjs'; import { ConditionReportService, FurnitureInventory, FurnitureInventoryLine, FurniturePresence, } from '../../services/condition-report.service'; import { httpErrorMessage } from '../../services/http-error-message'; const emptyInventory: FurnitureInventory = { id: '', templateVersion: '', lines: [], missingCategories: [], absentCategories: [], legalWarnings: [], readyForLeaseDocument: false, frozen: false, privateAreas: [], version: 0, }; @Component({ selector: 'app-inventory-page', imports: [RouterLink], templateUrl: './inventory-page.html', styleUrl: './inventory-page.css', }) export class InventoryPage { private readonly route = inject(ActivatedRoute); private readonly inventories = inject(ConditionReportService); readonly leaseId = this.route.snapshot.paramMap.get('leaseId') ?? ''; readonly inventory = signal(emptyInventory); readonly loading = signal(true); readonly saving = signal(false); readonly error = signal(''); readonly success = signal(''); constructor() { this.load(); } load(): void { this.loading.set(true); this.error.set(''); this.success.set(''); this.inventories .getInventory(this.leaseId) .pipe(rxFinalize(() => this.loading.set(false))) .subscribe({ next: (inventory) => this.inventory.set(inventory), error: (error) => this.error.set( httpErrorMessage(error, 'Impossible de charger l’inventaire du mobilier.'), ), }); } updateText( id: string, field: 'privateAreaId' | 'designation' | 'description', event: Event, ): void { const value = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement ? event.target.value : ''; this.updateLine(id, { [field]: value }); } updateQuantity(id: string, event: Event): void { const input = event.target instanceof HTMLInputElement ? event.target : null; const quantity = !input || input.value === '' || !Number.isFinite(input.valueAsNumber) ? -1 : input.valueAsNumber; this.updateLine(id, { quantity }); } updatePresence(id: string, event: Event): void { const line = this.inventory().lines.find((candidate) => candidate.id === id); const value = event.target instanceof HTMLSelectElement ? event.target.value : ''; const presence: FurniturePresence | null = value === 'PRESENT' || value === 'ABSENT' ? value : null; if (!line?.category) return; this.updateLine(id, { presence, quantity: presence === 'ABSENT' ? 0 : presence === 'PRESENT' && line.quantity === 0 ? 1 : line.quantity, }); } addLine(): void { if (this.inventory().frozen || this.saving()) return; this.inventory.update((inventory) => ({ ...inventory, lines: [ ...inventory.lines, { id: crypto.randomUUID(), category: null, presence: null, privateAreaId: '', designation: '', quantity: 1, description: '', }, ], })); } removeLine(id: string): void { if (this.inventory().frozen || this.saving()) return; this.inventory.update((inventory) => ({ ...inventory, lines: inventory.lines.filter((line) => line.id !== id || line.category !== null), })); } warningLabel(category: string): string { return ( this.inventory().lines.find((line) => line.category === category)?.designation ?? category ); } save(): void { const inventory = this.inventory(); if (inventory.frozen) { this.error.set('Cet inventaire est figé et ne peut plus être modifié.'); return; } const validationError = this.validationError(inventory.lines); if (validationError) { this.error.set(validationError); return; } this.saving.set(true); this.error.set(''); this.success.set(''); this.inventories .saveInventory(this.leaseId, { version: inventory.version, lines: inventory.lines }) .pipe(rxFinalize(() => this.saving.set(false))) .subscribe({ next: (saved) => { this.inventory.set(saved); this.success.set('Inventaire enregistré.'); }, error: (error) => this.error.set(httpErrorMessage(error, 'Impossible d’enregistrer l’inventaire.')), }); } private updateLine(id: string, changes: Partial): void { if (this.inventory().frozen || this.saving()) return; this.inventory.update((inventory) => ({ ...inventory, lines: inventory.lines.map((line) => (line.id === id ? { ...line, ...changes } : line)), })); } private validationError(lines: FurnitureInventoryLine[]): string | null { for (const line of lines) { if (!line.privateAreaId.trim()) return 'Renseignez la zone de chaque ligne.'; if (!line.designation.trim()) return 'Renseignez la désignation de chaque ligne.'; if (!Number.isInteger(line.quantity) || line.quantity < 0) { return 'Saisissez une quantité entière positive ou nulle.'; } if (line.category && line.presence === null) { return 'Déclarez la présence ou l’absence de chaque élément réglementaire.'; } if (line.category && line.presence === 'PRESENT' && line.quantity === 0) { return 'Un élément réglementaire présent doit avoir une quantité positive.'; } if (line.category && line.presence === 'ABSENT' && line.quantity !== 0) { return 'Un élément réglementaire absent doit avoir une quantité nulle.'; } } return null; } }