Checkpoint: Ajout d'une colonne "Rappels configurés" dans le tableau de gestion des séquences permettant de visualiser instantanément les rappels automatiques associés à chaque date de formation. Modification backend : création de la fonction getRappelsByDateFormation() dans db.ts qui récupère les rappels spécifiques à une date ainsi que les rappels globaux (sans dates spécifiques). Modification du router sequences.list pour inclure les rappels dans les données des séquences via Promise.all. Interface frontend : ajout de la colonne entre "Dates" et "Lieu" avec badges violets (bg-purple-50, text-purple-700, border-purple-300) et icône Bell pour chaque rappel, organisés par date avec labels "Date 1:", "Date 2:", etc. Affichage "Aucun rappel" en italique gris pour les dates sans rappels. Cette fonctionnalité améliore considérablement la visibilité de la configuration des rappels et facilite la gestion globale des communications automatiques.
This commit is contained in:
@@ -7,8 +7,9 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { trpc } from "@/lib/trpc";
|
import { trpc } from "@/lib/trpc";
|
||||||
import { Calendar, Edit, Eye, Plus, Trash2, X } from "lucide-react";
|
import { Calendar, Edit, Eye, Plus, Trash2, X, Bell } from "lucide-react";
|
||||||
import FormateurCombobox from "@/components/FormateurCombobox";
|
import FormateurCombobox from "@/components/FormateurCombobox";
|
||||||
import CapacityProgressBar from "@/components/CapacityProgressBar";
|
import CapacityProgressBar from "@/components/CapacityProgressBar";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -675,6 +676,7 @@ export default function AdminSequences() {
|
|||||||
<TableHead>Formation</TableHead>
|
<TableHead>Formation</TableHead>
|
||||||
<TableHead>Nom</TableHead>
|
<TableHead>Nom</TableHead>
|
||||||
<TableHead>Dates</TableHead>
|
<TableHead>Dates</TableHead>
|
||||||
|
<TableHead>Rappels configurés</TableHead>
|
||||||
<TableHead>Lieu</TableHead>
|
<TableHead>Lieu</TableHead>
|
||||||
<TableHead>Formateur</TableHead>
|
<TableHead>Formateur</TableHead>
|
||||||
<TableHead>Public cible</TableHead>
|
<TableHead>Public cible</TableHead>
|
||||||
@@ -698,6 +700,31 @@ export default function AdminSequences() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{sequence.dates.map((date: any) => (
|
||||||
|
<div key={date.id} className="space-y-1">
|
||||||
|
<div className="text-xs font-medium text-muted-foreground">Date {date.ordre}:</div>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{date.rappels && date.rappels.length > 0 ? (
|
||||||
|
date.rappels.map((rappel: any) => (
|
||||||
|
<Badge
|
||||||
|
key={rappel.id}
|
||||||
|
variant="outline"
|
||||||
|
className="bg-purple-50 text-purple-700 border-purple-300 text-xs"
|
||||||
|
>
|
||||||
|
<Bell className="w-3 h-3 mr-1" />
|
||||||
|
{rappel.nom}
|
||||||
|
</Badge>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground italic">Aucun rappel</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
<TableCell className="max-w-xs truncate">{sequence.lieu}</TableCell>
|
<TableCell className="max-w-xs truncate">{sequence.lieu}</TableCell>
|
||||||
<TableCell className="text-sm">{sequence.formateur?.nom || "-"}</TableCell>
|
<TableCell className="text-sm">{sequence.formateur?.nom || "-"}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|||||||
35
server/db.ts
35
server/db.ts
@@ -1113,6 +1113,41 @@ export async function updateRappelExecution(id: number) {
|
|||||||
}).where(eq(rappels.id, id));
|
}).where(eq(rappels.id, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRappelsByDateFormation(dateFormationId: number) {
|
||||||
|
const db = await getDb();
|
||||||
|
if (!db) return [];
|
||||||
|
|
||||||
|
// Récupérer les IDs des rappels associés à cette date
|
||||||
|
const rappelDatesResults = await db.select().from(rappelDates).where(eq(rappelDates.dateFormationId, dateFormationId));
|
||||||
|
const rappelIds = rappelDatesResults.map(rd => rd.rappelId);
|
||||||
|
|
||||||
|
if (rappelIds.length === 0) {
|
||||||
|
// Retourner tous les rappels actifs qui n'ont pas de dates spécifiques (rappels globaux)
|
||||||
|
const allRappels = await db.select().from(rappels).where(eq(rappels.actif, true));
|
||||||
|
const globalRappels = [];
|
||||||
|
|
||||||
|
for (const rappel of allRappels) {
|
||||||
|
const dates = await getRappelDates(rappel.id);
|
||||||
|
if (dates.length === 0) {
|
||||||
|
globalRappels.push(rappel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return globalRappels;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer les détails des rappels
|
||||||
|
const rappelsResults = await Promise.all(
|
||||||
|
rappelIds.map(async (id) => {
|
||||||
|
const rappelResult = await db.select().from(rappels).where(eq(rappels.id, id)).limit(1);
|
||||||
|
return rappelResult.length > 0 ? rappelResult[0] : null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filtrer les null et retourner
|
||||||
|
return rappelsResults.filter((r): r is Rappel => r !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ==================== UTILISATEURS ADMIN ====================
|
// ==================== UTILISATEURS ADMIN ====================
|
||||||
|
|
||||||
|
|||||||
@@ -258,7 +258,16 @@ export const appRouter = router({
|
|||||||
seqs.map(async (seq) => {
|
seqs.map(async (seq) => {
|
||||||
const dates = await db.getDatesBySequence(seq.id);
|
const dates = await db.getDatesBySequence(seq.id);
|
||||||
const nbInscrits = await db.countInscriptionsBySequence(seq.id, 'confirmee');
|
const nbInscrits = await db.countInscriptionsBySequence(seq.id, 'confirmee');
|
||||||
return { ...seq, dates, nbInscrits };
|
|
||||||
|
// Récupérer les rappels associés à chaque date
|
||||||
|
const datesAvecRappels = await Promise.all(
|
||||||
|
dates.map(async (date) => {
|
||||||
|
const rappels = await db.getRappelsByDateFormation(date.id);
|
||||||
|
return { ...date, rappels };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ...seq, dates: datesAvecRappels, nbInscrits };
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return sequencesAvecDates;
|
return sequencesAvecDates;
|
||||||
|
|||||||
5
todo.md
5
todo.md
@@ -585,3 +585,8 @@
|
|||||||
- [x] Ajouter des badges colorés pour distinguer formations, séquences et dates
|
- [x] Ajouter des badges colorés pour distinguer formations, séquences et dates
|
||||||
- [x] Utiliser des icônes pour identifier chaque type d'élément
|
- [x] Utiliser des icônes pour identifier chaque type d'élément
|
||||||
- [x] Améliorer la hiérarchie typographique pour faciliter la lecture
|
- [x] Améliorer la hiérarchie typographique pour faciliter la lecture
|
||||||
|
|
||||||
|
## Colonne Rappels configurés dans la gestion des séquences
|
||||||
|
- [x] Modifier le backend pour récupérer les rappels associés à chaque date
|
||||||
|
- [x] Ajouter la colonne "Rappels configurés" dans le tableau des séquences
|
||||||
|
- [x] Afficher les rappels avec badges visuels similaires à la page des rappels
|
||||||
|
|||||||
Reference in New Issue
Block a user