Files
lmnp-organizer/frontend/src/app/components/inventory-page/inventory-page.ts
T

189 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<FurnitureInventory>(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<FurnitureInventoryLine>): 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;
}
}