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:
9
.manus/db/db-query-1767868512905.json
Normal file
9
.manus/db/db-query-1767868512905.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"query": "ALTER TABLE rappels ADD COLUMN timing ENUM('pre_formation', 'post_formation') NOT NULL DEFAULT 'pre_formation' AFTER templateType;",
|
||||
"command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute ALTER TABLE rappels ADD COLUMN timing ENUM('pre_formation', 'post_formation') NOT NULL DEFAULT 'pre_formation' AFTER templateType;",
|
||||
"rows": [],
|
||||
"messages": [],
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"execution_time_ms": 1118
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
2043
drizzle/meta/0028_snapshot.json
Normal file
2043
drizzle/meta/0028_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -197,6 +197,13 @@
|
||||
"when": 1767799408213,
|
||||
"tag": "0027_naive_jackal",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 28,
|
||||
"version": "5",
|
||||
"when": 1767868505577,
|
||||
"tag": "0028_military_pandemic",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -276,9 +276,11 @@ export const rappels = mysqlTable("rappels", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
/** Nom du rappel */
|
||||
nom: varchar("nom", { length: 255 }).notNull(),
|
||||
/** Type de template à utiliser (rappel, rappelJ1) */
|
||||
/** Type de template à utiliser (rappel, rappelJ1, etc.) */
|
||||
templateType: varchar("templateType", { length: 50 }).notNull(),
|
||||
/** Nombre de jours avant la première date de la séquence */
|
||||
/** Timing : 'pre_formation' ou 'post_formation' */
|
||||
timing: mysqlEnum("timing", ["pre_formation", "post_formation"]).default("pre_formation").notNull(),
|
||||
/** Nombre de jours avant (positif) ou après (négatif) la première date de la séquence */
|
||||
joursAvant: int("joursAvant").notNull(),
|
||||
/** Heure d'envoi (format HH:MM) */
|
||||
heureEnvoi: varchar("heureEnvoi", { length: 5 }).default("09:00").notNull(),
|
||||
|
||||
@@ -1066,6 +1066,7 @@ export const appRouter = router({
|
||||
.input(z.object({
|
||||
nom: z.string(),
|
||||
templateType: z.string(),
|
||||
timing: z.enum(["pre_formation", "post_formation"]).default("pre_formation"),
|
||||
joursAvant: z.number(),
|
||||
heureEnvoi: z.string().default("09:00"),
|
||||
actif: z.boolean(),
|
||||
@@ -1079,6 +1080,7 @@ export const appRouter = router({
|
||||
id: z.number(),
|
||||
nom: z.string().optional(),
|
||||
templateType: z.string().optional(),
|
||||
timing: z.enum(["pre_formation", "post_formation"]).optional(),
|
||||
joursAvant: z.number().optional(),
|
||||
heureEnvoi: z.string().optional(),
|
||||
actif: z.boolean().optional(),
|
||||
|
||||
8
todo.md
8
todo.md
@@ -515,3 +515,11 @@
|
||||
|
||||
## Correction erreur emailTemplates.getByType
|
||||
- [x] Modifier getEmailTemplateByType dans db.ts pour retourner null au lieu de undefined
|
||||
|
||||
## Modification de la fenêtre de création des rappels
|
||||
- [x] Analyser le schéma actuel de la table rappels
|
||||
- [x] Ajouter un champ "timing" (pre_formation / post_formation) dans le schéma
|
||||
- [x] Appliquer la modification en base de données (ALTER TABLE)
|
||||
- [x] Modifier AdminRappels.tsx pour ajouter les radio buttons pré/post-formation
|
||||
- [x] Ajouter un champ de saisie du nombre de jours avec label dynamique
|
||||
- [x] Mettre à jour les procédures tRPC (create et update) pour gérer timing
|
||||
|
||||
Reference in New Issue
Block a user