Checkpoint: Ajout d'une page de paramètres pour gérer l'URL des QR codes
Modifications apportées : - Création de la table parametres dans la base de données - Création du fichier server/parametresDb.ts avec les fonctions getParametres et updateUrlPublique - Ajout du router parametres dans server/routers.ts avec les procédures get et updateUrlPublique - Création de la page client/src/pages/AdminParametres.tsx avec formulaire de modification - Ajout de la route /admin/parametres dans App.tsx - Ajout du lien "Paramètres" dans le menu après "Configuration SMTP" dans DashboardLayout - Modification de server/qrCodeGenerator.ts pour utiliser l'URL de la BDD au lieu de getAppBaseUrl - URL par défaut définie : https://formations.itinova.org Fonctionnalités : - Interface graphique pour modifier l'URL publique de l'application - Les QR codes d'émargement utilisent maintenant l'URL configurée dans la base de données - Validation de l'URL avant enregistrement - Message d'information sur l'impact des modifications
This commit is contained in:
@@ -30,6 +30,7 @@ import AdminRappelsStats from "./pages/admin/AdminRappelsStats";
|
||||
import AdminFormateurs from "./pages/admin/AdminFormateurs";
|
||||
import AdminNotifications from "./pages/admin/AdminNotifications";
|
||||
import AdminAttestations from "./pages/admin/AdminAttestations";
|
||||
import AdminParametres from "./pages/AdminParametres";
|
||||
import QuestionnaireReponse from "./pages/QuestionnaireReponse";
|
||||
import Inscription from "./pages/Inscription";
|
||||
import Login from "./pages/Login";
|
||||
@@ -57,6 +58,7 @@ function Router() {
|
||||
<Route path={"/admin/attestations"} component={AdminAttestations} />
|
||||
<Route path={"/admin/email-templates"} component={AdminEmailTemplates} />
|
||||
<Route path={"/admin/email-config"} component={AdminEmailConfig} />
|
||||
<Route path={"/admin/parametres"} component={AdminParametres} />
|
||||
<Route path={"/admin/calendrier"} component={AdminCalendrier} />
|
||||
<Route path={"/admin/rappels"} component={AdminRappels} />
|
||||
<Route path={"/admin/rappels/historique"} component={AdminRappelsHistorique} />
|
||||
|
||||
@@ -66,6 +66,7 @@ const menuSections = [
|
||||
{ icon: ClipboardList, label: "Questionnaires", path: "/admin/questionnaires" },
|
||||
{ icon: Mail, label: "Templates d'emails", path: "/admin/email-templates" },
|
||||
{ icon: Settings, label: "Configuration SMTP", path: "/admin/email-config" },
|
||||
{ icon: Settings, label: "Paramètres", path: "/admin/parametres" },
|
||||
{ icon: FileText, label: "Attestations de formation", path: "/admin/attestations" },
|
||||
]
|
||||
},
|
||||
|
||||
123
client/src/pages/AdminParametres.tsx
Normal file
123
client/src/pages/AdminParametres.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { trpc } from "@/lib/trpc";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { toast } from "sonner";
|
||||
import { Loader2, Save, Settings } from "lucide-react";
|
||||
|
||||
export default function AdminParametres() {
|
||||
const { data: parametres, isLoading, refetch } = trpc.parametres.get.useQuery();
|
||||
const [urlPublique, setUrlPublique] = useState("");
|
||||
|
||||
// Initialiser l'URL quand les données sont chargées
|
||||
useEffect(() => {
|
||||
if (parametres) {
|
||||
setUrlPublique(parametres.urlPublique);
|
||||
}
|
||||
}, [parametres]);
|
||||
|
||||
const updateMutation = trpc.parametres.updateUrlPublique.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success("Paramètres mis à jour avec succès");
|
||||
refetch();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(`Erreur : ${error.message}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Validation de l'URL
|
||||
try {
|
||||
new URL(urlPublique);
|
||||
} catch {
|
||||
toast.error("L'URL saisie n'est pas valide");
|
||||
return;
|
||||
}
|
||||
|
||||
updateMutation.mutate({ urlPublique });
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl py-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Settings className="h-8 w-8 text-primary" />
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">Paramètres de l'application</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Configuration globale de l'application
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>URL publique de l'application</CardTitle>
|
||||
<CardDescription>
|
||||
Cette URL est utilisée pour générer les QR codes d'émargement.
|
||||
Elle doit correspondre à l'URL publique de votre serveur.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="urlPublique">URL publique</Label>
|
||||
<Input
|
||||
id="urlPublique"
|
||||
type="url"
|
||||
placeholder="https://formations.itinova.org"
|
||||
value={urlPublique}
|
||||
onChange={(e) => setUrlPublique(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Exemple : https://formations.itinova.org (sans slash à la fin)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 pt-4">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={updateMutation.isPending}
|
||||
className="gap-2"
|
||||
>
|
||||
{updateMutation.isPending ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Enregistrement...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="h-4 w-4" />
|
||||
Enregistrer
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="mt-6 p-4 bg-muted rounded-lg">
|
||||
<h3 className="font-semibold mb-2">💡 Information</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Après avoir modifié l'URL publique, les nouveaux QR codes générés
|
||||
utiliseront automatiquement cette URL. Les QR codes déjà générés
|
||||
resteront inchangés.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user