espace-paie-odentas/components/staff/StaffContractsPageClient.tsx

336 lines
15 KiB
TypeScript

"use client";
import { useRef, useState, useEffect } from "react";
import { AlertCircle, Calendar, Euro, FileSignature } from "lucide-react";
import ContractsGrid, { ContractsGridHandle } from "./ContractsGrid";
export default function StaffContractsPageClient({ initialData, activeOrgId }: { initialData: any[]; activeOrgId?: string | null }) {
const gridRef = useRef<ContractsGridHandle | null>(null);
const [activeFilter, setActiveFilter] = useState<string | null>(null);
const [counts, setCounts] = useState({
dpaeAFaire: null as number | null,
contratsAFaireMois: null as number | null,
paieATraiterMoisDernier: null as number | null,
paieATraiterToutes: null as number | null,
nonSignesDateProche: null as number | null,
});
// Check localStorage to detect which filter is active
useEffect(() => {
try {
const saved = localStorage.getItem('staff-contracts-filters');
if (saved) {
const filters = JSON.parse(saved);
// Check for DPAE à faire filter
if (filters.dpaeFilter === 'À faire') {
const today = new Date();
const in7 = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
const y = in7.getFullYear();
const m = String(in7.getMonth() + 1).padStart(2, '0');
const day = String(in7.getDate()).padStart(2, '0');
const expectedEndDate = `${y}-${m}-${day}`;
if (filters.startTo === expectedEndDate && filters.sortField === 'start_date' && filters.sortOrder === 'asc') {
setActiveFilter('dpae-a-faire');
return;
}
}
// Check for Contrats à faire mois filter
if (filters.etatContratFilters?.length > 0 && filters.etatContratFilters.includes('Reçue')) {
const today = new Date();
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
const y1 = firstDay.getFullYear();
const m1 = String(firstDay.getMonth() + 1).padStart(2, '0');
const day1 = String(firstDay.getDate()).padStart(2, '0');
const expectedStartDate = `${y1}-${m1}-${day1}`;
const y2 = lastDay.getFullYear();
const m2 = String(lastDay.getMonth() + 1).padStart(2, '0');
const day2 = String(lastDay.getDate()).padStart(2, '0');
const expectedEndDate = `${y2}-${m2}-${day2}`;
if (filters.startFrom === expectedStartDate && filters.startTo === expectedEndDate && filters.sortField === 'start_date' && filters.sortOrder === 'asc') {
setActiveFilter('contrats-a-faire-mois');
return;
}
}
// Check for Paie à traiter mois dernier filter
if (filters.etatPaieFilter === 'À traiter') {
const today = new Date();
const firstDayThisMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDayLastMonth = new Date(firstDayThisMonth.getFullYear(), firstDayThisMonth.getMonth(), 0);
const firstDayLastMonth = new Date(lastDayLastMonth.getFullYear(), lastDayLastMonth.getMonth(), 1);
const y1 = firstDayLastMonth.getFullYear();
const m1 = String(firstDayLastMonth.getMonth() + 1).padStart(2, '0');
const day1 = String(firstDayLastMonth.getDate()).padStart(2, '0');
const expectedStartDate = `${y1}-${m1}-${day1}`;
const y2 = lastDayLastMonth.getFullYear();
const m2 = String(lastDayLastMonth.getMonth() + 1).padStart(2, '0');
const day2 = String(lastDayLastMonth.getDate()).padStart(2, '0');
const expectedEndDate = `${y2}-${m2}-${day2}`;
if (filters.endFrom === expectedStartDate && filters.endTo === expectedEndDate && filters.sortField === 'end_date' && filters.sortOrder === 'asc') {
setActiveFilter('paie-a-traiter-mois-dernier');
return;
}
}
// Check for Paie à traiter toutes les périodes filter
if (filters.etatPaieFilter === 'À traiter' && filters.etatContratFilters?.includes('Traitée')) {
const today = new Date();
const y = today.getFullYear();
const m = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const expectedEndDate = `${y}-${m}-${day}`;
if (filters.endTo === expectedEndDate && filters.startFrom === null && filters.startTo === null && filters.sortField === 'end_date' && filters.sortOrder === 'asc') {
setActiveFilter('paie-a-traiter-toutes');
return;
}
}
// Check for Non signés date proche filter
if (filters.signatureFilter === 'non_signe') {
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const y = tomorrow.getFullYear();
const m = String(tomorrow.getMonth() + 1).padStart(2, '0');
const day = String(tomorrow.getDate()).padStart(2, '0');
const expectedEndDate = `${y}-${m}-${day}`;
if (filters.startTo === expectedEndDate && filters.startFrom === null && filters.sortField === 'start_date' && filters.sortOrder === 'asc') {
setActiveFilter('non-signes-date-proche');
return;
}
}
// Check for Contrats en cours filter
// start_to = aujourd'hui, end_from = aujourd'hui, sort by end_date asc
const today = new Date();
const y = today.getFullYear();
const m = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const todayStr = `${y}-${m}-${day}`;
if (filters.startTo === todayStr && filters.endFrom === todayStr &&
filters.startFrom === null && filters.endTo === null &&
filters.sortField === 'end_date' && filters.sortOrder === 'asc') {
setActiveFilter('contrats-en-cours');
return;
}
}
} catch (e) {
// ignore
}
}, []);
// Update counts from grid when they change
useEffect(() => {
const updateCounts = () => {
setCounts({
dpaeAFaire: gridRef.current?.getCountDpaeAFaire() ?? null,
contratsAFaireMois: gridRef.current?.getCountContratsAFaireMois() ?? null,
paieATraiterMoisDernier: gridRef.current?.getCountPaieATraiterMoisDernier() ?? null,
paieATraiterToutes: gridRef.current?.getCountPaieATraiterToutes() ?? null,
nonSignesDateProche: gridRef.current?.getCountNonSignesDateProche() ?? null,
});
};
updateCounts();
const interval = setInterval(updateCounts, 500); // Update counts every 500ms
return () => clearInterval(interval);
}, []);
const handleDpaeAFaire = () => {
gridRef.current?.quickFilterDpaeAFaire();
setActiveFilter('dpae-a-faire');
};
const handleContratsAFaireMois = () => {
gridRef.current?.quickFilterContratsAFaireMois();
setActiveFilter('contrats-a-faire-mois');
};
const handlePaieATraiterMoisDernier = () => {
gridRef.current?.quickFilterPaieATraiterMoisDernier();
setActiveFilter('paie-a-traiter-mois-dernier');
};
const handlePaieATraiterToutes = () => {
gridRef.current?.quickFilterPaieATraiterToutes();
setActiveFilter('paie-a-traiter-toutes');
};
const handleNonSignesDateProche = () => {
gridRef.current?.quickFilterNonSignesDateProche();
setActiveFilter('non-signes-date-proche');
};
const handleContratsEnCours = () => {
gridRef.current?.quickFilterContratsEnCours();
setActiveFilter('contrats-en-cours');
};
return (
<div className="space-y-3">
<div className="flex items-center justify-between gap-4">
<h1 className="text-lg font-semibold">Contrats (Staff)</h1>
{/* Card de raccourcis à droite du titre */}
<div className="flex items-center gap-3 rounded-xl border border-slate-200 bg-gradient-to-r from-slate-50 to-slate-100 p-3">
<div className="flex flex-col gap-1">
<span className="text-xs text-slate-600 font-medium">Filtres contrats:</span>
<div className="flex items-center gap-2">
{/* Bouton DPAE à faire */}
<button
onClick={handleDpaeAFaire}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'dpae-a-faire'
? 'bg-gradient-to-r from-orange-500 to-orange-600 text-white shadow-md'
: 'bg-white text-orange-700 border border-orange-300 hover:border-orange-500 hover:shadow-sm'
}`}
title="Afficher les contrats avec DPAE à faire dont la date de début est passée ou dans les 7 jours"
>
<AlertCircle size={16} />
DPAE à faire
{counts.dpaeAFaire !== null && (
<span className={`ml-1 inline-flex items-center justify-center w-5 h-5 rounded-full text-xs font-bold ${
activeFilter === 'dpae-a-faire'
? 'bg-orange-300 text-orange-900'
: 'bg-orange-200 text-orange-800'
}`}>
{counts.dpaeAFaire}
</span>
)}
</button>
{/* Bouton Contrats à faire mois */}
<button
onClick={handleContratsAFaireMois}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'contrats-a-faire-mois'
? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-md'
: 'bg-white text-blue-700 border border-blue-300 hover:border-blue-500 hover:shadow-sm'
}`}
title="Afficher les contrats reçus ou en cours dont la date de début est dans le mois courant"
>
<Calendar size={16} />
Contrats à faire mois
{counts.contratsAFaireMois !== null && (
<span className={`ml-1 inline-flex items-center justify-center w-5 h-5 rounded-full text-xs font-bold ${
activeFilter === 'contrats-a-faire-mois'
? 'bg-blue-300 text-blue-900'
: 'bg-blue-200 text-blue-800'
}`}>
{counts.contratsAFaireMois}
</span>
)}
</button>
{/* Bouton Contrats non signés date proche */}
<button
onClick={handleNonSignesDateProche}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'non-signes-date-proche'
? 'bg-gradient-to-r from-red-500 to-red-600 text-white shadow-md'
: 'bg-white text-red-700 border border-red-300 hover:border-red-500 hover:shadow-sm'
}`}
title="Afficher les contrats non signés dont la date de début est passée, aujourd'hui ou demain"
>
<FileSignature size={16} />
Non signés
{counts.nonSignesDateProche !== null && (
<span className={`ml-1 inline-flex items-center justify-center w-5 h-5 rounded-full text-xs font-bold ${
activeFilter === 'non-signes-date-proche'
? 'bg-red-300 text-red-900'
: 'bg-red-200 text-red-800'
}`}>
{counts.nonSignesDateProche}
</span>
)}
</button>
{/* Bouton Contrats en cours */}
<button
onClick={handleContratsEnCours}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'contrats-en-cours'
? 'bg-gradient-to-r from-green-500 to-green-600 text-white shadow-md'
: 'bg-white text-green-700 border border-green-300 hover:border-green-500 hover:shadow-sm'
}`}
title="Afficher les contrats en cours (date de début passée et date de fin future)"
>
<Calendar size={16} />
En cours
</button>
</div>
</div>
{/* Séparateur vertical */}
<div className="border-l border-slate-300 h-12"></div>
{/* Sous-card Paies à traiter */}
<div className="flex flex-col gap-1">
<span className="text-xs text-slate-600 font-medium">Paies à traiter:</span>
<div className="flex items-center gap-2">
<button
onClick={handlePaieATraiterMoisDernier}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'paie-a-traiter-mois-dernier'
? 'bg-gradient-to-r from-green-500 to-green-600 text-white shadow-md'
: 'bg-white text-green-700 border border-green-300 hover:border-green-500 hover:shadow-sm'
}`}
title="Afficher les contrats avec paie à traiter dont la date de fin est au mois dernier"
>
<Euro size={16} />
Mois dernier
{counts.paieATraiterMoisDernier !== null && (
<span className={`ml-1 inline-flex items-center justify-center w-5 h-5 rounded-full text-xs font-bold ${
activeFilter === 'paie-a-traiter-mois-dernier'
? 'bg-green-300 text-green-900'
: 'bg-green-200 text-green-800'
}`}>
{counts.paieATraiterMoisDernier}
</span>
)}
</button>
<button
onClick={handlePaieATraiterToutes}
className={`inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg font-medium transition-all relative ${
activeFilter === 'paie-a-traiter-toutes'
? 'bg-gradient-to-r from-green-500 to-green-600 text-white shadow-md'
: 'bg-white text-green-700 border border-green-300 hover:border-green-500 hover:shadow-sm'
}`}
title="Afficher tous les contrats avec paie à traiter, toutes périodes confondues"
>
<Euro size={16} />
Toutes
{counts.paieATraiterToutes !== null && (
<span className={`ml-1 inline-flex items-center justify-center w-5 h-5 rounded-full text-xs font-bold ${
activeFilter === 'paie-a-traiter-toutes'
? 'bg-green-300 text-green-900'
: 'bg-green-200 text-green-800'
}`}>
{counts.paieATraiterToutes}
</span>
)}
</button>
</div>
</div>
</div>
</div>
<div className="rounded-2xl border bg-white p-4">
<ContractsGrid ref={gridRef} initialData={initialData} activeOrgId={activeOrgId} />
</div>
</div>
);
}