Files
lmnp-organizer/frontend/src/app/components/condition-report-page/condition-report-page.html
T

288 lines
10 KiB
HTML
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.
<section class="form-page" [attr.aria-busy]="loading() || saving() || finalizing()">
<a routerLink="/leases">← Retour aux baux</a>
<header class="page-heading">
<div>
<p class="eyebrow">État des lieux</p>
<h1>{{ type === 'ENTRY' ? 'Entrée' : 'Sortie' }}</h1>
<p>Constat contradictoire, photos de preuve et remise aux parties.</p>
</div>
</header>
@if (loading()) {
<p role="status">Chargement de l’état des lieux…</p>
} @else if (error() && !report()) {
<p role="alert" class="operation-error">{{ error() }}</p>
<button type="button" (click)="load()">Réessayer</button>
} @else if (report(); as value) {
@if (error()) {
<p role="alert" class="operation-error">{{ error() }}</p>
}
@if (success()) {
<p role="status">{{ success() }}</p>
}
@if (value.leaseRevisionNumber !== null) {
<p>
Révision de bail source {{ value.leaseRevisionNumber }}
@if (value.leaseRevisionFingerprint) {
· empreinte {{ value.leaseRevisionFingerprint }}
}
</p>
}
<app-condition-report-editor
[leaseId]="leaseId"
[type]="type"
[report]="value"
[entryPhotos]="entryPhotos()"
[dirty]="dirty()"
[saving]="saving()"
[finalizing]="finalizing()"
(reportChanged)="changeReport($event)"
(saveRequested)="save($event)"
(errorOccurred)="error.set($event)"
(successOccurred)="success.set($event)"
(reloadRequested)="load()"
/>
<section>
<h2>Révision et finalisation</h2>
@if (value.status === 'DRAFT') {
<button type="button" [disabled]="revisionPending() || dirty()" (click)="createRevision()">
{{ revisionPending() ? 'Génération…' : 'Générer et télécharger la révision PDF' }}
</button>
<label>
Scan signé PDF
<input type="file" accept="application/pdf,.pdf" (change)="selectSignedScan($event)" />
</label>
@if (signedScan(); as scan) {
<p>{{ scan.name }} · attestation de correspondance incluse à la finalisation.</p>
}
<label>
<input
type="checkbox"
[checked]="finalizationAttested()"
[disabled]="finalizing()"
(change)="finalizationAttested.set($any($event.target).checked)"
/>
J’atteste que le scan signé correspond à la dernière révision générée.
</label>
<label>
<input
type="checkbox"
[checked]="allPartiesSigned()"
[disabled]="finalizing()"
(change)="allPartiesSigned.set($any($event.target).checked)"
/>
J’atteste que chaque partie présente ou son mandataire identifié a signé le document.
</label>
<button
type="button"
[disabled]="
finalizing() ||
!signedScan() ||
!finalizationAttested() ||
!allPartiesSigned() ||
dirty()
"
(click)="finalize()"
>
{{ finalizing() ? 'Finalisation…' : 'Finaliser l’état des lieux' }}
</button>
} @else if (value.status === 'FINALIZED') {
<p>État des lieux finalisé.</p>
<button type="button" (click)="downloadBundle()">Télécharger le dossier ZIP</button>
<h3>Attestations de remise</h3>
@for (participant of value.participants; track participant.partyId) {
@if (hasAttested(participant.partyId)) {
<p>{{ participant.name }} : remise attestée.</p>
} @else {
<button
type="button"
[disabled]="!!busyAttestationPartyId()"
(click)="attestDelivery(participant.partyId)"
>
{{
busyAttestationPartyId() === participant.partyId
? 'Attestation…'
: 'Attester la remise à ' + participant.name
}}
</button>
}
}
@if (type === 'ENTRY') {
<label>
Nature du complément
<select
[value]="supplementType()"
(change)="supplementType.set($any($event.target).value)"
>
<option value="GENERAL">Constat général (délai indicatif de 10 jours)</option>
<option value="HEATING">Équipement de chauffage (premier mois de chauffe)</option>
</select>
</label>
<label>
Date de demande du complément
<input
type="date"
required
[value]="supplementRequestedOn()"
(input)="supplementRequestedOn.set($any($event.target).value)"
/>
</label>
<label>
Ajouts acceptés
<textarea
required
[value]="supplementAcceptedAdditions()"
(input)="supplementAcceptedAdditions.set($any($event.target).value)"
></textarea>
</label>
@if (supplementType() === 'HEATING') {
<label>
Début réel de la période de chauffe
<input
type="date"
required
[value]="heatingPeriodStartedOn()"
(input)="heatingPeriodStartedOn.set($any($event.target).value)"
/>
</label>
}
@if (supplementIsLate()) {
<p role="alert">
La demande dépasse le délai indicatif (échéance : {{ supplementDeadline() }}). L’ajout
reste possible avec une confirmation auditée.
</p>
<label>
Motif du dépassement de délai
<textarea
required
[value]="supplementReason()"
(input)="supplementReason.set($any($event.target).value)"
></textarea>
</label>
<label>
<input
type="checkbox"
[checked]="lateWarningConfirmed()"
(change)="lateWarningConfirmed.set($any($event.target).checked)"
/>
Je confirme l’ajout malgré le dépassement du délai indicatif.
</label>
}
<label>
Zone des prochaines photos
<select
[value]="supplementPhotoZoneId()"
(change)="supplementPhotoZoneId.set($any($event.target).value)"
>
<option value="">Choisir une zone</option>
@for (zone of value.zones; track zone.id) {
<option [value]="zone.id">{{ zone.label }}</option>
}
</select>
</label>
<label>
Photos du complément (facultatives)
<input
type="file"
accept="image/jpeg,image/png,image/webp,image/heic,image/heif"
multiple
[disabled]="supplementPending()"
(change)="selectSupplementPhotos($event)"
/>
</label>
@for (photo of supplementPhotos(); track $index; let index = $index) {
<p>
{{ photo.file.name }} — {{ photo.zoneId }}
<button type="button" (click)="removeSupplementPhoto(index)">Retirer</button>
</p>
}
<label>
Complément signé PDF (obligatoire)
<input
type="file"
accept="application/pdf,.pdf"
[disabled]="supplementPending()"
(change)="selectSupplementSignedPdf($event)"
/>
</label>
@if (supplementSignedPdf(); as pdf) {
<p>{{ pdf.name }} · {{ supplementPhotos().length }} photo(s).</p>
}
<button
type="button"
[disabled]="supplementPending() || !supplementSignedPdf()"
(click)="appendSupplement()"
>
{{ supplementPending() ? 'Ajout…' : 'Ajouter le complément signé' }}
</button>
}
} @else {
<p role="status">Ce brouillon est obsolète car la révision du bail a changé.</p>
}
</section>
@if (type === 'EXIT') {
<section>
<h2>Comparaison structurée</h2>
<p>Les constats sont alignés sans responsabilité ni montant.</p>
<button type="button" [disabled]="comparisonPending()" (click)="loadComparison()">
{{ comparisonPending() ? 'Comparaison…' : 'Comparer entrée et sortie' }}
</button>
@for (section of comparisonSections(); track section.title) {
<h3>{{ section.title }}</h3>
<table>
<thead>
<tr>
<th>Référence</th>
<th>Entrée</th>
<th>Sortie</th>
<th>Changement</th>
<th>Motif d’absence</th>
</tr>
</thead>
<tbody>
@for (line of section.lines; track line.reference) {
<tr>
<th>{{ line.reference }}</th>
<td>{{ line.entry }}</td>
<td>{{ line.exit }}</td>
<td>{{ line.changed ? 'Oui' : 'Non' }}</td>
<td>{{ line.absence }}</td>
</tr>
}
</tbody>
</table>
}
@if (comparison(); as compared) {
<h3>Photos</h3>
@for (line of compared.photos; track line.reference) {
<article class="photo-comparison">
<div>
<h4>Entrée</h4>
@if (line.entry) {
<img [src]="photoUrl('ENTRY', line.entry.reference)" alt="Photo d’entrée" />
} @else {
<p>Aucune photo d’entrée.</p>
}
</div>
<div>
<h4>Sortie</h4>
@if (line.exit) {
<img [src]="photoUrl('EXIT', line.exit.reference)" alt="Photo de sortie" />
} @else {
<p>Aucune photo de sortie.</p>
}
</div>
@if (line.absence) {
<p>Motif d’absence : {{ line.absence.reason }}</p>
}
</article>
}
}
</section>
}
}
</section>