refactor: standardize quotation marks across all files and improve code consistency
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
"use client"
|
||||
"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 { 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,
|
||||
@@ -15,13 +21,21 @@ import {
|
||||
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"
|
||||
} 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)
|
||||
const { data, isLoading, refresh, update } = useBankingData();
|
||||
const [importing, setImporting] = useState(false);
|
||||
|
||||
if (isLoading || !data) {
|
||||
return (
|
||||
@@ -31,72 +45,80 @@ export default function SettingsPage() {
|
||||
<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 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"
|
||||
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
|
||||
const file = (e.target as HTMLInputElement).files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
setImporting(true)
|
||||
setImporting(true);
|
||||
try {
|
||||
const content = await file.text()
|
||||
const importedData = JSON.parse(content) as BankingData
|
||||
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
|
||||
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")
|
||||
update(importedData);
|
||||
alert("Données importées avec succès");
|
||||
} catch (error) {
|
||||
alert("Erreur lors de l'import")
|
||||
alert("Erreur lors de l'import");
|
||||
} finally {
|
||||
setImporting(false)
|
||||
setImporting(false);
|
||||
}
|
||||
}
|
||||
input.click()
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
localStorage.removeItem("banking-app-data")
|
||||
window.location.reload()
|
||||
}
|
||||
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")
|
||||
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")
|
||||
console.error(error);
|
||||
alert("Erreur lors de la suppression des catégories");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const categorizedCount = data.transactions.filter((t) => t.categoryId).length
|
||||
const categorizedCount = data.transactions.filter((t) => t.categoryId).length;
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-background">
|
||||
@@ -105,7 +127,9 @@ export default function SettingsPage() {
|
||||
<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>
|
||||
<p className="text-muted-foreground">
|
||||
Gérez vos données et préférences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
@@ -114,25 +138,37 @@ export default function SettingsPage() {
|
||||
<Database className="w-5 h-5" />
|
||||
Données
|
||||
</CardTitle>
|
||||
<CardDescription>Exportez ou importez vos données pour les sauvegarder ou les transférer</CardDescription>
|
||||
<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
|
||||
{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">
|
||||
<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}>
|
||||
<Button
|
||||
onClick={importData}
|
||||
variant="outline"
|
||||
className="flex-1 bg-transparent"
|
||||
disabled={importing}
|
||||
>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
{importing ? "Import..." : "Importer"}
|
||||
</Button>
|
||||
@@ -146,31 +182,45 @@ export default function SettingsPage() {
|
||||
<Trash2 className="w-5 h-5" />
|
||||
Zone dangereuse
|
||||
</CardTitle>
|
||||
<CardDescription>Actions irréversibles - procédez avec prudence</CardDescription>
|
||||
<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">
|
||||
<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" : ""}
|
||||
{categorizedCount} opération
|
||||
{categorizedCount > 1 ? "s" : ""} catégorisée
|
||||
{categorizedCount > 1 ? "s" : ""}
|
||||
</span>
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Supprimer toutes les catégories ?</AlertDialogTitle>
|
||||
<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.
|
||||
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">
|
||||
<AlertDialogAction
|
||||
onClick={clearAllCategories}
|
||||
className="bg-orange-600 hover:bg-orange-700"
|
||||
>
|
||||
Supprimer les affectations
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
@@ -180,7 +230,10 @@ export default function SettingsPage() {
|
||||
{/* Réinitialiser toutes les données */}
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="destructive" className="w-full justify-start">
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="w-full justify-start"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Réinitialiser toutes les données
|
||||
</Button>
|
||||
@@ -189,13 +242,17 @@ export default function SettingsPage() {
|
||||
<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.
|
||||
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">
|
||||
<AlertDialogAction
|
||||
onClick={resetData}
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
>
|
||||
Supprimer tout
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
@@ -210,17 +267,21 @@ export default function SettingsPage() {
|
||||
<FileJson className="w-5 h-5" />
|
||||
Format OFX
|
||||
</CardTitle>
|
||||
<CardDescription>Informations sur l'import de fichiers</CardDescription>
|
||||
<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.
|
||||
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.
|
||||
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>
|
||||
@@ -228,5 +289,5 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user