From 26edba35045260a35c1e895c72b1ec95985024d6 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Tue, 15 Jul 2025 20:41:26 +0200 Subject: [PATCH] Add StepImportProfile component --- .../StepImportProfile/classes.module.scss | 72 ++++++++++ .../Layouts/Login/StepImportProfile/index.tsx | 132 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 src/front/Components/Layouts/Login/StepImportProfile/classes.module.scss create mode 100644 src/front/Components/Layouts/Login/StepImportProfile/index.tsx diff --git a/src/front/Components/Layouts/Login/StepImportProfile/classes.module.scss b/src/front/Components/Layouts/Login/StepImportProfile/classes.module.scss new file mode 100644 index 00000000..6350bf78 --- /dev/null +++ b/src/front/Components/Layouts/Login/StepImportProfile/classes.module.scss @@ -0,0 +1,72 @@ +@import "@Themes/constants.scss"; + +.root { + display: flex; + flex-direction: column; + max-width: 530px; + margin: auto; + margin-top: 220px; + + .title { + text-align: left; + + @media (max-width: $screen-s) { + font-family: 48px; + } + } + + .description { + margin-top: 16px; + color: var(--color-neutral-700); + line-height: 1.5; + } + + .import-section { + margin-top: 32px; + } + + .error { + margin-top: 16px; + padding: 12px; + background-color: var(--color-error-50); + border: 1px solid var(--color-error-200); + border-radius: 6px; + } + + .profile-preview { + margin-top: 24px; + padding: 16px; + background-color: var(--color-neutral-50); + border: 1px solid var(--color-neutral-200); + border-radius: 8px; + + .preview-title { + margin-bottom: 12px; + color: var(--color-neutral-800); + } + } + + .actions { + margin-top: 32px; + display: flex; + gap: 16px; + justify-content: flex-end; + + .back-button { + min-width: 100px; + } + + .submit-button { + min-width: 140px; + } + + @media (max-width: $screen-s) { + flex-direction: column; + + .back-button, + .submit-button { + width: 100%; + } + } + } +} \ No newline at end of file diff --git a/src/front/Components/Layouts/Login/StepImportProfile/index.tsx b/src/front/Components/Layouts/Login/StepImportProfile/index.tsx new file mode 100644 index 00000000..44ddfeb0 --- /dev/null +++ b/src/front/Components/Layouts/Login/StepImportProfile/index.tsx @@ -0,0 +1,132 @@ +import React, { useState, useCallback } from "react"; +import classes from "./classes.module.scss"; +import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; +import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; +import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop"; +import { ValidationError } from "class-validator"; + +type IProps = { + onSubmit: (profileData: any) => void; + onBack: () => void; + validationErrors: ValidationError[]; +}; + +export default function StepImportProfile(props: IProps) { + const { onSubmit, onBack, validationErrors } = props; + const [importedFile, setImportedFile] = useState(null); + const [profileData, setProfileData] = useState(null); + const [error, setError] = useState(""); + + const validateProfileStructure = (data: any): boolean => { + // Basic validation for profile structure + return data && + typeof data === "object" && + data.version && + data.userData && + typeof data.userData === "object"; + }; + + const handleFileUpload = useCallback(async (files: File[]) => { + const file = files[0]; + if (!file) return; + + // Validate file type + if (file.type !== "application/json") { + setError("Veuillez sélectionner un fichier JSON valide."); + return; + } + + // Validate file size (max 1MB) + if (file.size > 1024 * 1024) { + setError("Le fichier est trop volumineux. Taille maximum : 1MB."); + return; + } + + setImportedFile(file); + setError(""); + + // Read and parse JSON + try { + const text = await file.text(); + const data = JSON.parse(text); + + // Validate profile structure + if (!validateProfileStructure(data)) { + setError("Le fichier ne contient pas un profil valide."); + return; + } + + setProfileData(data); + } catch (err) { + setError("Erreur lors de la lecture du fichier JSON."); + } + }, []); + + const handleSubmit = useCallback(() => { + if (profileData) { + onSubmit(profileData); + } + }, [profileData, onSubmit]); + + // Get validation error for import + const importError = validationErrors.find((error) => error.property === "import"); + + return ( +
+ +
Importer un profil
+
+ + + Sélectionnez un fichier JSON contenant vos données de profil pour restaurer votre session. + + +
+ +
+ + {(error || importError) && ( + + {error || (importError?.constraints && Object.values(importError.constraints)[0])} + + )} + + {profileData && ( +
+ + Profil détecté : + + + {profileData.userData?.email || "Email non disponible"} + + + Version : {profileData.version} + + {profileData.exportedAt && ( + + Exporté le : {new Date(profileData.exportedAt).toLocaleDateString('fr-FR')} + + )} +
+ )} + +
+ + +
+
+ ); +} \ No newline at end of file