Rollback to 9d67724e
This commit is contained in:
323
client/src/pages/AdminEmailConfig.tsx
Normal file
323
client/src/pages/AdminEmailConfig.tsx
Normal file
@@ -0,0 +1,323 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import DashboardLayout from "@/components/DashboardLayout";
|
||||
import { trpc } from "@/lib/trpc";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "sonner";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { AlertCircle, CheckCircle2, Mail, Send } from "lucide-react";
|
||||
|
||||
export default function AdminEmailConfig() {
|
||||
const { data: config, isLoading, refetch } = trpc.emailConfig.get.useQuery();
|
||||
const upsertMutation = trpc.emailConfig.upsert.useMutation();
|
||||
const testEmailMutation = trpc.emailConfig.testEmail.useMutation();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
provider: "resend",
|
||||
apiKey: "",
|
||||
fromEmail: "",
|
||||
fromName: "Formation Manager Itinova",
|
||||
mode: "simulation" as "simulation" | "production",
|
||||
domainVerified: false,
|
||||
});
|
||||
|
||||
const [testEmail, setTestEmail] = useState("");
|
||||
|
||||
// Charger la configuration existante
|
||||
useEffect(() => {
|
||||
if (config) {
|
||||
setFormData({
|
||||
provider: config.provider,
|
||||
apiKey: config.apiKey || "",
|
||||
fromEmail: config.fromEmail,
|
||||
fromName: config.fromName,
|
||||
mode: config.mode,
|
||||
domainVerified: config.domainVerified,
|
||||
});
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
upsertMutation.mutate(formData, {
|
||||
onSuccess: () => {
|
||||
toast.success("Configuration enregistrée avec succès");
|
||||
refetch();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(`Erreur: ${error.message}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleTestEmail = () => {
|
||||
if (!testEmail) {
|
||||
toast.error("Veuillez saisir une adresse email");
|
||||
return;
|
||||
}
|
||||
|
||||
testEmailMutation.mutate(
|
||||
{ toEmail: testEmail },
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
if (data.success) {
|
||||
toast.success(data.message || "Email de test envoyé avec succès");
|
||||
} else {
|
||||
toast.error(data.message || "Échec de l'envoi de l'email de test");
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(`Erreur: ${error.message}`);
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="container py-8">
|
||||
<p>Chargement...</p>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="container py-8 max-w-4xl">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold">Configuration des emails</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
Configurez l'envoi d'emails via Resend et basculez entre mode simulation et production
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6">
|
||||
{/* Statut actuel */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Mail className="h-5 w-5" />
|
||||
Statut actuel
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium">Mode d'envoi</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formData.mode === "simulation"
|
||||
? "Les emails sont simulés et envoyés comme notifications"
|
||||
: "Les emails sont envoyés via Resend"}
|
||||
</p>
|
||||
</div>
|
||||
<div className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||
formData.mode === "production"
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-yellow-100 text-yellow-800"
|
||||
}`}>
|
||||
{formData.mode === "production" ? "Production" : "Simulation"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{formData.apiKey && (
|
||||
<Alert>
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Clé API Resend configurée
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{!formData.apiKey && (
|
||||
<Alert>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Aucune clé API configurée. Les emails seront simulés.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Formulaire de configuration */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Configuration Resend</CardTitle>
|
||||
<CardDescription>
|
||||
Configurez votre compte Resend pour envoyer de vrais emails
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="apiKey">Clé API Resend *</Label>
|
||||
<Input
|
||||
id="apiKey"
|
||||
type="password"
|
||||
placeholder="re_..."
|
||||
value={formData.apiKey}
|
||||
onChange={(e) => setFormData({ ...formData, apiKey: e.target.value })}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Obtenez votre clé API sur{" "}
|
||||
<a
|
||||
href="https://resend.com/api-keys"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
resend.com/api-keys
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="fromEmail">Email expéditeur *</Label>
|
||||
<Input
|
||||
id="fromEmail"
|
||||
type="email"
|
||||
placeholder="noreply@votredomaine.com"
|
||||
value={formData.fromEmail}
|
||||
onChange={(e) => setFormData({ ...formData, fromEmail: e.target.value })}
|
||||
required
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
L'adresse email doit être vérifiée dans votre compte Resend
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="fromName">Nom de l'expéditeur *</Label>
|
||||
<Input
|
||||
id="fromName"
|
||||
type="text"
|
||||
placeholder="Formation Manager Itinova"
|
||||
value={formData.fromName}
|
||||
onChange={(e) => setFormData({ ...formData, fromName: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="mode">Mode production</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Activer l'envoi réel d'emails (désactiver pour simuler)
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="mode"
|
||||
checked={formData.mode === "production"}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData({ ...formData, mode: checked ? "production" : "simulation" })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Button type="submit" disabled={upsertMutation.isPending}>
|
||||
{upsertMutation.isPending ? "Enregistrement..." : "Enregistrer la configuration"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Test d'envoi */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Send className="h-5 w-5" />
|
||||
Test d'envoi
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Envoyez un email de test pour vérifier votre configuration
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="testEmail">Adresse email de test</Label>
|
||||
<Input
|
||||
id="testEmail"
|
||||
type="email"
|
||||
placeholder="votre@email.com"
|
||||
value={testEmail}
|
||||
onChange={(e) => setTestEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleTestEmail}
|
||||
disabled={testEmailMutation.isPending || !testEmail}
|
||||
variant="outline"
|
||||
>
|
||||
{testEmailMutation.isPending ? "Envoi en cours..." : "Envoyer un email de test"}
|
||||
</Button>
|
||||
|
||||
{formData.mode === "simulation" && (
|
||||
<Alert>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Mode simulation actif. L'email de test sera simulé et envoyé comme notification au propriétaire.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Documentation */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Guide de configuration</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4 text-sm">
|
||||
<div>
|
||||
<h4 className="font-medium mb-2">1. Créer un compte Resend</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Inscrivez-vous gratuitement sur{" "}
|
||||
<a href="https://resend.com" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||
resend.com
|
||||
</a>{" "}
|
||||
(100 emails/jour gratuits)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium mb-2">2. Vérifier votre domaine</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Ajoutez et vérifiez votre domaine dans les paramètres Resend pour pouvoir envoyer des emails
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium mb-2">3. Obtenir la clé API</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Générez une clé API dans la section "API Keys" de votre compte Resend
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium mb-2">4. Configurer et tester</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Saisissez vos informations ci-dessus, enregistrez, puis testez l'envoi avant de passer en mode production
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user