espace-paie-odentas/app/(app)/contrats/[id]/modification/page.tsx
2025-10-12 17:05:46 +02:00

261 lines
No EOL
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useParams, useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { useMemo, useState } from "react";
import { api } from "@/lib/fetcher";
import { Loader2, Info } from "lucide-react";
type ContratDetail = {
id: string;
numero: string;
regime: "CDDU_MONO" | "CDDU_MULTI" | "RG";
production?: string | null;
salarie?: { nom?: string[] } | null;
salarie_nom?: string | null;
// signatures parfois séparées (historique)
contrat_signe?: "Oui" | "Non";
contrat_signe_employeur?: "oui" | "non" | "Oui" | "Non";
contrat_signe_salarie?: "oui" | "non" | "Oui" | "Non";
etat_demande?: string;
};
function Card({ children }: { children: React.ReactNode }) {
return (
<div className="rounded-2xl border bg-white">
<div className="p-4">{children}</div>
</div>
);
}
export default function ContratModificationRequestPage() {
const { id } = useParams<{ id: string }>();
const router = useRouter();
const { data, isLoading, isError, error } = useQuery<ContratDetail>({
queryKey: ["contrat-detail", id],
queryFn: () => api<ContratDetail>(`/contrats/${id}`),
});
// Normalisation "contrat signé ?"
const isSigned = useMemo(() => {
if (!data) return false;
if (typeof data.contrat_signe === "string") {
return data.contrat_signe.toLowerCase() === "oui";
}
const emp = (data.contrat_signe_employeur || "").toLowerCase();
const sal = (data.contrat_signe_salarie || "").toLowerCase();
// on considère signé si les 2 sont “oui”
if (emp && sal) return emp === "oui" && sal === "oui";
return false;
}, [data]);
// Form state
const [message, setMessage] = useState("");
const [sending, setSending] = useState(false);
const [sent, setSent] = useState(false);
const [err, setErr] = useState<string | null>(null);
async function onSubmit() {
if (!data) return;
setErr(null);
if (!message.trim()) {
setErr("Merci de détailler votre demande dans le champ message.");
return;
}
try {
setSending(true);
// Préparation des informations du contrat pour le ticket
const salarieNom =
data.salarie_nom ||
(Array.isArray(data.salarie?.nom) ? data.salarie?.nom?.[0] : "");
// Création du sujet du ticket
const ticketSubject = `Demande de modification - Contrat ${data.numero}`;
// Préparation du message détaillé pour le ticket
const ticketMessage = `**DEMANDE DE MODIFICATION DE CONTRAT**
**Informations du contrat :**
- Numéro : ${data.numero}
- Régime : ${data.regime === "CDDU_MONO" ? "CDDU (mono-mois)" : data.regime === "CDDU_MULTI" ? "CDDU (multi-mois)" : "Régime général"}
- Production : ${data.production || "Non renseignée"}
- Salarié : ${salarieNom || "Non renseigné"}
- Contrat signé : ${isSigned ? "Oui" : "Non"}
- Statut signature : ${isSigned ? "⚠️ Avenant nécessaire (9,99€ HT)" : "✅ Modification gratuite"}
**Demande du client :**
${message}
**Lien vers le contrat :** ${window.location.origin}/contrats/${data.id}`;
// Création du ticket via l'API
const response = await fetch("/api/tickets", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
subject: ticketSubject,
message: ticketMessage,
priority: isSigned ? "high" : "normal", // Priorité haute si contrat signé (coût financier)
category: "modification_contrat"
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText || "Impossible de créer le ticket de demande.");
}
const ticket = await response.json();
// Confirmation et redirection vers le ticket créé
setSent(true);
setTimeout(() => {
router.push(`/support/${ticket.id}`);
}, 2500);
} catch (e: any) {
setErr(e?.message || "Impossible denvoyer la demande.");
} finally {
setSending(false);
}
}
if (isLoading) {
return (
<main className="p-6 text-slate-500">
<Loader2 className="inline w-4 h-4 animate-spin mr-2" />
Chargement
</main>
);
}
if (isError || !data) {
return (
<main className="p-6 text-rose-600">
Erreur : {(error as any)?.message || "Contrat introuvable"}
</main>
);
}
return (
<main className="p-6 space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-lg font-semibold">
Demande de modification {data.numero}
</h1>
</div>
{/* En-tête contrat */}
<Card>
<div className="text-sm text-slate-700 space-y-1">
<div>
<span className="font-medium">Contrat :</span> {data.numero} {" "}
{data.regime === "CDDU_MONO"
? "CDDU (mono-mois)"
: data.regime === "CDDU_MULTI"
? "CDDU (multi-mois)"
: "Régime général"}
</div>
{data.production ? (
<div>
<span className="font-medium">Production :</span> {data.production}
</div>
) : null}
<div className="text-slate-500">
La demande sera traitée via le <span className="font-medium">système de support</span>
</div>
</div>
</Card>
{/* Avenant / Coût */}
<div
className={`rounded-2xl border p-4 ${
isSigned
? "bg-amber-50 border-amber-200 text-amber-900"
: "bg-emerald-50 border-emerald-200 text-emerald-900"
}`}
>
<div className="flex items-start gap-2">
<Info className="w-4 h-4 mt-0.5" />
<div className="text-sm leading-relaxed">
{isSigned ? (
<>
<p className="font-medium">
Ce contrat étant signé par toutes les parties, la signature
dun avenant (de modification ou d'annulation) est nécessaire.
</p>
<p>Cette opération est facturée <strong>9,99€ HT</strong>.</p>
</>
) : (
<>
<p className="font-medium">
Ce contrat nétant pas signé par toutes les parties, la
signature dun avenant (de modification ou d'annulation) nest pas nécessaire.
</p>
<p>Cette modification ne vous sera donc <strong>pas facturée</strong>.</p>
</>
)}
</div>
</div>
</div>
{/* Formulaire */}
<Card>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium mb-1">Message *</label>
<textarea
rows={6}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Expliquez précisément votre demande de modification (dates, rémunération, fonction, etc.)."
className="w-full px-3 py-2 rounded-lg border bg-white text-sm"
/>
</div>
{err && <div className="text-sm text-rose-600">{err}</div>}
<div className="flex items-center justify-end gap-3">
<a
href={`/contrats/${data.id}`}
className="px-4 py-2 rounded-lg border"
>
Annuler
</a>
<button
type="button"
onClick={onSubmit}
disabled={sending || sent}
className="px-4 py-2 rounded-lg bg-emerald-600 text-white hover:bg-emerald-700 disabled:opacity-50"
>
{sending ? (
<>
<Loader2 className="w-4 h-4 inline animate-spin mr-2" />
Envoi
</>
) : (
"Envoyer la demande"
)}
</button>
</div>
</div>
</Card>
{/* Overlay succès */}
{sent && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-white/70">
<div className="rounded-2xl border bg-white p-6 text-center shadow-xl">
<Loader2 className="w-6 h-6 animate-spin mx-auto mb-3" />
<div className="font-medium">Ticket créé avec succès</div>
<p className="text-sm text-slate-600 mt-1">
Redirection vers votre demande de support
</p>
</div>
</div>
)}
</main>
);
}