import { useRoute } from "wouter"; import { trpc } from "@/lib/trpc"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; 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, Download } from "lucide-react"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; const COLORS = ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"]; export default function AdminQuestionnaireAnalyse() { const [, params] = useRoute("/admin/questionnaires/:id/analyse"); const questionnaireId = parseInt(params?.id || "0"); const { data: questionnaire } = trpc.questionnaires.getById.useQuery({ id: questionnaireId }); const { data: stats } = trpc.questionnaires.getStats.useQuery({ id: questionnaireId }); 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 ( Chargement... ); } // Analyser les réponses par question const analyseParQuestion = questions?.map((question) => { const reponsesQuestion = reponsesDetaillees?.filter((r) => r.questionId === question.id) || []; if (question.typeQuestion === "echelle" || question.typeQuestion === "oui_non") { // Calculer la moyenne et la distribution const valeurs = reponsesQuestion .map((r) => r.reponseNumerique) .filter((v): v is number => v !== null); const moyenne = valeurs.length > 0 ? valeurs.reduce((a, b) => a + b, 0) / valeurs.length : 0; // Distribution des réponses const distribution: Record = {}; valeurs.forEach((v) => { distribution[v] = (distribution[v] || 0) + 1; }); const distributionData = Object.entries(distribution).map(([valeur, count]) => ({ valeur: parseInt(valeur), count, label: question.typeQuestion === "oui_non" ? (valeur === "1" ? "Oui" : "Non") : valeur, })); return { question, type: "numerique", moyenne: Math.round(moyenne * 10) / 10, totalReponses: valeurs.length, distribution: distributionData, }; } else if (question.typeQuestion === "choix_multiple") { // Distribution des choix const choix: Record = {}; reponsesQuestion.forEach((r) => { if (r.reponseTexte) { choix[r.reponseTexte] = (choix[r.reponseTexte] || 0) + 1; } }); const distributionData = Object.entries(choix).map(([label, count]) => ({ label, count, })); return { question, type: "choix", totalReponses: reponsesQuestion.length, distribution: distributionData, }; } else { // Texte libre - afficher les réponses const reponses = reponsesQuestion .map((r) => r.reponseTexte) .filter((t): t is string => !!t); return { question, type: "texte", totalReponses: reponses.length, reponses: reponses.slice(0, 10), // Limiter à 10 réponses }; } }) || []; return ( {questionnaire.nom} Analyse des réponses {exportExcelMutation.isPending ? "Export..." : "Excel"} {exportPDFMutation.isPending ? "Export..." : "PDF"} {/* Statistiques globales */} Envois {stats?.totalEnvois || 0} Questionnaires envoyés Réponses {stats?.totalReponses || 0} Questionnaires complétés Taux de réponse {stats?.tauxReponse || 0}% Questions {questions?.length || 0} Questions au total {/* Analyse par question */} {analyseParQuestion.map((analyse, index) => ( Question {index + 1} : {analyse.question.texte} {analyse.totalReponses} réponse(s) {analyse.type === "numerique" && ` - Moyenne: ${analyse.moyenne}`} {analyse.type === "numerique" && analyse.distribution && ( )} {analyse.type === "choix" && analyse.distribution && ( `${label} (${(percent * 100).toFixed(0)}%)`} outerRadius={80} fill="#8884d8" dataKey="count" > {analyse.distribution.map((entry, index) => ( ))} )} {analyse.type === "texte" && analyse.reponses && ( {analyse.reponses.map((reponse, i) => ( {reponse} ))} {analyse.totalReponses > 10 && ( ... et {analyse.totalReponses - 10} autre(s) réponse(s) )} )} ))} {analyseParQuestion.length === 0 && ( Aucune réponse pour le moment )} ); }
Chargement...
Analyse des réponses
Questionnaires envoyés
Questionnaires complétés
Questions au total
{reponse}
... et {analyse.totalReponses - 10} autre(s) réponse(s)
Aucune réponse pour le moment