294 lines
9.9 KiB
TypeScript
294 lines
9.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Sidebar } from "@/components/dashboard/sidebar";
|
|
import { useBankingData } from "@/lib/hooks";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from "@/components/ui/alert-dialog";
|
|
import {
|
|
Download,
|
|
Trash2,
|
|
Upload,
|
|
RefreshCw,
|
|
Database,
|
|
FileJson,
|
|
Tags,
|
|
} from "lucide-react";
|
|
import type { BankingData } from "@/lib/types";
|
|
|
|
export default function SettingsPage() {
|
|
const { data, isLoading, refresh, update } = useBankingData();
|
|
const [importing, setImporting] = useState(false);
|
|
|
|
if (isLoading || !data) {
|
|
return (
|
|
<div className="flex h-screen">
|
|
<Sidebar />
|
|
<main className="flex-1 flex items-center justify-center">
|
|
<RefreshCw className="w-8 h-8 animate-spin text-muted-foreground" />
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const exportData = () => {
|
|
const dataStr = JSON.stringify(data, null, 2);
|
|
const blob = new Blob([dataStr], { type: "application/json" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = `fintrack-backup-${new Date().toISOString().split("T")[0]}.json`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
};
|
|
|
|
const importData = () => {
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = ".json";
|
|
input.onchange = async (e) => {
|
|
const file = (e.target as HTMLInputElement).files?.[0];
|
|
if (!file) return;
|
|
|
|
setImporting(true);
|
|
try {
|
|
const content = await file.text();
|
|
const importedData = JSON.parse(content) as BankingData;
|
|
|
|
// Validate structure
|
|
if (
|
|
!importedData.accounts ||
|
|
!importedData.transactions ||
|
|
!importedData.categories ||
|
|
!importedData.folders
|
|
) {
|
|
alert("Format de fichier invalide");
|
|
return;
|
|
}
|
|
|
|
update(importedData);
|
|
alert("Données importées avec succès");
|
|
} catch (error) {
|
|
alert("Erreur lors de l'import");
|
|
} finally {
|
|
setImporting(false);
|
|
}
|
|
};
|
|
input.click();
|
|
};
|
|
|
|
const resetData = () => {
|
|
localStorage.removeItem("banking-app-data");
|
|
window.location.reload();
|
|
};
|
|
|
|
const clearAllCategories = async () => {
|
|
try {
|
|
const response = await fetch(
|
|
"/api/banking/transactions/clear-categories",
|
|
{
|
|
method: "POST",
|
|
},
|
|
);
|
|
if (!response.ok) throw new Error("Erreur");
|
|
refresh();
|
|
alert("Catégories supprimées de toutes les opérations");
|
|
} catch (error) {
|
|
console.error(error);
|
|
alert("Erreur lors de la suppression des catégories");
|
|
}
|
|
};
|
|
|
|
const categorizedCount = data.transactions.filter((t) => t.categoryId).length;
|
|
|
|
return (
|
|
<div className="flex h-screen bg-background">
|
|
<Sidebar />
|
|
<main className="flex-1 overflow-auto">
|
|
<div className="p-6 space-y-6 max-w-2xl">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-foreground">Paramètres</h1>
|
|
<p className="text-muted-foreground">
|
|
Gérez vos données et préférences
|
|
</p>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Database className="w-5 h-5" />
|
|
Données
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Exportez ou importez vos données pour les sauvegarder ou les
|
|
transférer
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
|
|
<div>
|
|
<p className="font-medium">Statistiques</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
{data.accounts.length} comptes, {data.transactions.length}{" "}
|
|
transactions, {data.categories.length} catégories
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-2">
|
|
<Button
|
|
onClick={exportData}
|
|
variant="outline"
|
|
className="flex-1 bg-transparent"
|
|
>
|
|
<Download className="w-4 h-4 mr-2" />
|
|
Exporter (JSON)
|
|
</Button>
|
|
<Button
|
|
onClick={importData}
|
|
variant="outline"
|
|
className="flex-1 bg-transparent"
|
|
disabled={importing}
|
|
>
|
|
<Upload className="w-4 h-4 mr-2" />
|
|
{importing ? "Import..." : "Importer"}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-red-200">
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2 text-red-600">
|
|
<Trash2 className="w-5 h-5" />
|
|
Zone dangereuse
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Actions irréversibles - procédez avec prudence
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{/* Supprimer catégories des opérations */}
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
className="w-full justify-start border-orange-300 text-orange-700 hover:bg-orange-50"
|
|
>
|
|
<Tags className="w-4 h-4 mr-2" />
|
|
Supprimer les catégories des opérations
|
|
<span className="ml-auto text-xs text-muted-foreground">
|
|
{categorizedCount} opération
|
|
{categorizedCount > 1 ? "s" : ""} catégorisée
|
|
{categorizedCount > 1 ? "s" : ""}
|
|
</span>
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>
|
|
Supprimer toutes les catégories ?
|
|
</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Cette action va retirer la catégorie de {categorizedCount}{" "}
|
|
opération{categorizedCount > 1 ? "s" : ""}. Les catégories
|
|
elles-mêmes ne seront pas supprimées, seulement leur
|
|
affectation aux opérations.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Annuler</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={clearAllCategories}
|
|
className="bg-orange-600 hover:bg-orange-700"
|
|
>
|
|
Supprimer les affectations
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
|
|
{/* Réinitialiser toutes les données */}
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
variant="destructive"
|
|
className="w-full justify-start"
|
|
>
|
|
<Trash2 className="w-4 h-4 mr-2" />
|
|
Réinitialiser toutes les données
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Êtes-vous sûr ?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Cette action supprimera définitivement tous vos comptes,
|
|
transactions, catégories et dossiers. Cette action est
|
|
irréversible.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Annuler</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={resetData}
|
|
className="bg-red-600 hover:bg-red-700"
|
|
>
|
|
Supprimer tout
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<FileJson className="w-5 h-5" />
|
|
Format OFX
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Informations sur l'import de fichiers
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="prose prose-sm text-muted-foreground">
|
|
<p>
|
|
L'application accepte les fichiers au format OFX (Open
|
|
Financial Exchange) ou QFX. Ces fichiers sont généralement
|
|
disponibles depuis l'espace client de votre banque.
|
|
</p>
|
|
<p className="mt-2">
|
|
Lors de l'import, les transactions sont automatiquement
|
|
catégorisées selon les mots-clés définis. Les doublons sont
|
|
détectés et ignorés automatiquement.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|