feat: add lease end page and resolution forms with necessary services and components

- Implemented LeaseEndPage component for managing lease end processes.
- Created LeaseEndResolutionForms component for handling agreement and closure submissions.
- Added civil date formatting utilities for French date representation.
- Developed styles for lease end resolution forms and rent due correction components.
- Integrated HTTP service for lease end operations including notice recording, withdrawal acceptance, and closure.
- Added unit tests for new components and services to ensure functionality.
This commit is contained in:
nessar committed 2026-08-17 14:18:16 +02:00
1 parent 3d42c9a256
commit c1e6b905d5
114 files changed
+6582 -345

No files matched your search

@@ -1,4 +1,3 @@
import { DatePipe } from '@angular/common';
import { Component, computed, inject, signal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
@@ -19,15 +18,12 @@ import { formatEuro } from '../../services/rent-payment-format';
import { PaymentAllocation, RentPayment } from '../../services/rent-payment.service';
import { WorkspaceService } from '../../services/workspace.service';
import { RentPaymentDialogResult, RENT_PAYMENT_DATA_STALE } from '../rent-payment-dialog-result';
interface PaymentFeedback {
kind: 'success' | 'error';
message: string;
}
import { RentDueCorrection } from '../rent-due-correction/rent-due-correction';
import { formatFrenchCivilDate } from '../../services/civil-date';
@Component({
selector: 'app-rent-payments-page',
imports: [DatePipe, MatButtonModule, MatProgressBarModule, MatProgressSpinnerModule],
imports: [MatButtonModule, MatProgressBarModule, MatProgressSpinnerModule, RentDueCorrection],
templateUrl: './rent-payments-page.html',
styleUrl: './rent-payments-page.css',
})
@@ -35,8 +31,9 @@ export class RentPaymentsPage {
readonly workspace = inject(WorkspaceService);
private readonly dialog = inject(MatDialog);
readonly showFullSchedule = signal(false);
readonly feedback = signal<PaymentFeedback | null>(null);
readonly feedback = signal<{ kind: 'success' | 'error'; message: string } | null>(null);
readonly formatEuro = formatEuro;
readonly civilDate = formatFrenchCivilDate;
readonly displayedRentDues = computed(() =>
this.showFullSchedule()
? sortRentDues(this.workspace.rentDues())
@@ -227,14 +224,21 @@ function currentRentDues(rentDues: readonly RentDue[]): RentDue[] {
function currentSchedule(schedule: RentDue[]): RentDue[] {
const nextDueIndex = schedule.findIndex((rentDue) => rentDue.timing === 'UPCOMING');
const throughNextDue = nextDueIndex === -1 ? schedule : schedule.slice(0, nextDueIndex + 1);
const lastCorrectionIndex = schedule.reduce(
(lastCorrection, rentDue, index) => (rentDue.correctionRequired ? index : lastCorrection),
-1,
);
const visibleEndIndex =
nextDueIndex === -1 ? schedule.length - 1 : Math.max(nextDueIndex, lastCorrectionIndex);
const throughNextDue = schedule.slice(0, visibleEndIndex + 1);
const lastPaidIndex = throughNextDue.reduce(
(lastPaid, rentDue, index) => (rentDue.settlement === 'PAID' ? index : lastPaid),
-1,
);
const firstUnpaidIndex = throughNextDue.findIndex((rentDue) => rentDue.settlement !== 'PAID');
const firstCorrectionIndex = throughNextDue.findIndex((rentDue) => rentDue.correctionRequired);
const firstVisibleIndex = Math.min(
...[lastPaidIndex, firstUnpaidIndex].filter((index) => index !== -1),
...[lastPaidIndex, firstUnpaidIndex, firstCorrectionIndex].filter((index) => index !== -1),
);
return firstVisibleIndex === Infinity ? [] : throughNextDue.slice(firstVisibleIndex);