Add StepImportProfile component

This commit is contained in:
Sosthene 2025-07-15 20:41:26 +02:00
parent bd4c9205d9
commit 26edba3504
2 changed files with 204 additions and 0 deletions

View File

@ -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%;
}
}
}
}

View File

@ -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<File | null>(null);
const [profileData, setProfileData] = useState<any>(null);
const [error, setError] = useState<string>("");
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 (
<div className={classes["root"]}>
<Typography typo={ETypo.DISPLAY_LARGE}>
<div className={classes["title"]}>Importer un profil</div>
</Typography>
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["description"]}>
Sélectionnez un fichier JSON contenant vos données de profil pour restaurer votre session.
</Typography>
<div className={classes["import-section"]}>
<DragAndDrop
title="Importer un profil"
description="Glissez-déposez votre fichier JSON ou cliquez pour parcourir"
onChange={handleFileUpload}
/>
</div>
{(error || importError) && (
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_ERROR_500} className={classes["error"]}>
{error || (importError?.constraints && Object.values(importError.constraints)[0])}
</Typography>
)}
{profileData && (
<div className={classes["profile-preview"]}>
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} className={classes["preview-title"]}>
Profil détecté :
</Typography>
<Typography typo={ETypo.TEXT_MD_REGULAR}>
{profileData.userData?.email || "Email non disponible"}
</Typography>
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}>
Version : {profileData.version}
</Typography>
{profileData.exportedAt && (
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}>
Exporté le : {new Date(profileData.exportedAt).toLocaleDateString('fr-FR')}
</Typography>
)}
</div>
)}
<div className={classes["actions"]}>
<Button variant={EButtonVariant.SECONDARY} onClick={onBack} className={classes["back-button"]}>
Retour
</Button>
<Button
variant={EButtonVariant.PRIMARY}
onClick={handleSubmit}
disabled={!profileData}
className={classes["submit-button"]}
>
Importer le profil
</Button>
</div>
</div>
);
}