261 lines
No EOL
8.5 KiB
TypeScript
261 lines
No EOL
8.5 KiB
TypeScript
"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 d’envoyer 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
|
||
d’un 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 d’un avenant (de modification ou d'annulation) n’est 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>
|
||
);
|
||
} |