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

248 lines
9.5 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { FileText, Plus, Search, Check, X, RefreshCw } from "lucide-react";
import { Amendment } from "@/types/amendments";
interface StaffAvenantsPageClientProps {
initialData: Amendment[];
}
export default function StaffAvenantsPageClient({ initialData }: StaffAvenantsPageClientProps) {
const router = useRouter();
const [amendments, setAmendments] = useState<Amendment[]>(initialData);
const [searchTerm, setSearchTerm] = useState("");
const [isRefreshing, setIsRefreshing] = useState(false);
const handleRefresh = () => {
setIsRefreshing(true);
router.refresh();
// Attendre un peu pour que l'animation soit visible
setTimeout(() => setIsRefreshing(false), 1000);
};
const filteredAmendments = amendments.filter((amendment) => {
const term = searchTerm.toLowerCase();
return (
amendment.contract_number?.toLowerCase().includes(term) ||
amendment.employee_name?.toLowerCase().includes(term) ||
amendment.organization_name?.toLowerCase().includes(term)
);
});
const formatDate = (dateString?: string) => {
if (!dateString) return "-";
const [y, m, d] = dateString.split("-");
return `${d}/${m}/${y}`;
};
const getStatusBadge = (status: Amendment["status"]) => {
const badges = {
draft: "bg-slate-100 text-slate-700",
pending: "bg-orange-100 text-orange-700",
signed: "bg-green-100 text-green-700",
cancelled: "bg-red-100 text-red-700",
};
const labels = {
draft: "Brouillon",
pending: "En attente",
signed: "Signé",
cancelled: "Annulé",
};
return (
<span className={`px-2 py-1 text-xs font-medium rounded ${badges[status]}`}>
{labels[status]}
</span>
);
};
const getSignatureIcons = (signatureStatus?: string) => {
// Déterminer si employeur a signé
const employerSigned = signatureStatus === 'pending_employee' || signatureStatus === 'signed';
// Déterminer si salarié a signé
const employeeSigned = signatureStatus === 'signed';
// Si pas encore envoyé
const notSent = !signatureStatus || signatureStatus === 'not_sent';
return (
<div className="flex items-center gap-3">
<div className="flex flex-col items-center gap-1">
<div className="text-xs font-semibold text-slate-600">E</div>
{notSent ? (
<span className="text-xs text-slate-400"></span>
) : employerSigned ? (
<Check className="w-4 h-4 text-green-600" strokeWidth={3} />
) : (
<X className="w-4 h-4 text-red-600" strokeWidth={3} />
)}
</div>
<div className="flex flex-col items-center gap-1">
<div className="text-xs font-semibold text-slate-600">S</div>
{notSent ? (
<span className="text-xs text-slate-400"></span>
) : employeeSigned ? (
<Check className="w-4 h-4 text-green-600" strokeWidth={3} />
) : (
<X className="w-4 h-4 text-red-600" strokeWidth={3} />
)}
</div>
</div>
);
};
const getElementsLabel = (elements: Amendment["elements"]) => {
const labels = {
objet: "Objet",
duree: "Durée",
lieu_horaire: "Lieu/Horaire",
remuneration: "Rémunération",
};
return elements.map((el) => labels[el]).join(", ");
};
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-slate-900">Avenants aux contrats</h1>
<p className="text-sm text-slate-600 mt-1">
Gérez les avenants aux contrats de travail
</p>
</div>
<div className="flex items-center gap-3">
<button
onClick={handleRefresh}
disabled={isRefreshing}
className="flex items-center gap-2 px-4 py-2 border border-slate-300 text-slate-700 rounded-lg hover:bg-slate-50 transition-colors disabled:opacity-50"
title="Rafraîchir la liste"
>
<RefreshCw className={`h-4 w-4 ${isRefreshing ? "animate-spin" : ""}`} />
Rafraîchir
</button>
<button
onClick={() => router.push("/staff/avenants/nouveau")}
className="flex items-center gap-2 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors shadow-sm"
>
<Plus className="h-4 w-4" />
Nouvel avenant
</button>
</div>
</div>
{/* Search bar */}
<div className="bg-white rounded-xl border shadow-sm p-4">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" />
<input
type="text"
placeholder="Rechercher par n° contrat, salarié, organisation..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
</div>
{/* Table */}
{filteredAmendments.length === 0 ? (
<div className="bg-white rounded-xl border shadow-sm p-12 text-center">
<FileText className="h-12 w-12 text-slate-300 mx-auto mb-4" />
<h3 className="text-lg font-medium text-slate-900 mb-2">
{searchTerm ? "Aucun résultat" : "Aucun avenant"}
</h3>
<p className="text-sm text-slate-600 mb-6">
{searchTerm
? "Aucun avenant ne correspond à votre recherche."
: "Commencez par créer un nouvel avenant."}
</p>
{!searchTerm && (
<button
onClick={() => router.push("/staff/avenants/nouveau")}
className="inline-flex items-center gap-2 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors"
>
<Plus className="h-4 w-4" />
Créer le premier avenant
</button>
)}
</div>
) : (
<div className="bg-white rounded-xl border shadow-sm overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-slate-50 border-b">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
N° Contrat
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Salarié
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Organisation
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Éléments avenantés
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Date d'effet
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Signé
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Statut
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-slate-600">
Actions
</th>
</tr>
</thead>
<tbody className="divide-y">
{filteredAmendments.map((amendment) => (
<tr
key={amendment.id}
onClick={() => router.push(`/staff/avenants/${amendment.id}`)}
className="hover:bg-slate-50 transition-colors cursor-pointer"
>
<td className="px-4 py-3 text-sm font-medium text-slate-900">
{amendment.contract_number || "-"}
</td>
<td className="px-4 py-3 text-sm text-slate-700">
{amendment.employee_name || "-"}
</td>
<td className="px-4 py-3 text-sm text-slate-700">
{amendment.organization_name || "-"}
</td>
<td className="px-4 py-3 text-sm text-slate-700">
{getElementsLabel(amendment.elements)}
</td>
<td className="px-4 py-3 text-sm text-slate-700">
{formatDate(amendment.date_effet)}
</td>
<td className="px-4 py-3">
{getSignatureIcons(amendment.signature_status)}
</td>
<td className="px-4 py-3">{getStatusBadge(amendment.status)}</td>
<td className="px-4 py-3 text-sm">
<button
onClick={(e) => {
e.stopPropagation();
router.push(`/staff/avenants/${amendment.id}`);
}}
className="text-indigo-600 hover:text-indigo-700 font-medium"
>
Voir
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
);
}