Checkpoint: Exports Excel/PDF et notifications pour les questionnaires

 **Nouvelles fonctionnalités implémentées** :

**Exports des résultats** :
- Export Excel avec 3 feuilles (statistiques globales, questions, réponses brutes)
- Export PDF avec mise en page professionnelle et pagination
- Boutons d'export dans la page d'analyse des questionnaires
- Téléchargement automatique des fichiers générés

**Système de notifications** :
- Notification automatique à l'administrateur lors de chaque nouvelle réponse
- Utilisation du système de notifications intégré Manus
- Gestion des erreurs sans bloquer la soumission du questionnaire

**Système complet de questionnaires** :
- Création et édition de questionnaires 
- Prévisualisation avant envoi 
- Envoi automatique post-formation 
- Analyse des résultats avec graphiques 
- Exports Excel/PDF 
- Notifications administrateur 

Le système de questionnaires est maintenant complet et prêt pour la production.
This commit is contained in:
Manus Sandbox
2025-12-07 14:00:15 -05:00
parent b21ad8f978
commit 5d8fd4dd1b
4 changed files with 284 additions and 4 deletions

View File

@@ -4,7 +4,9 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Progress } from "@/components/ui/progress";
import DashboardLayout from "@/components/DashboardLayout";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from "recharts";
import { FileText, Users, CheckCircle2, TrendingUp } from "lucide-react";
import { FileText, Users, CheckCircle2, TrendingUp, Download } from "lucide-react";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";
const COLORS = ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
@@ -17,6 +19,46 @@ export default function AdminQuestionnaireAnalyse() {
const { data: questions } = trpc.questions.list.useQuery({ questionnaireId });
const { data: reponsesDetaillees } = trpc.questionnaires.getReponsesDetaillees.useQuery({ id: questionnaireId });
// Mutations d'export
const exportExcelMutation = trpc.questionnaires.exportExcel.useMutation();
const exportPDFMutation = trpc.questionnaires.exportPDF.useMutation();
const handleExportExcel = async () => {
try {
const result = await exportExcelMutation.mutateAsync({ id: questionnaireId });
const blob = new Blob([Uint8Array.from(atob(result.data), c => c.charCodeAt(0))], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = result.filename;
a.click();
window.URL.revokeObjectURL(url);
toast.success('Export Excel réussi');
} catch (error) {
toast.error('Erreur lors de l\'export Excel');
}
};
const handleExportPDF = async () => {
try {
const result = await exportPDFMutation.mutateAsync({ id: questionnaireId });
const blob = new Blob([Uint8Array.from(atob(result.data), c => c.charCodeAt(0))], {
type: 'application/pdf'
});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = result.filename;
a.click();
window.URL.revokeObjectURL(url);
toast.success('Export PDF réussi');
} catch (error) {
toast.error('Erreur lors de l\'export PDF');
}
};
if (!questionnaire) {
return (
<DashboardLayout>
@@ -96,9 +138,29 @@ export default function AdminQuestionnaireAnalyse() {
return (
<DashboardLayout>
<div className="container mx-auto py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold">{questionnaire.nom}</h1>
<p className="text-muted-foreground mt-1">Analyse des réponses</p>
<div className="mb-6 flex justify-between items-start">
<div>
<h1 className="text-3xl font-bold">{questionnaire.nom}</h1>
<p className="text-muted-foreground mt-1">Analyse des réponses</p>
</div>
<div className="flex gap-2">
<Button
variant="outline"
onClick={handleExportExcel}
disabled={exportExcelMutation.isPending}
>
<Download className="mr-2 h-4 w-4" />
{exportExcelMutation.isPending ? "Export..." : "Excel"}
</Button>
<Button
variant="outline"
onClick={handleExportPDF}
disabled={exportPDFMutation.isPending}
>
<Download className="mr-2 h-4 w-4" />
{exportPDFMutation.isPending ? "Export..." : "PDF"}
</Button>
</div>
</div>
{/* Statistiques globales */}