Checkpoint: Modification de la fenêtre de création des rappels

## Modifications effectuées :

### Base de données (drizzle/schema.ts)
Ajout d'un champ `timing` dans la table `rappels` :
- Type : ENUM('pre_formation', 'post_formation')
- Valeur par défaut : 'pre_formation'
- Permet de distinguer les rappels avant et après formation

### Interface utilisateur (client/src/pages/AdminRappels.tsx)
**Formulaire de création et d'édition :**
- Ajout de radio buttons pour choisir "Pré-formation" ou "Post-formation"
- Le label du champ "Nombre de jours" change dynamiquement selon le choix :
  * "Nombre de jours avant la séquence" (pré-formation)
  * "Nombre de jours après la séquence" (post-formation)
- Le texte d'aide s'adapte également au timing choisi

**États et formulaires :**
- Ajout du champ `timing` dans `formData`
- Mise à jour de `resetForm()` et `handleEdit()` pour gérer le nouveau champ

### Backend (server/routers.ts)
Mise à jour des procédures tRPC :
- `rappels.create` : ajout du champ `timing` avec validation Zod
- `rappels.update` : ajout du champ `timing` optionnel

## Fonctionnement
L'utilisateur peut maintenant créer des rappels qui seront envoyés soit avant (pré-formation) soit après (post-formation) une séquence, avec un nombre de jours personnalisable pour chaque cas.
This commit is contained in:
Manus
2026-01-08 05:38:52 -05:00
parent 1cee07582f
commit 6e25c00865
7 changed files with 2154 additions and 9 deletions

View File

@@ -42,6 +42,7 @@ export default function AdminRappels() {
const [formData, setFormData] = useState({
nom: "",
templateType: "",
timing: "pre_formation" as "pre_formation" | "post_formation",
joursAvant: 7,
heureEnvoi: "09:00",
actif: true,
@@ -97,6 +98,7 @@ export default function AdminRappels() {
setFormData({
nom: "",
templateType: "",
timing: "pre_formation",
joursAvant: 7,
heureEnvoi: "09:00",
actif: true,
@@ -116,6 +118,7 @@ export default function AdminRappels() {
setFormData({
nom: rappel.nom,
templateType: rappel.templateType,
timing: rappel.timing || "pre_formation",
joursAvant: rappel.joursAvant,
heureEnvoi: rappel.heureEnvoi,
actif: rappel.actif,
@@ -292,19 +295,53 @@ export default function AdminRappels() {
</div>
<div className="space-y-2">
<Label htmlFor="joursAvant">Nombre de jours avant la séquence *</Label>
<Label>Timing d'envoi *</Label>
<div className="flex gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="timing"
value="pre_formation"
checked={formData.timing === "pre_formation"}
onChange={(e) => setFormData({ ...formData, timing: e.target.value as "pre_formation" | "post_formation" })}
className="w-4 h-4"
/>
<span>Pré-formation</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="timing"
value="post_formation"
checked={formData.timing === "post_formation"}
onChange={(e) => setFormData({ ...formData, timing: e.target.value as "pre_formation" | "post_formation" })}
className="w-4 h-4"
/>
<span>Post-formation</span>
</label>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="joursAvant">
{formData.timing === "pre_formation"
? "Nombre de jours avant la séquence *"
: "Nombre de jours après la séquence *"}
</Label>
<Input
id="joursAvant"
type="number"
min="0"
min="1"
value={formData.joursAvant}
onChange={(e) =>
setFormData({ ...formData, joursAvant: parseInt(e.target.value) || 0 })
setFormData({ ...formData, joursAvant: parseInt(e.target.value) || 1 })
}
placeholder="Ex: 7"
/>
<p className="text-sm text-muted-foreground">
Le rappel sera envoyé ce nombre de jours avant le début de la séquence
{formData.timing === "pre_formation"
? "Le rappel sera envoyé ce nombre de jours avant le début de la séquence"
: "Le rappel sera envoyé ce nombre de jours après la fin de la séquence"}
</p>
</div>
@@ -381,17 +418,54 @@ export default function AdminRappels() {
</div>
<div className="space-y-2">
<Label htmlFor="edit-joursAvant">Nombre de jours avant la séquence *</Label>
<Label>Timing d'envoi *</Label>
<div className="flex gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="edit-timing"
value="pre_formation"
checked={formData.timing === "pre_formation"}
onChange={(e) => setFormData({ ...formData, timing: e.target.value as "pre_formation" | "post_formation" })}
className="w-4 h-4"
/>
<span>Pré-formation</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="edit-timing"
value="post_formation"
checked={formData.timing === "post_formation"}
onChange={(e) => setFormData({ ...formData, timing: e.target.value as "pre_formation" | "post_formation" })}
className="w-4 h-4"
/>
<span>Post-formation</span>
</label>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="edit-joursAvant">
{formData.timing === "pre_formation"
? "Nombre de jours avant la séquence *"
: "Nombre de jours après la séquence *"}
</Label>
<Input
id="edit-joursAvant"
type="number"
min="0"
min="1"
value={formData.joursAvant}
onChange={(e) =>
setFormData({ ...formData, joursAvant: parseInt(e.target.value) || 0 })
setFormData({ ...formData, joursAvant: parseInt(e.target.value) || 1 })
}
placeholder="Ex: 7"
/>
<p className="text-sm text-muted-foreground">
{formData.timing === "pre_formation"
? "Le rappel sera envoyé ce nombre de jours avant le début de la séquence"
: "Le rappel sera envoyé ce nombre de jours après la fin de la séquence"}
</p>
</div>
<div className="space-y-2">