Skip to content

Commit 949d7e9

Browse files
committed
feat: update localization files with success messages and error handling for database restoration
1 parent 9fb9c36 commit 949d7e9

File tree

7 files changed

+204
-44
lines changed

7 files changed

+204
-44
lines changed

client/src/locales/base/ar-SA.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,14 @@
7373
"download-converted-images_other": "تنزيل الصور المحولة",
7474
"export-database-description": "استخدم هذه الأداة لتصدير محتوى قاعدة البيانات.",
7575
"restore-database-description": "استخدم هذه الأداة لاستعادة قاعدة البيانات",
76-
"restore-database-success": "قاعدة البيانات كما تم ترميمها"
76+
"restore-database-success": "تم استعادة قاعدة البيانات",
77+
"cancel": "يلغي",
78+
"confirm-restore": "تأكيد الاستعادة",
79+
"confirm-restore-warning": "هل أنت متأكد من استعادة قاعدة البيانات؟",
80+
"error-file-too-large": "خطأ: ملف كبير جدًا",
81+
"error-not-json-file": "خطأ: ليس ملف JSON",
82+
"error-reading-file": "خطأ قراءة الملف",
83+
"error-unknown": "خطأ غير معروف",
84+
"restore-database": "استعادة قاعدة البيانات",
85+
"restore-the-database": "استعادة قاعدة البيانات"
7786
}

client/src/locales/base/en-US.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,13 @@
7373
"export-database-description": "Use this tool to export the database content.",
7474
"restore-the-database": "Restore the database",
7575
"restore-database-description": "Use this tool to restore the database",
76-
"restore-database-success": "The database as been restored"
76+
"restore-database-success": "The database has been restored",
77+
"error-not-json-file": "Error: not a JSON file",
78+
"error-file-too-large": "Error: File too large",
79+
"error-unknown": "Unknown error",
80+
"error-reading-file": "Error reading file",
81+
"restore-database": "Restore the database",
82+
"confirm-restore": "Confirm the restoration",
83+
"cancel": "Cancel",
84+
"confirm-restore-warning": "Are you sure to restore the database ?"
7785
}

client/src/locales/base/es-ES.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,14 @@
7373
"download-converted-images_other": "Descargar imágenes convertidas",
7474
"export-database-description": "Use esta herramienta para exportar el contenido de la base de datos.",
7575
"restore-database-description": "Use esta herramienta para restaurar la base de datos",
76-
"restore-database-success": "La base de datos como se ha restaurado"
76+
"restore-database-success": "La base de datos se ha restaurado",
77+
"cancel": "Cancelar",
78+
"confirm-restore": "Confirmar la restauración",
79+
"confirm-restore-warning": "¿Estás seguro de restaurar la base de datos?",
80+
"error-file-too-large": "Error: archivo demasiado grande",
81+
"error-not-json-file": "Error: no un archivo JSON",
82+
"error-reading-file": "Error al archivo de lectura",
83+
"error-unknown": "Error desconocido",
84+
"restore-database": "Restaurar la base de datos",
85+
"restore-the-database": "Restaurar la base de datos"
7786
}

client/src/locales/base/fr-FR.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,14 @@
7373
"download-converted-images_other": "Télécharger des images converties",
7474
"export-database-description": "Utilisez cet outil pour exporter le contenu de la base de données.",
7575
"restore-database-description": "Utilisez cet outil pour restaurer la base de données",
76-
"restore-database-success": "La base de données telle qu'elle a été restaurée"
76+
"restore-database-success": "La base de données a été restaurée",
77+
"cancel": "Annuler",
78+
"confirm-restore": "Confirmer la restauration",
79+
"confirm-restore-warning": "Êtes-vous sûr de restaurer la base de données?",
80+
"error-file-too-large": "Erreur: fichier trop grand",
81+
"error-not-json-file": "Erreur: pas un fichier JSON",
82+
"error-reading-file": "Fichier de lecture d'erreur",
83+
"error-unknown": "Erreur inconnue",
84+
"restore-database": "Restaurer la base de données",
85+
"restore-the-database": "Restaurer la base de données"
7786
}

client/src/locales/base/he-IL.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,14 @@
7373
"download-converted-images_other": "הורד תמונות שהוסבו",
7474
"export-database-description": "השתמש בכלי זה כדי לייצא את תוכן מסד הנתונים.",
7575
"restore-database-description": "השתמש בכלי זה כדי לשחזר את מסד הנתונים",
76-
"restore-database-success": "בסיס הנתונים ששוחזר"
76+
"restore-database-success": "בסיס הנתונים שוחזר",
77+
"cancel": "לְבַטֵל",
78+
"confirm-restore": "אשר את השיקום",
79+
"confirm-restore-warning": "האם אתה בטוח לשחזר את מסד הנתונים?",
80+
"error-file-too-large": "שגיאה: קובץ גדול מדי",
81+
"error-not-json-file": "שגיאה: לא קובץ JSON",
82+
"error-reading-file": "קובץ קריאת שגיאה",
83+
"error-unknown": "שגיאה לא ידועה",
84+
"restore-database": "שחזר את בסיס הנתונים",
85+
"restore-the-database": "שחזר את בסיס הנתונים"
7786
}

client/src/locales/base/zh-CN.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,14 @@
7373
"download-converted-images_other": "下载转换的图像",
7474
"export-database-description": "使用此工具导出数据库内容。",
7575
"restore-database-description": "使用此工具还原数据库",
76-
"restore-database-success": "还原的数据库"
76+
"restore-database-success": "数据库已恢复",
77+
"cancel": "取消",
78+
"confirm-restore": "确认修复",
79+
"confirm-restore-warning": "您一定要还原数据库吗?",
80+
"error-file-too-large": "错误:文件太大",
81+
"error-not-json-file": "错误:不是JSON文件",
82+
"error-reading-file": "错误读取文件",
83+
"error-unknown": "未知错误",
84+
"restore-database": "还原数据库",
85+
"restore-the-database": "还原数据库"
7786
}

client/src/pages/manage-database.tsx

Lines changed: 145 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,76 +8,183 @@ import DefaultLayout from "@/layouts/default";
88
import { ProtectedFetchToDownload } from "@/components/secured-fetch-to-download-button";
99
import FileUpload from "@/components/file-upload/file-upload";
1010
import { postJsonToSecuredApi } from "@/components/auth0";
11+
import Panel from "@/components/panel";
1112

1213
export default function ManageDatabasePage() {
1314
const { t } = useTranslation();
14-
const [fileUpload, setFileUpload] = useState(null as File | null);
15+
const [fileUpload, setFileUpload] = useState<File | null>(null);
1516
const { getAccessTokenSilently } = useAuth0();
16-
const [restoreDatabaseResult, setRestoreDatabaseResult] = useState(
17-
null as any | null,
18-
);
17+
const [restoreDatabaseResult, setRestoreDatabaseResult] = useState<
18+
any | null
19+
>(null);
20+
const [error, setError] = useState<string | null>(null);
21+
const [isLoading, setIsLoading] = useState(false);
22+
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
23+
24+
const validateBackupFile = (
25+
file: File,
26+
): { valid: boolean; error?: string } => {
27+
// Vérifier le type MIME
28+
if (file.type !== "application/json") {
29+
return { valid: false, error: t("error-not-json-file") };
30+
}
31+
32+
// Vérifier la taille
33+
if (file.size > 10 * 1024 * 1024) {
34+
// 10MB max
35+
return { valid: false, error: t("error-file-too-large") };
36+
}
37+
38+
return { valid: true };
39+
};
1940

2041
const handleJsonFileUpload = async (file: File) => {
42+
setError(null);
43+
setIsLoading(true);
44+
2145
const reader = new FileReader();
2246

2347
reader.onload = async (event) => {
2448
const jsonData = event.target?.result;
2549

2650
if (jsonData) {
2751
try {
28-
const destUrl = `${import.meta.env.API_BASE_URL}/backup/json`;
52+
// Valider que le JSON est bien formé
53+
const parsedData = JSON.parse(jsonData as string);
54+
55+
// Vous pouvez ajouter ici une validation de la structure du JSON
2956

3057
const ret = await postJsonToSecuredApi(
3158
`${import.meta.env.API_BASE_URL}/backup/json`,
32-
JSON.parse(jsonData as string),
59+
parsedData,
3360
getAccessTokenSilently,
3461
);
3562

3663
setRestoreDatabaseResult(ret);
3764
} catch (error) {
38-
console.error("Error parsing JSON:", error);
65+
console.error("Error processing JSON:", error);
66+
setError(error instanceof Error ? error.message : t("error-unknown"));
67+
} finally {
68+
setIsLoading(false);
3969
}
4070
}
4171
};
72+
73+
reader.onerror = () => {
74+
setError(t("error-reading-file"));
75+
setIsLoading(false);
76+
};
77+
4278
reader.readAsText(file);
4379
};
4480

4581
return (
4682
<DefaultLayout>
4783
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
48-
<div className="inline-block max-w-lg text-center justify-center">
84+
<div className="inline-block max-w-lg text-center justify-center mb-6">
4985
<h1 className={title()}>{t("manage-database")}</h1>
5086
</div>
51-
<p className="text-center text-muted-foreground">
52-
{t("export-database-description")}
53-
</p>
54-
<ProtectedFetchToDownload
55-
putDateInFilename
56-
buttonText={t("export-database")}
57-
downloadLinkText={t("download-backup")}
58-
filename={`database-backup.json`}
59-
permission={import.meta.env.BACKUP_PERMISSION}
60-
url={`${import.meta.env.API_BASE_URL}/backup/json`}
61-
/>
62-
<p className="text-center text-muted-foreground">
63-
{t("restore-database-description")}
64-
</p>
65-
<FileUpload
66-
accept="application/json"
67-
browseButtonText={t("browse")}
68-
resetButtonText={t("reset")}
69-
onChange={(files) => {
70-
setFileUpload(files[0]);
71-
}}
72-
/>
73-
{fileUpload && !restoreDatabaseResult?.success && (
74-
<Button onPress={async () => await handleJsonFileUpload(fileUpload)}>
75-
{t("restore-the-database")}
76-
</Button>
77-
)}
78-
{restoreDatabaseResult?.success && (
79-
<div className="text-center text-muted-foreground">
80-
<p>{t("restore-database-success")}</p>
87+
88+
<div className="grid gap-8 w-full max-w-xl">
89+
{/* Section Export */}
90+
<Panel
91+
title={t("export-database")}
92+
description={t("export-database-description")}
93+
>
94+
<ProtectedFetchToDownload
95+
putDateInFilename
96+
buttonText={t("export-database")}
97+
downloadLinkText={t("download-backup")}
98+
filename={`database-backup.json`}
99+
permission={import.meta.env.BACKUP_PERMISSION}
100+
url={`${import.meta.env.API_BASE_URL}/backup/json`}
101+
/>
102+
</Panel>
103+
104+
{/* Section Restore */}
105+
<Panel
106+
title={t("restore-database")}
107+
description={t("restore-database-description")}
108+
>
109+
<div className="space-y-4">
110+
<FileUpload
111+
accept="application/json"
112+
browseButtonText={t("browse")}
113+
className="rounded-xl"
114+
resetButtonText={t("reset")}
115+
onChange={(files) => {
116+
if (files && files.length > 0) {
117+
const validation = validateBackupFile(files[0]);
118+
119+
if (validation.valid) {
120+
setFileUpload(files[0]);
121+
setError(null);
122+
} else {
123+
setFileUpload(null);
124+
setError(validation.error || "");
125+
}
126+
} else {
127+
setFileUpload(null);
128+
setError(null);
129+
}
130+
setRestoreDatabaseResult(null);
131+
}}
132+
/>
133+
134+
{fileUpload && !restoreDatabaseResult?.success && (
135+
<div className="flex justify-center">
136+
<Button
137+
color="primary"
138+
onPress={() => setShowConfirmDialog(true)}
139+
>
140+
{t("restore-the-database")}
141+
</Button>
142+
</div>
143+
)}
144+
145+
{error && (
146+
<div className="mt-2 p-3 bg-danger-50 text-danger border border-danger-200 rounded-lg">
147+
<p className="text-center">{error}</p>
148+
</div>
149+
)}
150+
151+
{restoreDatabaseResult?.success && (
152+
<div className="mt-2 p-3 bg-success-50 text-success border border-success-200 rounded-lg">
153+
<p className="text-center">{t("restore-database-success")}</p>
154+
</div>
155+
)}
156+
</div>
157+
</Panel>
158+
</div>
159+
160+
{/* Confirmation dialog */}
161+
{showConfirmDialog && (
162+
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
163+
<div className="bg-gray-100 dark:bg-gray-700 p-6 rounded-lg max-w-md w-full">
164+
<h3 className="text-lg font-semibold mb-2">
165+
{t("confirm-restore")}
166+
</h3>
167+
<p className="mb-4 text-danger">{t("confirm-restore-warning")}</p>
168+
<div className="flex justify-end gap-2">
169+
<Button
170+
color="warning"
171+
variant="light"
172+
onPress={() => setShowConfirmDialog(false)}
173+
>
174+
{t("cancel")}
175+
</Button>
176+
<Button
177+
color="danger"
178+
isLoading={isLoading}
179+
onPress={async () => {
180+
setShowConfirmDialog(false);
181+
await handleJsonFileUpload(fileUpload!);
182+
}}
183+
>
184+
{t("confirm-restore")}
185+
</Button>
186+
</div>
187+
</div>
81188
</div>
82189
)}
83190
</section>

0 commit comments

Comments
 (0)