feat(frontend): fusion liste des frais et message compte existant [#NEWTS-9]

This commit is contained in:
Luc SORIGNET
2026-03-15 12:09:02 +01:00
parent c296af2c07
commit e30a41a58b
10 changed files with 755 additions and 294 deletions

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { Calendar } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
@ -13,8 +13,22 @@ const paymentPlansOptions = [
{ id: 4, name: '12 fois', frequency: 12 },
];
/**
* Affiche les plans de paiement communs aux deux types de frais.
* Quand `allPaymentPlans` est fourni (mode unifié), un plan coché est créé pour
* les deux types (inscription 0 ET scolarité 1) en même temps.
*
* Props (mode unifié) :
* allPaymentPlans : [{plan_type, type, ...}, ...] - liste combinée des deux types
* handleCreate : (data) => Promise - avec type et establishment déjà présent dans data
* handleDelete : (id) => Promise
*
* Props (mode legacy) :
* paymentPlans, handleCreate, handleDelete, type
*/
const PaymentPlanSelector = ({
paymentPlans,
allPaymentPlans,
handleCreate,
handleDelete,
type,
@ -24,38 +38,63 @@ const PaymentPlanSelector = ({
const { selectedEstablishmentId } = useEstablishment();
const [checkedPlans, setCheckedPlans] = useState([]);
// Vérifie si un plan existe pour ce type (par id)
const plans = useMemo(
() =>
Array.isArray(allPaymentPlans)
? allPaymentPlans
: Array.isArray(paymentPlans)
? paymentPlans
: [],
[allPaymentPlans, paymentPlans]
);
const unified = !!allPaymentPlans;
// Un plan est coché si au moins un enregistrement existe pour cette option
const isChecked = (planOption) => checkedPlans.includes(planOption.id);
// Création ou suppression du plan
const handlePlanToggle = (planOption) => {
const updatedPlan = paymentPlans.find(
(plan) => plan.plan_type === planOption.id
);
if (isChecked(planOption)) {
// Supprimer tous les enregistrements correspondant à cette option (les deux types en mode unifié)
const toDelete = plans.filter(
(p) =>
(typeof p.plan_type === 'object' ? p.plan_type.id : p.plan_type) ===
planOption.id
);
setCheckedPlans((prev) => prev.filter((id) => id !== planOption.id));
handleDelete(updatedPlan.id, null);
toDelete.forEach((p) =>
handleDelete(p.id, null).catch((e) => logger.error(e))
);
} else {
setCheckedPlans((prev) => [...prev, planOption.id]);
handleCreate({
plan_type: planOption.id,
type,
establishment: selectedEstablishmentId,
});
if (unified) {
// Créer pour inscription (0) et scolarité (1)
[0, 1].forEach((t) =>
handleCreate({
plan_type: planOption.id,
type: t,
establishment: selectedEstablishmentId,
}).catch((e) => logger.error(e))
);
} else {
handleCreate({
plan_type: planOption.id,
type,
establishment: selectedEstablishmentId,
}).catch((e) => logger.error(e));
}
}
};
useEffect(() => {
if (paymentPlans && paymentPlans.length > 0) {
setCheckedPlans(
paymentPlans.map((plan) =>
typeof plan.plan_type === 'object'
? plan.plan_type.id
: plan.plan_type
)
if (plans.length > 0) {
const ids = plans.map((plan) =>
typeof plan.plan_type === 'object' ? plan.plan_type.id : plan.plan_type
);
setCheckedPlans([...new Set(ids)]);
} else {
setCheckedPlans([]);
}
}, [paymentPlans]);
}, [plans]);
return (
<div className="space-y-4">