- Ajout du paramètre d'envoi automatique des attestations dans la page Paramètres - Ajout de la prévisualisation du modèle d'attestation avec génération PDF - Création de la page Historique des attestations dans la section Traçabilité - Création de la table historiqueAttestations pour tracer les envois - Ajout des procédures tRPC pour l'historique et la prévisualisation
237 lines
8.8 KiB
TypeScript
237 lines
8.8 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { trpc } from "@/lib/trpc";
|
|
import DashboardLayout from "@/components/DashboardLayout";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { toast } from "sonner";
|
|
import { FileText, Save, Eye } from "lucide-react";
|
|
import ImageUpload from "@/components/ImageUpload";
|
|
|
|
export default function AdminAttestations() {
|
|
const { data: config, isLoading, refetch } = trpc.attestations.getConfig.useQuery();
|
|
const updateConfig = trpc.attestations.updateConfig.useMutation();
|
|
const previsualiser = trpc.attestations.previsualiser.useMutation();
|
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
|
|
|
const [formData, setFormData] = useState({
|
|
nomSignataire: "",
|
|
fonctionSignataire: "",
|
|
texteAttestation: "",
|
|
logoUrl: "",
|
|
logoS3Key: "",
|
|
signatureUrl: "",
|
|
signatureS3Key: "",
|
|
});
|
|
|
|
// Mettre à jour le formulaire quand les données sont chargées
|
|
useEffect(() => {
|
|
if (config) {
|
|
setFormData({
|
|
nomSignataire: config.nomSignataire || "",
|
|
fonctionSignataire: config.fonctionSignataire || "",
|
|
texteAttestation: config.texteAttestation || "",
|
|
logoUrl: config.logoUrl || "",
|
|
logoS3Key: config.logoS3Key || "",
|
|
signatureUrl: config.signatureUrl || "",
|
|
signatureS3Key: config.signatureS3Key || "",
|
|
});
|
|
}
|
|
}, [config]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
try {
|
|
await updateConfig.mutateAsync(formData);
|
|
toast.success("Configuration enregistrée avec succès");
|
|
refetch();
|
|
} catch (error) {
|
|
toast.error("Erreur lors de l'enregistrement");
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="flex items-center justify-center h-64">
|
|
<p className="text-muted-foreground">Chargement...</p>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="container max-w-4xl py-8">
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<FileText className="h-8 w-8 text-primary" />
|
|
<div>
|
|
<h1 className="text-3xl font-bold">Configuration des attestations</h1>
|
|
<p className="text-muted-foreground">
|
|
Personnalisez le contenu et la signature des attestations de formation
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Informations du signataire</CardTitle>
|
|
<CardDescription>
|
|
Ces informations apparaîtront en bas de l'attestation
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="nomSignataire">Nom du signataire</Label>
|
|
<Input
|
|
id="nomSignataire"
|
|
value={formData.nomSignataire}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, nomSignataire: e.target.value })
|
|
}
|
|
placeholder="Ex: Jean Dupont"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="fonctionSignataire">Fonction du signataire</Label>
|
|
<Input
|
|
id="fonctionSignataire"
|
|
value={formData.fonctionSignataire}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, fonctionSignataire: e.target.value })
|
|
}
|
|
placeholder="Ex: Directeur de la formation"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Logo et signature</CardTitle>
|
|
<CardDescription>
|
|
Ajoutez un logo et une signature pour personnaliser vos attestations
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<ImageUpload
|
|
label="Logo de l'organisation"
|
|
currentImageUrl={formData.logoUrl}
|
|
onUploadComplete={(url, s3Key) =>
|
|
setFormData({ ...formData, logoUrl: url, logoS3Key: s3Key })
|
|
}
|
|
/>
|
|
|
|
<ImageUpload
|
|
label="Signature du signataire"
|
|
currentImageUrl={formData.signatureUrl}
|
|
onUploadComplete={(url, s3Key) =>
|
|
setFormData({ ...formData, signatureUrl: url, signatureS3Key: s3Key })
|
|
}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Texte de l'attestation</CardTitle>
|
|
<CardDescription>
|
|
Personnalisez le texte principal de l'attestation. Utilisez les variables suivantes :
|
|
<ul className="list-disc list-inside mt-2 space-y-1">
|
|
<li><code className="bg-muted px-1 py-0.5 rounded">{'{{nomComplet}}'}</code> - Nom complet de l'apprenant</li>
|
|
<li><code className="bg-muted px-1 py-0.5 rounded">{'{{nomFormation}}'}</code> - Nom de la formation</li>
|
|
<li><code className="bg-muted px-1 py-0.5 rounded">{'{{dateDebut}}'}</code> - Date de début de la formation</li>
|
|
<li><code className="bg-muted px-1 py-0.5 rounded">{'{{dateFin}}'}</code> - Date de fin de la formation</li>
|
|
</ul>
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Textarea
|
|
value={formData.texteAttestation}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, texteAttestation: e.target.value })
|
|
}
|
|
placeholder="Nous attestons que {{nomComplet}} a suivi avec assiduité la formation "{{nomFormation}}" organisée du {{dateDebut}} au {{dateFin}}."
|
|
rows={6}
|
|
className="font-serif"
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<Button
|
|
type="button"
|
|
variant="secondary"
|
|
onClick={async () => {
|
|
try {
|
|
const result = await previsualiser.mutateAsync();
|
|
setPreviewUrl(result.pdfUrl);
|
|
toast.success("Prévisualisation générée");
|
|
} catch (error) {
|
|
toast.error("Erreur lors de la génération de la prévisualisation");
|
|
console.error(error);
|
|
}
|
|
}}
|
|
disabled={previsualiser.isPending}
|
|
>
|
|
<Eye className="mr-2 h-4 w-4" />
|
|
{previsualiser.isPending ? "Génération..." : "Prévisualiser le modèle"}
|
|
</Button>
|
|
|
|
<div className="flex gap-3">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => {
|
|
if (config) {
|
|
setFormData({
|
|
nomSignataire: config.nomSignataire || "",
|
|
fonctionSignataire: config.fonctionSignataire || "",
|
|
texteAttestation: config.texteAttestation || "",
|
|
logoUrl: config.logoUrl || "",
|
|
logoS3Key: config.logoS3Key || "",
|
|
signatureUrl: config.signatureUrl || "",
|
|
signatureS3Key: config.signatureS3Key || "",
|
|
});
|
|
}
|
|
}}
|
|
>
|
|
Annuler
|
|
</Button>
|
|
<Button type="submit" disabled={updateConfig.isPending}>
|
|
<Save className="mr-2 h-4 w-4" />
|
|
{updateConfig.isPending ? "Enregistrement..." : "Enregistrer"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Prévisualisation PDF */}
|
|
{previewUrl && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Prévisualisation du modèle</CardTitle>
|
|
<CardDescription>
|
|
Aperçu du PDF d'attestation avec des données fictives
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<iframe
|
|
src={previewUrl}
|
|
className="w-full h-[800px] border rounded"
|
|
title="Prévisualisation de l'attestation"
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</form>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|