diff --git a/admin/src/scenes/classe/Import.tsx b/admin/src/scenes/classe/Import.tsx new file mode 100644 index 0000000000..f485a09004 --- /dev/null +++ b/admin/src/scenes/classe/Import.tsx @@ -0,0 +1,106 @@ +import React, { ChangeEvent, useRef, useState } from "react"; +import ReactLoading from "react-loading"; +import { HiOutlineDocumentAdd } from "react-icons/hi"; + +import { MIME_TYPES, translate } from "snu-lib"; + +import api from "@/services/api"; +import { capture } from "@/sentry"; + +import { Button, Container, Header, Page } from "@snu/ds/admin"; + +import { toastr } from "react-redux-toastr"; + +const FILE_SIZE_LIMIT = 5 * 1024 * 1024; + +export default function Import() { + const [isUploading, setIsUploading] = useState(false); + const [uploadError, setUploadError] = useState(null); + const fileInput = useRef(null); + + function importFile(e: React.MouseEvent) { + e.preventDefault(); + setUploadError(null); + if (fileInput && fileInput.current) { + fileInput.current.click(); + } + } + + async function handleUpload(e: ChangeEvent) { + if (!e?.target?.files?.length) return; + const file = e.target.files[0]; + setUploadError(null); + if (file.type !== MIME_TYPES.EXCEL) { + setUploadError("Le fichier doit être au format Excel."); + return; + } + if (file.size > FILE_SIZE_LIMIT) { + setUploadError("Votre fichier dépasse la limite de 5Mo."); + return; + } + + setIsUploading(true); + try { + const res = await api.uploadFiles(`/cle/classe/importAuto/classe-importAuto`, [file]); + if (res.code === "FILE_CORRUPTED") { + setUploadError("Le fichier semble corrompu. Pouvez-vous changer le format ou regénérer votre fichier ? Si vous rencontrez toujours le problème, contactez le support."); + } else if (!res.ok) { + toastr.error("Une erreur est survenue lors de l'import du fichier", translate(res.code)); + capture(res.code); + setUploadError("Une erreur s'est produite lors du téléversement de votre fichier."); + } else { + toastr.success("Succès", "Fichier importé avec succès"); + const { data: base64Data, mimeType, fileName } = res; + const binaryData = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0)); + + const blob = new Blob([binaryData], { type: mimeType }); + const url = window.URL.createObjectURL(blob); + + const a = document.createElement("a"); + a.href = url; + a.download = fileName; + document.body.appendChild(a); + a.click(); + + a.remove(); + window.URL.revokeObjectURL(url); + } + } catch (err) { + setUploadError("La requête étant trop longue, nous vous avons envoyé un mail avec le fichier de résultat."); + } + setIsUploading(false); + } + + return ( + +
+ +
+
+

Mettre à jour les classes

+

Importez votre fichier (au format .xlsx jusqu’à 5Mo)

+ + {!isUploading ? ( + <> + + + {uploadError &&
{uploadError}
} + + ) : ( + <> +
+

Temps Estimé : 1 minute

+
+ + + )} +
+
+
+ + ); +} diff --git a/admin/src/scenes/classe/index.jsx b/admin/src/scenes/classe/index.jsx index 9c3c047d89..d5401826d7 100644 --- a/admin/src/scenes/classe/index.jsx +++ b/admin/src/scenes/classe/index.jsx @@ -6,6 +6,7 @@ import { SentryRoute } from "../../sentry"; import Create from "./create"; import List from "./list"; import View from "./view"; +import Import from "./Import"; import { toastr } from "react-redux-toastr"; import NotFound from "@/components/layout/NotFound"; @@ -15,6 +16,7 @@ export default function Index() { return ( + { diff --git a/admin/src/scenes/classe/list.tsx b/admin/src/scenes/classe/list.tsx index 7b24495eda..5ba0eba630 100644 --- a/admin/src/scenes/classe/list.tsx +++ b/admin/src/scenes/classe/list.tsx @@ -10,7 +10,7 @@ import { Filters, ResultTable, Save, SelectedFilters, SortOption } from "@/compo import { capture } from "@/sentry"; import api from "@/services/api"; import { Button, Container, Header, Page } from "@snu/ds/admin"; -import { ROLES, translateStatusClasse, translate, EtablissementType, ClasseType } from "snu-lib"; +import { ROLES, translateStatusClasse, translate, EtablissementType, ClasseType, isSuperAdmin } from "snu-lib"; import { orderCohort } from "../../components/filters-system-v2/components/filters/utils"; import { getCohortGroups } from "@/services/cohort.service"; @@ -142,12 +142,18 @@ export default function List() { [ROLES.ADMIN, ROLES.REFERENT_REGION].includes(user.role) && (