feat: Ajout saisie heures répétitions et correction PDF avenants

- Ajout champs heures totales et minutes pour répétitions dans AmendmentDureeForm
- Intégration DatesQuantityModal pour saisie heures par jour de répétition
- Ajout type 'heures_repetitions' dans dateFormatter et DatesQuantityModal
- Correction envoi precisions_salaire à PDFMonkey pour les avenants
- Ajout champs séparés dates_representations_detail et dates_repetitions_detail
- Correction logique dates_repetitions_heures pour éviter doublons dans PDF
This commit is contained in:
odentas 2025-10-24 15:53:00 +02:00
parent 3653dd6e77
commit 44fc21c817
5 changed files with 102 additions and 6 deletions

View file

@ -215,7 +215,11 @@ export async function POST(request: NextRequest) {
if (dureeData.dates_representations) {
datesTravaillees += dureeData.dates_representations;
}
if (dureeData.dates_repetitions) {
// Pour les répétitions, utiliser dates_repetitions_heures si disponible, sinon dates_repetitions
if (dureeData.dates_repetitions_heures) {
if (datesTravaillees) datesTravaillees += " ; ";
datesTravaillees += dureeData.dates_repetitions_heures;
} else if (dureeData.dates_repetitions) {
if (datesTravaillees) datesTravaillees += " ; ";
datesTravaillees += dureeData.dates_repetitions;
}
@ -284,6 +288,7 @@ export async function POST(request: NextRequest) {
maximumFractionDigits: 2
})
: "",
precisions_salaire: remunerationData.precisions_salaire || contract.precisions_salaire || "",
signature_contrat: formatDate(contract.date_signature),
CCN: ccn,
effet_avenant: formatDate(amendmentData.date_effet),
@ -297,6 +302,8 @@ export async function POST(request: NextRequest) {
heures: heures,
heuresparjour: heuresParJour
},
dates_representations_detail: dureeData.dates_representations || contract.jours_representations || "",
dates_repetitions_detail: dureeData.dates_repetitions_heures || dureeData.dates_repetitions || contract.jours_repetitions || "",
imageUrl: orgDetails.logo || ""
};

View file

@ -20,7 +20,7 @@ interface DatesQuantityModalProps {
pdfFormatted: string;
}) => void;
selectedDates: string[]; // format input "12/10, 13/10, ..."
dateType: "representations" | "repetitions" | "jours_travail"; // Type de dates pour déterminer le libellé
dateType: "representations" | "repetitions" | "jours_travail" | "heures_repetitions"; // Type de dates pour déterminer le libellé
minDate?: string;
maxDate?: string;
}
@ -78,6 +78,8 @@ export default function DatesQuantityModal({
return "service(s) de répétition";
case "jours_travail":
return isRange ? "heure(s) par jour" : "heure(s)";
case "heures_repetitions":
return isRange ? "heure(s) par jour" : "heure(s)";
default:
return "";
}
@ -91,6 +93,8 @@ export default function DatesQuantityModal({
return "service(s) de répétition";
case "jours_travail":
return isRange ? "heure(s) par jour" : "heure(s)";
case "heures_repetitions":
return isRange ? "heure(s) par jour" : "heure(s)";
default:
return "";
}

View file

@ -24,12 +24,13 @@ export default function AmendmentDureeForm({
// États pour les calendriers (dates de représentations/répétitions/jours de travail uniquement)
const [showDatesRep, setShowDatesRep] = useState(false);
const [showDatesServ, setShowDatesServ] = useState(false);
const [showHeuresRepetitions, setShowHeuresRepetitions] = useState(false);
const [showJoursTravail, setShowJoursTravail] = useState(false);
// Modal de quantités
const [quantityModalOpen, setQuantityModalOpen] = useState(false);
const [quantityModalType, setQuantityModalType] = useState<
"representations" | "repetitions" | "jours_travail"
"representations" | "repetitions" | "jours_travail" | "heures_repetitions"
>("representations");
const [pendingDates, setPendingDates] = useState<string[]>([]);
@ -68,6 +69,17 @@ export default function AmendmentDureeForm({
setQuantityModalOpen(true);
};
const handleHeuresRepetitionsApply = (result: {
selectedDates: string[];
hasMultiMonth: boolean;
pdfFormatted: string;
}) => {
// Pour les heures de répétitions, on passe directement à la modale de quantités
setPendingDates(result.selectedDates);
setQuantityModalType("heures_repetitions");
setQuantityModalOpen(true);
};
const handleJoursTravailApply = (result: {
selectedDates: string[];
hasMultiMonth: boolean;
@ -105,6 +117,12 @@ export default function AmendmentDureeForm({
nb_repetitions: nbDates,
});
break;
case "heures_repetitions":
onChange({
...data,
dates_repetitions_heures: result.pdfFormatted,
});
break;
}
setQuantityModalOpen(false);
@ -237,6 +255,68 @@ export default function AmendmentDureeForm({
</div>
)}
</div>
<div>
<label className="block text-xs font-medium text-slate-700 mb-2">
Nombre d'heures de répétition (total)
</label>
<div className="text-xs text-slate-500 mb-2">
Actuellement : {originalData.nb_heures || "-"}
</div>
<div className="flex items-center gap-2">
<input
type="number"
min="0"
value={data.heures_repetitions || ""}
onChange={(e) =>
onChange({
...data,
heures_repetitions: e.target.value ? Number(e.target.value) : "",
})
}
placeholder="ex: 3"
className="flex-1 px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
<select
value={data.minutes_repetitions || "0"}
onChange={(e) =>
onChange({
...data,
minutes_repetitions: e.target.value,
})
}
className="px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
aria-label="Minutes"
>
<option value="0">+ 00 min</option>
<option value="30">+ 30 min</option>
</select>
<button
onClick={() => setShowHeuresRepetitions(!showHeuresRepetitions)}
className="px-3 py-2 border rounded-lg text-sm hover:bg-slate-50 flex items-center gap-2 whitespace-nowrap"
>
<Calendar className="h-4 w-4" />
Heures/jour
</button>
</div>
<p className="text-xs text-slate-500 mt-1">
Nombre d'heures total sur toutes les répétitions
</p>
{showHeuresRepetitions && (
<DatePickerCalendar
isOpen={showHeuresRepetitions}
onClose={() => setShowHeuresRepetitions(false)}
onApply={handleHeuresRepetitionsApply}
initialDates={[]}
title="Sélectionner les dates de répétition avec heures"
/>
)}
{data.dates_repetitions_heures && (
<div className="mt-2 p-2 bg-white rounded text-xs text-slate-600 border">
{data.dates_repetitions_heures}
</div>
)}
</div>
</>
)}

View file

@ -85,7 +85,7 @@ export { generateDateRange };
export function formatQuantifiedDates(
isos: string[],
quantities: Record<string, number | string>,
dateType: "representations" | "repetitions" | "jours_travail"
dateType: "representations" | "repetitions" | "jours_travail" | "heures_repetitions"
): string {
if (!isos || isos.length === 0) return "";
@ -121,7 +121,7 @@ export function formatQuantifiedDates(
let base = '';
if (dateType === 'representations') base = qtyNum > 1 ? 'représentations' : 'représentation';
else if (dateType === 'repetitions') base = qtyNum > 1 ? 'services de répétition' : 'service de répétition';
else if (dateType === 'jours_travail') base = qtyNum > 1 ? 'heures' : 'heure';
else if (dateType === 'jours_travail' || dateType === 'heures_repetitions') base = qtyNum > 1 ? 'heures' : 'heure';
if (g.startIso === g.endIso) {
// date isolée
@ -129,7 +129,7 @@ export function formatQuantifiedDates(
}
// plage: utiliser "par jour" pour indiquer par-journée
if (dateType === 'jours_travail') {
if (dateType === 'jours_travail' || dateType === 'heures_repetitions') {
return `${qty} ${base} par jour du ${formatDateFr(g.startIso)} au ${formatDateFr(g.endIso!)}`;
}

View file

@ -29,8 +29,11 @@ export interface AmendmentDureeData {
nb_representations?: number;
nb_repetitions?: number;
nb_heures?: number; // Pour techniciens
heures_repetitions?: number; // Heures totales pour répétitions (artistes)
minutes_repetitions?: string; // Minutes pour répétitions: "0" ou "30"
dates_representations?: string;
dates_repetitions?: string;
dates_repetitions_heures?: string; // Heures de répétition par jour (format texte)
jours_travail?: string;
}
@ -104,6 +107,8 @@ export interface OriginalContractData {
nb_representations?: number;
nb_repetitions?: number;
nb_heures?: number;
heures_repetitions?: number; // Heures totales pour répétitions (artistes)
minutes_repetitions?: string; // Minutes pour répétitions: "0" ou "30"
dates_representations?: string;
dates_repetitions?: string;
jours_travail?: string;