577 lines
26 KiB
TypeScript
577 lines
26 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { trpc } from "@/lib/trpc";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { toast } from "sonner";
|
|
import { Loader2, Save, Upload, FolderOpen, Mail, Play, Square, Download, Inbox, CheckCircle2, Monitor, FolderOutput } from "lucide-react";
|
|
import DashboardLayout from "@/components/DashboardLayout";
|
|
|
|
export default function ImportSettings() {
|
|
const { data: settings, isLoading } = trpc.importSettings.get.useQuery();
|
|
const { data: emailServiceStatus } = trpc.emailImportService.status.useQuery();
|
|
const { data: folderServiceStatus } = trpc.folderImportService.status.useQuery();
|
|
const updateMutation = trpc.importSettings.update.useMutation();
|
|
const startEmailServiceMutation = trpc.emailImportService.start.useMutation();
|
|
const stopEmailServiceMutation = trpc.emailImportService.stop.useMutation();
|
|
const checkNowEmailMutation = trpc.emailImportService.checkNow.useMutation();
|
|
const utils = trpc.useUtils();
|
|
const startFolderServiceMutation = trpc.folderImportService.start.useMutation();
|
|
const stopFolderServiceMutation = trpc.folderImportService.stop.useMutation();
|
|
|
|
// Manual import
|
|
const [manualImportEnabled, setManualImportEnabled] = useState(true);
|
|
|
|
// Automatic import from folder
|
|
const [autoImportEnabled, setAutoImportEnabled] = useState(false);
|
|
const [autoImportSourcePath, setAutoImportSourcePath] = useState("");
|
|
const [autoImportFrequency, setAutoImportFrequency] = useState(60);
|
|
|
|
// Email import
|
|
const [emailImportEnabled, setEmailImportEnabled] = useState(false);
|
|
const [emailImportAddress, setEmailImportAddress] = useState("");
|
|
const [emailImportPassword, setEmailImportPassword] = useState("");
|
|
const [emailImportHost, setEmailImportHost] = useState("");
|
|
const [emailImportPort, setEmailImportPort] = useState(993);
|
|
const [emailImportFrequency, setEmailImportFrequency] = useState(30);
|
|
|
|
// Export folder
|
|
const [exportFolder, setExportFolder] = useState("");
|
|
// BAP export mode
|
|
const [bapExportMode, setBapExportMode] = useState<"browser" | "folder">("browser");
|
|
|
|
// Initialize form with settings from database
|
|
useEffect(() => {
|
|
if (settings) {
|
|
setManualImportEnabled(settings.manualImportEnabled === 1);
|
|
setAutoImportEnabled(settings.autoImportEnabled === 1);
|
|
setAutoImportSourcePath(settings.autoImportSourcePath || "");
|
|
setAutoImportFrequency(settings.autoImportFrequency || 60);
|
|
setEmailImportEnabled(settings.emailImportEnabled === 1);
|
|
setEmailImportAddress(settings.emailImportAddress || "");
|
|
setEmailImportPassword(settings.emailImportPassword || "");
|
|
setEmailImportHost(settings.emailImportHost || "");
|
|
setEmailImportPort(settings.emailImportPort || 993);
|
|
setEmailImportFrequency(settings.emailImportFrequency || 30);
|
|
setExportFolder(settings.exportFolder || "");
|
|
setBapExportMode((settings.bapExportMode as "browser" | "folder") || "browser");
|
|
}
|
|
}, [settings]);
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
await updateMutation.mutateAsync({
|
|
manualImportEnabled: manualImportEnabled ? 1 : 0,
|
|
autoImportEnabled: autoImportEnabled ? 1 : 0,
|
|
autoImportSourcePath: autoImportSourcePath || null,
|
|
autoImportFrequency: autoImportFrequency,
|
|
emailImportEnabled: emailImportEnabled ? 1 : 0,
|
|
emailImportAddress: emailImportAddress || null,
|
|
emailImportPassword: emailImportPassword || null,
|
|
emailImportHost: emailImportHost || null,
|
|
emailImportPort: emailImportPort,
|
|
emailImportFrequency: emailImportFrequency,
|
|
exportFolder: exportFolder || null,
|
|
bapExportMode: bapExportMode,
|
|
});
|
|
|
|
toast.success("Paramètres enregistrés avec succès");
|
|
} catch (error) {
|
|
toast.error("Impossible d'enregistrer les paramètres");
|
|
}
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="flex flex-col items-center justify-center h-64 gap-4">
|
|
<Loader2 className="w-12 h-12 animate-spin text-primary" />
|
|
<p className="text-muted-foreground">Chargement des paramètres...</p>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="max-w-5xl space-y-8">
|
|
{/* Header */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-3 bg-gradient-to-br from-blue-500 to-cyan-600 rounded-xl shadow-lg">
|
|
<Inbox className="w-7 h-7 text-white" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-4xl font-bold bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent">
|
|
Paramètres de réception
|
|
</h1>
|
|
<p className="text-muted-foreground mt-1">
|
|
Configurez les différentes méthodes d'importation de factures
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Manual Import */}
|
|
<Card className="border-2 hover:border-primary/50 transition-colors">
|
|
<CardHeader className="bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-950/20 dark:to-pink-950/20 border-b">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-2 bg-purple-500 rounded-lg">
|
|
<Upload className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<CardTitle className="text-xl">Import manuel</CardTitle>
|
|
<CardDescription className="mt-1">
|
|
Autoriser l'upload manuel de fichiers PDF via l'interface
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
<Badge variant={manualImportEnabled ? "default" : "secondary"} className="text-sm">
|
|
{manualImportEnabled ? "Activé" : "Désactivé"}
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="pt-6">
|
|
<div className="flex items-center justify-between p-4 bg-muted/30 rounded-lg">
|
|
<Label htmlFor="manual-import" className="text-base font-medium">
|
|
Activer l'import manuel
|
|
</Label>
|
|
<Switch
|
|
id="manual-import"
|
|
checked={manualImportEnabled}
|
|
onCheckedChange={setManualImportEnabled}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Automatic Import from Folder */}
|
|
<Card className="border-2 hover:border-primary/50 transition-colors">
|
|
<CardHeader className="bg-gradient-to-r from-blue-50 to-cyan-50 dark:from-blue-950/20 dark:to-cyan-950/20 border-b">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-2 bg-blue-500 rounded-lg">
|
|
<FolderOpen className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<CardTitle className="text-xl">Import automatique depuis dossier</CardTitle>
|
|
<CardDescription className="mt-1">
|
|
Surveiller un dossier et importer automatiquement les nouveaux fichiers PDF
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
<Badge variant={autoImportEnabled ? "default" : "secondary"} className="text-sm">
|
|
{autoImportEnabled ? "Activé" : "Désactivé"}
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6 pt-6">
|
|
<div className="flex items-center justify-between p-4 bg-muted/30 rounded-lg">
|
|
<Label htmlFor="auto-import" className="text-base font-medium">
|
|
Activer l'import automatique
|
|
</Label>
|
|
<Switch
|
|
id="auto-import"
|
|
checked={autoImportEnabled}
|
|
onCheckedChange={setAutoImportEnabled}
|
|
/>
|
|
</div>
|
|
|
|
{autoImportEnabled && (
|
|
<div className="space-y-4 p-4 bg-blue-50/50 dark:bg-blue-950/10 rounded-lg border-2 border-blue-200 dark:border-blue-800">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="source-path" className="text-base font-medium">Chemin du dossier source</Label>
|
|
<Input
|
|
id="source-path"
|
|
type="text"
|
|
placeholder="/chemin/vers/dossier/factures"
|
|
value={autoImportSourcePath}
|
|
onChange={(e) => setAutoImportSourcePath(e.target.value)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Chemin absolu du dossier à surveiller pour les nouveaux fichiers PDF
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="auto-frequency" className="text-base font-medium">Fréquence de lecture (minutes)</Label>
|
|
<Input
|
|
id="auto-frequency"
|
|
type="number"
|
|
min="1"
|
|
value={autoImportFrequency}
|
|
onChange={(e) => setAutoImportFrequency(parseInt(e.target.value) || 60)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Intervalle de temps entre chaque vérification du dossier (minimum: 1 minute)
|
|
</p>
|
|
</div>
|
|
|
|
{/* Service Control Buttons */}
|
|
<div className="flex gap-3 pt-4 border-t border-blue-200 dark:border-blue-800">
|
|
{folderServiceStatus?.isRunning ? (
|
|
<>
|
|
<Button
|
|
variant="destructive"
|
|
onClick={async () => {
|
|
try {
|
|
await stopFolderServiceMutation.mutateAsync();
|
|
await utils.folderImportService.status.invalidate();
|
|
toast.success("Service arrêté");
|
|
} catch (error) {
|
|
toast.error("Impossible d'arrêter le service");
|
|
}
|
|
}}
|
|
disabled={stopFolderServiceMutation.isPending}
|
|
>
|
|
{stopFolderServiceMutation.isPending ? (
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
) : (
|
|
<Square className="mr-2 h-5 w-5" />
|
|
)}
|
|
Arrêter le service
|
|
</Button>
|
|
<Badge variant="default" className="flex items-center gap-2 px-4 bg-green-500">
|
|
<span className="w-2 h-2 bg-white rounded-full animate-pulse" />
|
|
Service actif
|
|
</Badge>
|
|
</>
|
|
) : (
|
|
<Button
|
|
onClick={async () => {
|
|
try {
|
|
const result = await startFolderServiceMutation.mutateAsync();
|
|
if (result.success) {
|
|
await utils.folderImportService.status.invalidate();
|
|
toast.success("Service démarré");
|
|
} else {
|
|
toast.error("Impossible de démarrer le service");
|
|
}
|
|
} catch (error) {
|
|
toast.error("Impossible de démarrer le service");
|
|
}
|
|
}}
|
|
disabled={!autoImportEnabled || startFolderServiceMutation.isPending}
|
|
className="bg-blue-500 hover:bg-blue-600"
|
|
>
|
|
{startFolderServiceMutation.isPending ? (
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
) : (
|
|
<Play className="mr-2 h-5 w-5" />
|
|
)}
|
|
Démarrer le service
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Email Import */}
|
|
<Card className="border-2 hover:border-primary/50 transition-colors">
|
|
<CardHeader className="bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-950/20 dark:to-emerald-950/20 border-b">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-2 bg-green-500 rounded-lg">
|
|
<Mail className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<CardTitle className="text-xl">Import par email</CardTitle>
|
|
<CardDescription className="mt-1">
|
|
Récupérer automatiquement les factures reçues par email
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
<Badge variant={emailImportEnabled ? "default" : "secondary"} className="text-sm">
|
|
{emailImportEnabled ? "Activé" : "Désactivé"}
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6 pt-6">
|
|
<div className="flex items-center justify-between p-4 bg-muted/30 rounded-lg">
|
|
<Label htmlFor="email-import" className="text-base font-medium">
|
|
Activer l'import par email
|
|
</Label>
|
|
<Switch
|
|
id="email-import"
|
|
checked={emailImportEnabled}
|
|
onCheckedChange={setEmailImportEnabled}
|
|
/>
|
|
</div>
|
|
|
|
{emailImportEnabled && (
|
|
<div className="space-y-4 p-4 bg-green-50/50 dark:bg-green-950/10 rounded-lg border-2 border-green-200 dark:border-green-800">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email-address" className="text-base font-medium">Adresse email</Label>
|
|
<Input
|
|
id="email-address"
|
|
type="email"
|
|
placeholder="factures@votreentreprise.com"
|
|
value={emailImportAddress}
|
|
onChange={(e) => setEmailImportAddress(e.target.value)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Adresse email à surveiller pour les factures
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email-password" className="text-base font-medium">Mot de passe</Label>
|
|
<Input
|
|
id="email-password"
|
|
type="password"
|
|
placeholder="••••••••"
|
|
value={emailImportPassword}
|
|
onChange={(e) => setEmailImportPassword(e.target.value)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Mot de passe du compte email (stocké de manière sécurisée)
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email-host" className="text-base font-medium">Serveur IMAP</Label>
|
|
<Input
|
|
id="email-host"
|
|
type="text"
|
|
placeholder="imap.gmail.com"
|
|
value={emailImportHost}
|
|
onChange={(e) => setEmailImportHost(e.target.value)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Adresse du serveur IMAP
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email-port" className="text-base font-medium">Port IMAP</Label>
|
|
<Input
|
|
id="email-port"
|
|
type="number"
|
|
min="1"
|
|
max="65535"
|
|
value={emailImportPort}
|
|
onChange={(e) => setEmailImportPort(parseInt(e.target.value) || 993)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Port SSL (993 par défaut)
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email-frequency" className="text-base font-medium">Fréquence de lecture (minutes)</Label>
|
|
<Input
|
|
id="email-frequency"
|
|
type="number"
|
|
min="1"
|
|
value={emailImportFrequency}
|
|
onChange={(e) => setEmailImportFrequency(parseInt(e.target.value) || 30)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
Intervalle de temps entre chaque vérification des emails (minimum: 1 minute)
|
|
</p>
|
|
</div>
|
|
|
|
{/* Service Control Buttons */}
|
|
<div className="flex flex-wrap gap-3 pt-4 border-t border-green-200 dark:border-green-800">
|
|
{emailServiceStatus?.isRunning ? (
|
|
<>
|
|
<Button
|
|
variant="destructive"
|
|
onClick={async () => {
|
|
try {
|
|
await stopEmailServiceMutation.mutateAsync();
|
|
await utils.emailImportService.status.invalidate();
|
|
toast.success("Service arrêté");
|
|
} catch (error) {
|
|
toast.error("Impossible d'arrêter le service");
|
|
}
|
|
}}
|
|
disabled={stopEmailServiceMutation.isPending}
|
|
>
|
|
{stopEmailServiceMutation.isPending ? (
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
) : (
|
|
<Square className="mr-2 h-5 w-5" />
|
|
)}
|
|
Arrêter le service
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
onClick={async () => {
|
|
try {
|
|
toast.info("Vérification en cours...");
|
|
const result = await checkNowEmailMutation.mutateAsync();
|
|
if (result.success) {
|
|
await utils.importLogs.getByUser.invalidate();
|
|
toast.success(result.message);
|
|
} else {
|
|
toast.error(result.message);
|
|
}
|
|
} catch (error: any) {
|
|
toast.error("Impossible de vérifier les emails");
|
|
}
|
|
}}
|
|
disabled={checkNowEmailMutation.isPending}
|
|
className="border-green-300 hover:bg-green-50 dark:hover:bg-green-950/20"
|
|
>
|
|
{checkNowEmailMutation.isPending ? (
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
) : (
|
|
<CheckCircle2 className="mr-2 h-5 w-5" />
|
|
)}
|
|
Vérifier maintenant
|
|
</Button>
|
|
<Badge variant="default" className="flex items-center gap-2 px-4 bg-green-500">
|
|
<span className="w-2 h-2 bg-white rounded-full animate-pulse" />
|
|
Service actif
|
|
</Badge>
|
|
</>
|
|
) : (
|
|
<Button
|
|
onClick={async () => {
|
|
try {
|
|
const result = await startEmailServiceMutation.mutateAsync();
|
|
if (result.success) {
|
|
await utils.emailImportService.status.invalidate();
|
|
toast.success("Service démarré");
|
|
} else {
|
|
toast.error("Impossible de démarrer le service");
|
|
}
|
|
} catch (error) {
|
|
toast.error("Impossible de démarrer le service");
|
|
}
|
|
}}
|
|
disabled={!emailImportEnabled || startEmailServiceMutation.isPending}
|
|
className="bg-green-500 hover:bg-green-600"
|
|
>
|
|
{startEmailServiceMutation.isPending ? (
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
) : (
|
|
<Play className="mr-2 h-5 w-5" />
|
|
)}
|
|
Démarrer le service
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Export Folder & BAP Export Mode */}
|
|
<Card className="border-2 hover:border-primary/50 transition-colors">
|
|
<CardHeader className="bg-gradient-to-r from-orange-50 to-amber-50 dark:from-orange-950/20 dark:to-amber-950/20 border-b">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-2 bg-orange-500 rounded-lg">
|
|
<Download className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<CardTitle className="text-xl">Export des factures BAP</CardTitle>
|
|
<CardDescription className="mt-1">
|
|
Configurez le mode d'export et le dossier de destination pour les factures validées BAP
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6 pt-6">
|
|
{/* BAP Export Mode */}
|
|
<div className="space-y-3">
|
|
<Label className="text-base font-medium">Mode d'export BAP</Label>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={() => setBapExportMode("browser")}
|
|
className={`flex flex-col items-center gap-2 p-4 rounded-xl border-2 transition-all ${
|
|
bapExportMode === "browser"
|
|
? "border-blue-500 bg-blue-50 dark:bg-blue-950/30 text-blue-700 dark:text-blue-300"
|
|
: "border-muted hover:border-muted-foreground/40 text-muted-foreground"
|
|
}`}
|
|
>
|
|
<Monitor className="w-8 h-8" />
|
|
<div className="text-center">
|
|
<div className="font-semibold text-sm">Ouvrir dans le navigateur</div>
|
|
<div className="text-xs mt-0.5 opacity-70">Le PDF s'ouvre directement dans un nouvel onglet</div>
|
|
</div>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setBapExportMode("folder")}
|
|
className={`flex flex-col items-center gap-2 p-4 rounded-xl border-2 transition-all ${
|
|
bapExportMode === "folder"
|
|
? "border-orange-500 bg-orange-50 dark:bg-orange-950/30 text-orange-700 dark:text-orange-300"
|
|
: "border-muted hover:border-muted-foreground/40 text-muted-foreground"
|
|
}`}
|
|
>
|
|
<FolderOutput className="w-8 h-8" />
|
|
<div className="text-center">
|
|
<div className="font-semibold text-sm">Enregistrer dans un dossier</div>
|
|
<div className="text-xs mt-0.5 opacity-70">Le PDF est copié dans le dossier configuré ci-dessous</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Export Folder (shown for both modes but required for folder mode) */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="exportFolder" className="text-base font-medium">
|
|
Chemin du dossier d'export
|
|
{bapExportMode === "folder" && <span className="text-red-500 ml-1">*</span>}
|
|
</Label>
|
|
<Input
|
|
id="exportFolder"
|
|
type="text"
|
|
placeholder="/chemin/vers/dossier/export"
|
|
value={exportFolder}
|
|
onChange={(e) => setExportFolder(e.target.value)}
|
|
className="h-11"
|
|
/>
|
|
<p className="text-sm text-muted-foreground">
|
|
{bapExportMode === "folder"
|
|
? "Chemin absolu obligatoire du dossier où les PDF BAP seront enregistrés"
|
|
: "Chemin optionnel du dossier d'export (utilisé pour l'export SFTP et l'export groupé)"}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Save Button */}
|
|
<div className="flex justify-end pb-8">
|
|
<Button
|
|
onClick={handleSave}
|
|
disabled={updateMutation.isPending}
|
|
size="lg"
|
|
className="bg-gradient-to-r from-blue-500 to-cyan-600 hover:from-blue-600 hover:to-cyan-700 text-white shadow-lg"
|
|
>
|
|
{updateMutation.isPending ? (
|
|
<>
|
|
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
|
Enregistrement...
|
|
</>
|
|
) : (
|
|
<>
|
|
<Save className="mr-2 h-5 w-5" />
|
|
Enregistrer les paramètres
|
|
</>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|