+ Send email
+
+ ),
+ },
+ {
+ key: "2",
+ name: "Doe",
+ firstname: "Jane",
+ button: ,
+ },
+ {
+ key: "3",
+ name: "Doe",
+ firstname: "Jack",
+ button: ,
+ },
+ ]}
+ />
+
+ Buttons
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ size={EButtonSize.SM}>
+ Primary SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Primary SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Primary SM
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ size={EButtonSize.MD}>
+ Primary MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Primary MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Primary MD
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ size={EButtonSize.LG}>
+ Primary LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Primary LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Primary LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ size={EButtonSize.SM}>
+ Secondary SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Secondary SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Secondary SM
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ size={EButtonSize.MD}>
+ Secondary MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Secondary MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Secondary MD
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ size={EButtonSize.LG}>
+ Secondary LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Secondary LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SECONDARY}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Secondary LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ size={EButtonSize.SM}>
+ Neutral SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Neutral SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Neutral SM
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ size={EButtonSize.MD}>
+ Neutral MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Neutral MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Neutral MD
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ size={EButtonSize.LG}>
+ Neutral LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Neutral LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.NEUTRAL}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Neutral LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ size={EButtonSize.SM}>
+ Error SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Error SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Error SM
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ size={EButtonSize.MD}>
+ Error MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Error MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Error MD
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ size={EButtonSize.LG}>
+ Error LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Error LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.ERROR}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Error LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ size={EButtonSize.SM}>
+ Warning SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Warning SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Warning SM
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ size={EButtonSize.MD}>
+ Warning MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Warning MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Warning MD
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ size={EButtonSize.LG}>
+ Warning LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Warning LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.WARNING}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Warning LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ size={EButtonSize.SM}>
+ Success SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Success SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Success SM
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ size={EButtonSize.MD}>
+ Success MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Success MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Success MD
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ size={EButtonSize.LG}>
+ Success LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Success LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.SUCCESS}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Success LG
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ size={EButtonSize.SM}>
+ Info SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.SM}>
+ Info SM
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.SM}>
+ Info SM
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ size={EButtonSize.MD}>
+ Info MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.MD}>
+ Info MD
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.MD}>
+ Info MD
+
+
+
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ size={EButtonSize.LG}>
+ Info LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.OUTLINED}
+ size={EButtonSize.LG}>
+ Info LG
+
+
}
+ rightIcon={
}
+ variant={EButtonVariant.INFO}
+ styletype={EButtonstyletype.TEXT}
+ size={EButtonSize.LG}>
+ Info LG
+
+
+
+ } variant={EIconButtonVariant.DEFAULT} />
+ } variant={EIconButtonVariant.NEUTRAL} />
+ } variant={EIconButtonVariant.PRIMARY} />
+ } variant={EIconButtonVariant.ERROR} />
+ } variant={EIconButtonVariant.SUCCESS} />
+ } variant={EIconButtonVariant.WARNING} />
+ } variant={EIconButtonVariant.INFO} />
+ } variant={EIconButtonVariant.INFO} disabled />
+
+
+ Alerts
+ ,
+ rightIcon: ,
+ }}
+ secondButton={{ children: "Button", leftIcon: , rightIcon: }}
+ variant={EAlertVariant.INFO}
+ closeButton
+ />
+ ,
+ rightIcon: ,
+ }}
+ secondButton={{ children: "Button", leftIcon: , rightIcon: }}
+ variant={EAlertVariant.ERROR}
+ />
+ ,
+ rightIcon: ,
+ }}
+ secondButton={{ children: "Button", leftIcon: , rightIcon: }}
+ variant={EAlertVariant.WARNING}
+ />
+ ,
+ rightIcon: ,
+ }}
+ secondButton={{ children: "Button", leftIcon: , rightIcon: }}
+ variant={EAlertVariant.SUCCESS}
+ />
+ ,
+ rightIcon: ,
+ }}
+ secondButton={{ children: "Button", leftIcon: , rightIcon: }}
+ variant={EAlertVariant.NEUTRAL}
+ />
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/DeleteCustomerModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/DeleteCustomerModal/index.tsx
new file mode 100644
index 00000000..bdf407d9
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/DeleteCustomerModal/index.tsx
@@ -0,0 +1,33 @@
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import React, { useCallback } from "react";
+
+type IProps = {
+ customerUid: string;
+ isOpen: boolean;
+ onClose?: () => void;
+
+ onDelete: (customerUid: string) => void;
+};
+
+export default function DeleteCustomerModal(props: IProps) {
+ const { isOpen, onClose, onDelete } = props;
+
+ const handleDelete = useCallback(() => {
+ onDelete(props.customerUid);
+ }, [onDelete, props.customerUid]);
+
+ return (
+
+
+ Cette action retirera le client de ce dossier. Vous ne pourrez plus récupérer les informations associées à ce client dans ce
+ dossier une fois supprimées.
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/classes.module.scss
new file mode 100644
index 00000000..5e75f6fa
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/classes.module.scss
@@ -0,0 +1,22 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ width: fit-content;
+ padding: var(--spacing-md, 16px);
+ flex-direction: column;
+ gap: var(--spacing-md, 16px);
+ background: var(--primary-weak-higlight, #e5eefa);
+ min-width: 300px;
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ }
+
+ .delete-button {
+ margin: auto;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/index.tsx
new file mode 100644
index 00000000..b667ad9c
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/index.tsx
@@ -0,0 +1,141 @@
+import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import useOpenable from "@Front/Hooks/useOpenable";
+import { PencilSquareIcon, TrashIcon, UsersIcon } from "@heroicons/react/24/outline";
+
+import { ICustomer } from "..";
+import { AnchorStatus } from "../..";
+import classes from "./classes.module.scss";
+import DeleteCustomerModal from "./DeleteCustomerModal";
+import Module from "@Front/Config/Module";
+import { useCallback } from "react";
+import { Note } from "le-coffre-resources/dist/Customer";
+import ButtonWithSubMenu from "@Front/Components/Elements/ButtonWithSubMenu";
+import Modal from "@Front/Components/DesignSystem/Modal";
+
+type IProps = {
+ customer: ICustomer;
+ anchorStatus: AnchorStatus;
+ folderUid: string | undefined;
+ customerNote: Note | null;
+ onDelete: (customerUid: string) => void;
+};
+
+export default function ClientBox(props: IProps) {
+ const { customer, anchorStatus, folderUid, customerNote } = props;
+
+ const { isOpen: isDeleteModalOpen, open: openDeleteModal, close: closeDeleteModal } = useOpenable();
+ const { isOpen: isErrorModalOpen, open: openErrorModal, close: closeErrorModal } = useOpenable();
+
+ const handleDelete = useCallback(
+ (customerUid: string) => {
+ if (customer.documents && customer.documents.length > 0) {
+ closeDeleteModal();
+ openErrorModal();
+ return;
+ }
+ props.onDelete(customerUid);
+ },
+ [closeDeleteModal, customer.documents, openErrorModal, props],
+ );
+
+ let createOrUpdateNotePath = Module.getInstance()
+ .get()
+ .modules.pages.Notes.pages.EditNote.props.path.replace("[noteUid]", customerNote?.uid ?? "");
+ if (!customerNote) {
+ createOrUpdateNotePath = Module.getInstance()
+ .get()
+ .modules.pages.Notes.pages.CreateNote.props.path.replace("[folderUid]", folderUid ?? "");
+ createOrUpdateNotePath = createOrUpdateNotePath.replace("[customerUid]", customer.uid ?? "");
+ }
+
+ return (
+
+
+
+ {customer.contact?.first_name} {customer.contact?.last_name}
+
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && (
+
}
+ openingSide="right"
+ subElements={[
+ {
+ icon:
,
+ text: "Modifier les informations",
+ link: Module.getInstance()
+ .get()
+ .modules.pages.Folder.pages.EditClient.props.path.replace("[folderUid]", folderUid ?? "")
+ .replace("[customerUid]", customer.uid ?? ""),
+ hasSeparator: true,
+ },
+ {
+ icon:
,
+ text: "Modifier la note",
+ link: createOrUpdateNotePath,
+ },
+ ]}
+ />
+ //
+ //
+ //
+ )}
+
+
+
+ Numéro de téléphone
+
+
+ {customer.contact?.cell_phone_number ?? customer.contact?.phone_number ?? "_"}
+
+
+
+
+ E-mail
+
+
+ {customer.contact?.email ?? "_"}
+
+
+
+
+ Note client
+
+
+ {customerNote?.content ?? "-"}
+
+
+
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && (
+ <>
+
}
+ onClick={openDeleteModal}>
+ Supprimer le client
+
+
+ >
+ )}
+
+
+ Ce client ne peut pas être supprimé car des documents sont associés à son dossier. Veuillez d'abord gérer ou supprimer
+ ces documents avant de tenter de supprimer le client.
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/DeleteAskedDocumentModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/DeleteAskedDocumentModal/index.tsx
new file mode 100644
index 00000000..901780d9
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/DeleteAskedDocumentModal/index.tsx
@@ -0,0 +1,37 @@
+import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import React, { useCallback } from "react";
+
+type IProps = {
+ documentUid: string;
+ isOpen: boolean;
+ onClose?: () => void;
+
+ onDeleteSuccess: (uid: string) => void;
+};
+
+export default function DeleteAskedDocumentModal(props: IProps) {
+ const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
+
+ const onDelete = useCallback(
+ () =>
+ Documents.getInstance()
+ .delete(documentUid)
+ .then(() => onDeleteSuccess(documentUid))
+ .then(onClose)
+ .catch((error) => console.warn(error)),
+ [documentUid, onClose, onDeleteSuccess],
+ );
+
+ return (
+
+ Cette action annulera la demande du document en cours.
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/FilePreviewModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/FilePreviewModal/index.tsx
new file mode 100644
index 00000000..6ef96822
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/FilePreviewModal/index.tsx
@@ -0,0 +1,20 @@
+import Modal from "@Front/Components/DesignSystem/Modal";
+import { File } from "le-coffre-resources/dist/Customer";
+import React from "react";
+
+type IProps = {
+ file: File;
+ url: string;
+ isOpen: boolean;
+ onClose?: () => void;
+};
+
+export default function FilePreviewModal(props: IProps) {
+ const { isOpen, onClose, file, url } = props;
+
+ return (
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/classes.module.scss
new file mode 100644
index 00000000..1e85e972
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/classes.module.scss
@@ -0,0 +1,14 @@
+@import "@Themes/constants.scss";
+
+.root {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+
+ .title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/index.tsx
new file mode 100644
index 00000000..8d85e9f2
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/index.tsx
@@ -0,0 +1,226 @@
+import Files from "@Front/Api/LeCoffreApi/Notary/Files/Files";
+import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
+import IconButton from "@Front/Components/DesignSystem/IconButton";
+import Table from "@Front/Components/DesignSystem/Table";
+import { IHead, IRowProps } from "@Front/Components/DesignSystem/Table/MuiTable";
+import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import Module from "@Front/Config/Module";
+import useOpenable from "@Front/Hooks/useOpenable";
+import { ArrowDownTrayIcon, EyeIcon, TrashIcon } from "@heroicons/react/24/outline";
+import { Document } from "le-coffre-resources/dist/Customer";
+import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
+import Link from "next/link";
+import { useCallback, useEffect, useMemo, useState } from "react";
+
+import classes from "./classes.module.scss";
+import DeleteAskedDocumentModal from "./DeleteAskedDocumentModal";
+
+type IProps = {
+ documents: Document[];
+ folderUid: string;
+};
+
+const header: readonly IHead[] = [
+ {
+ key: "document_type",
+ title: "Type de document",
+ },
+ {
+ key: "document_status",
+ title: "Statut",
+ },
+ {
+ key: "created_at",
+ title: "Demandé le",
+ },
+ {
+ key: "actions",
+ title: "Actions",
+ },
+];
+
+const tradDocumentStatus: Record = {
+ [EDocumentStatus.ASKED]: "Demandé",
+ [EDocumentStatus.DEPOSITED]: "À valider",
+ [EDocumentStatus.VALIDATED]: "Validé",
+ [EDocumentStatus.REFUSED]: "Refusé",
+};
+
+export default function DocumentTables(props: IProps) {
+ const { documents: documentsProps, folderUid } = props;
+ const [documents, setDocuments] = useState(documentsProps);
+ const [documentUid, setDocumentUid] = useState(null);
+
+ const deleteAskedOocumentModal = useOpenable();
+
+ useEffect(() => {
+ setDocuments(documentsProps);
+ }, [documentsProps]);
+
+ const openDeleteAskedDocumentModal = useCallback(
+ (uid: string | undefined) => {
+ if (!uid) return;
+ setDocumentUid(uid);
+ deleteAskedOocumentModal.open();
+ },
+ [deleteAskedOocumentModal],
+ );
+
+ const onDownload = useCallback((doc: Document) => {
+ const file = doc.files?.[0];
+ if (!file || !file?.uid) return;
+
+ return Files.getInstance()
+ .download(file.uid)
+ .then((blob) => {
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = file.file_name ?? "file";
+ a.click();
+ URL.revokeObjectURL(url);
+ })
+ .catch((e) => console.warn(e));
+ }, []);
+
+ const askedDocuments: IRowProps[] = useMemo(
+ () =>
+ documents
+ .map((document) => {
+ if (document.document_status !== EDocumentStatus.ASKED) return null;
+ return {
+ key: document.uid,
+ document_type: document.document_type?.name ?? "_",
+ document_status: (
+
+ ),
+ created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
+ actions: openDeleteAskedDocumentModal(document.uid)} />} />,
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents, openDeleteAskedDocumentModal],
+ );
+
+ const toValidateDocuments: IRowProps[] = useMemo(
+ () =>
+ documents
+ .map((document) => {
+ if (document.document_status !== EDocumentStatus.DEPOSITED) return null;
+ return {
+ key: document.uid,
+ document_type: document.document_type?.name ?? "_",
+ document_status: (
+
+ ),
+ created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
+ actions: (
+
+ } />
+
+ ),
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents, folderUid],
+ );
+
+ const validatedDocuments: IRowProps[] = useMemo(
+ () =>
+ documents
+ .map((document) => {
+ if (document.document_status !== EDocumentStatus.VALIDATED) return null;
+ return {
+ key: document.uid,
+ document_type: document.document_type?.name ?? "_",
+ document_status: (
+
+ ),
+ created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
+ actions: (
+
+
+
} />
+
+
onDownload(document)} icon={ } />
+
+ ),
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents, folderUid, onDownload],
+ );
+
+ const refusedDocuments: IRowProps[] = useMemo(
+ () =>
+ documents
+ .map((document) => {
+ if (document.document_status !== EDocumentStatus.REFUSED) return null;
+ return {
+ key: document.uid,
+ document_type: document.document_type?.name ?? "_",
+ document_status: (
+
+ ),
+ created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
+ actions: "",
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents],
+ );
+
+ const progress = useMemo(() => {
+ const total = askedDocuments.length + toValidateDocuments.length + validatedDocuments.length + refusedDocuments.length;
+ if (total === 0) return 0;
+ return (validatedDocuments.length / total) * 100;
+ }, [askedDocuments.length, refusedDocuments.length, toValidateDocuments.length, validatedDocuments.length]);
+
+ return (
+
+
+
+ Documents
+
+
+
+ {askedDocuments.length > 0 &&
}
+ {toValidateDocuments.length > 0 &&
}
+ {validatedDocuments.length > 0 &&
}
+ {refusedDocuments.length > 0 &&
}
+ {documentUid && (
+
setDocuments(documents.filter((document) => document.uid !== uid))}
+ documentUid={documentUid}
+ />
+ )}
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/classes.module.scss
new file mode 100644
index 00000000..0594b1b7
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/classes.module.scss
@@ -0,0 +1,7 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/index.tsx
new file mode 100644
index 00000000..49de66dc
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/NoDocument/index.tsx
@@ -0,0 +1,20 @@
+import EmptyAlert from "@Front/Components/DesignSystem/EmptyAlert";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import { DocumentIcon } from "@heroicons/react/24/outline";
+
+import classes from "./classes.module.scss";
+
+export default function NoDocument() {
+ return (
+
+
+ Documents
+
+ }
+ title="Aucune demande de document"
+ description="Vous n'avez encore demandé aucun document pour ce client. Pour commencer, cliquez sur le bouton ci-dessous pour créer une nouvelle demande de document."
+ />
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/classes.module.scss
new file mode 100644
index 00000000..673d2297
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/classes.module.scss
@@ -0,0 +1,30 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+
+ .tab-container {
+ display: flex;
+ gap: var(--spacing-md, 16px);
+ justify-content: space-between;
+ align-items: center;
+
+ .tabs {
+ width: calc(100% - 210px);
+ }
+ }
+
+ .content {
+ display: flex;
+ gap: var(--spacing-lg, 24px);
+
+ .client-box {
+ display: flex;
+ flex-direction: column;
+
+ gap: var(--spacing-lg, 24px);
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientView/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/index.tsx
new file mode 100644
index 00000000..cce182f9
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/index.tsx
@@ -0,0 +1,115 @@
+import Tabs from "@Front/Components/Elements/Tabs";
+import Customer from "le-coffre-resources/dist/Customer";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+
+import { useCallback, useMemo, useState } from "react";
+
+import { AnchorStatus } from "..";
+import classes from "./classes.module.scss";
+import ClientBox from "./ClientBox";
+import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { DocumentIcon, UserPlusIcon } from "@heroicons/react/24/outline";
+import Module from "@Front/Config/Module";
+import Link from "next/link";
+import NoDocument from "./NoDocument";
+import DocumentTables from "./DocumentTables";
+import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
+
+type IProps = {
+ folder: OfficeFolder;
+ anchorStatus: AnchorStatus;
+};
+
+export type ICustomer = Customer & { id: string };
+
+export default function ClientView(props: IProps) {
+ const { folder, anchorStatus } = props;
+
+ const customers: ICustomer[] = useMemo(
+ () =>
+ folder?.customers?.map((customer) => ({
+ id: customer.uid ?? "",
+ ...customer,
+ })) ?? [],
+ [folder],
+ );
+
+ const [customer, setCustomer] = useState<(typeof customers)[number]>(customers[0]!);
+
+ const tabs = useMemo(
+ () =>
+ customers.map((customer) => ({
+ label: `${customer.contact?.first_name} ${customer.contact?.last_name}`,
+ key: customer.uid,
+ value: customer,
+ })),
+ [customers],
+ );
+
+ const doesCustomerHaveDocument = useMemo(() => customer.documents && customer.documents.length > 0, [customer]);
+
+ const handleClientDelete = useCallback(
+ (customerUid: string) => {
+ if (!folder.uid) return;
+ Folders.getInstance().put(
+ folder.uid,
+ OfficeFolder.hydrate({
+ ...folder,
+ customers: folder.customers?.filter((customer) => customer.uid !== customerUid),
+ }),
+ );
+ window.location.reload();
+ },
+ [folder],
+ );
+
+ return (
+
+
+
{tabs && tabs={tabs} onSelect={setCustomer} />}
+
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && (
+
+
}
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.TEXT}>
+ Ajouter un client
+
+
+ )}
+
+
+
+ value.customer?.uid === customer.uid) ?? null}
+ />
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && (
+
+ } variant={EButtonVariant.PRIMARY} fullwidth>
+ Demander un document
+
+
+ )}
+
+ {doesCustomerHaveDocument ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/classes.module.scss
new file mode 100644
index 00000000..dd4839c0
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/classes.module.scss
@@ -0,0 +1,50 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ gap: var(--spacing-lg, 40px);
+ .info-box1 {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ gap: var(--spacing-sm, 8px);
+
+ .open-date {
+ display: flex;
+ gap: var(--spacing-sm, 8px);
+ }
+ }
+
+ .info-box2 {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-lg, 24px);
+ width: 100%;
+ max-width: 400px;
+
+ .progress-container {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .icon-container {
+ display: flex;
+ gap: var(--spacing-md, 8px);
+ }
+ }
+
+ .description-container {
+ .text {
+ max-height: 60px;
+ overflow-y: auto;
+ }
+ }
+ }
+
+ .separator {
+ background-color: var(--separator-stroke-light);
+ width: 1px;
+ align-self: stretch;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/index.tsx
new file mode 100644
index 00000000..4e60f216
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/index.tsx
@@ -0,0 +1,103 @@
+import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
+import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import Module from "@Front/Config/Module";
+import { ArchiveBoxIcon, EllipsisHorizontalIcon, PencilSquareIcon, UsersIcon } from "@heroicons/react/24/outline";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+
+import classes from "./classes.module.scss";
+import { AnchorStatus } from "..";
+import ButtonWithSubMenu, { ISubElement } from "@Front/Components/Elements/ButtonWithSubMenu";
+import { useCallback } from "react";
+
+type IProps = {
+ folder: OfficeFolder | null;
+ progress: number;
+ onArchive: () => void;
+ anchorStatus: AnchorStatus;
+ isArchived: boolean;
+};
+
+export default function InformationSection(props: IProps) {
+ const { folder, progress, onArchive, anchorStatus, isArchived } = props;
+
+ const getSubMenuElement = useCallback(() => {
+ let elements: ISubElement[] = [];
+
+ // Creating the three elements and adding them conditionnally
+ const modifyCollaboratorsElement = {
+ icon: ,
+ text: "Modifier les collaborateurs",
+ link: Module.getInstance()
+ .get()
+ .modules.pages.Folder.pages.EditCollaborators.props.path.replace("[folderUid]", folder?.uid ?? ""),
+ hasSeparator: true,
+ };
+ const modifyInformationsElement = {
+ icon: ,
+ text: "Modifier les informations du dossier",
+ link: Module.getInstance()
+ .get()
+ .modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
+ hasSeparator: true,
+ };
+
+ const archiveElement = {
+ icon: ,
+ text: "Archiver le dossier",
+ onClick: onArchive,
+ color: ETypoColor.ERROR_WEAK_CONTRAST,
+ };
+
+ // If the folder is not anchored, we can modify the collaborators and the informations
+ if (anchorStatus === AnchorStatus.NOT_ANCHORED) {
+ elements.push(modifyCollaboratorsElement);
+ // Remove the separator if it's the last item (if the folder is not archived)
+ if (isArchived) modifyInformationsElement.hasSeparator = false;
+
+ elements.push(modifyInformationsElement);
+ }
+
+ // If the folder is not archived, we can archive it
+ if (!isArchived) {
+ elements.push(archiveElement);
+ }
+ return elements;
+ }, [anchorStatus, folder?.uid, isArchived, onArchive]);
+ return (
+
+
+
+ {folder?.folder_number}
+ {folder?.name}
+
+
+
+
+ Ouverture du dossier
+
+ {folder?.created_at ? new Date(folder.created_at).toLocaleDateString() : ""}
+
+
+
+
+
+
+
+
+
+ } subElements={getSubMenuElement()} />
+
+
+
+
+ Note du dossier
+
+
+ {folder?.description}
+
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx
new file mode 100644
index 00000000..09421703
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx
@@ -0,0 +1,34 @@
+import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import EmptyAlert from "@Front/Components/DesignSystem/EmptyAlert";
+import Module from "@Front/Config/Module";
+import { UserPlusIcon } from "@heroicons/react/24/outline";
+import Link from "next/link";
+import React, { useMemo } from "react";
+
+type IProps = {
+ folderUid: string;
+};
+
+export default function AddClientSection(props: IProps) {
+ const { folderUid } = props;
+
+ const addClientPath = useMemo(() => {
+ if (!folderUid) return "";
+ return Module.getInstance().get().modules.pages.Folder.pages.AddClient.props.path.replace("[folderUid]", folderUid);
+ }, [folderUid]);
+
+ return (
+ }
+ title="Ajouter des clients au dossier"
+ description="Pour pouvoir faire une demande de document, vous devez d'abord ajouter un ou plusieurs clients à ce dossier. Cette étape est essentielle pour assurer le suivi et la gestion des documents."
+ footer={
+
+
+ Ajouter un client
+
+
+ }
+ />
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/DeleteFolderModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/DeleteFolderModal/index.tsx
new file mode 100644
index 00000000..814f5d56
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/DeleteFolderModal/index.tsx
@@ -0,0 +1,42 @@
+import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import Module from "@Front/Config/Module";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import { useRouter } from "next/router";
+import React, { useCallback } from "react";
+
+type IProps = {
+ isOpen: boolean;
+ onClose?: () => void;
+ folder: OfficeFolder;
+};
+
+export default function DeleteFolderModal(props: IProps) {
+ const { isOpen, onClose, folder } = props;
+ const router = useRouter();
+
+ const onDelete = useCallback(() => {
+ if (!folder.uid) return;
+ if ((folder?.customers?.length ?? 0) > 0 || (folder?.documents?.length ?? 0) > 0)
+ return console.warn("Cannot delete folder with customers or documents");
+
+ return Folders.getInstance()
+ .delete(folder.uid)
+ .then(() => router.push(Module.getInstance().get().modules.pages.Folder.props.path))
+ .then(onClose);
+ }, [folder, router, onClose]);
+
+ return (
+
+
+ Cette action est irréversible. En supprimant ce dossier, toutes les informations associées seront définitivement perdues.
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/classes.module.scss
new file mode 100644
index 00000000..9c8a3edd
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/classes.module.scss
@@ -0,0 +1,11 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+
+ .delete-button {
+ align-self: flex-end;
+ }
+}
\ No newline at end of file
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/index.tsx
new file mode 100644
index 00000000..3eb99a8d
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/index.tsx
@@ -0,0 +1,42 @@
+import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { TrashIcon } from "@heroicons/react/24/outline";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import { useMemo } from "react";
+
+import AddClientSection from "./AddClientSection";
+import classes from "./classes.module.scss";
+import DeleteFolderModal from "./DeleteFolderModal";
+import useOpenable from "@Front/Hooks/useOpenable";
+import { AnchorStatus } from "..";
+
+type IProps = {
+ folder: OfficeFolder;
+ anchorStatus: AnchorStatus;
+};
+
+export default function NoClientView(props: IProps) {
+ const { folder, anchorStatus } = props;
+
+ const deleteFolderModal = useOpenable();
+ const canDeleteFolder = useMemo(() => folder.documents?.length === 0 && folder.customers?.length === 0, [folder]);
+
+ return (
+
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && }
+ {canDeleteFolder && (
+ <>
+ }
+ onClick={deleteFolderModal.open}
+ size={EButtonSize.SM}>
+ Supprimer le dossier
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertInfo/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertInfo/index.tsx
new file mode 100644
index 00000000..bbdf1378
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertInfo/index.tsx
@@ -0,0 +1,25 @@
+import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
+import { EButtonstyletype } from "@Front/Components/DesignSystem/Button";
+import { LockClosedIcon } from "@heroicons/react/24/outline";
+
+type IProps = {
+ onAnchor: () => void;
+};
+
+export default function AnchoringAlertInfo(props: IProps) {
+ const { onAnchor } = props;
+ return (
+ ,
+ onClick: onAnchor,
+ }}
+ variant={EAlertVariant.INFO}
+ fullWidth
+ />
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertSuccess/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertSuccess/index.tsx
new file mode 100644
index 00000000..0c5ae96f
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringAlertSuccess/index.tsx
@@ -0,0 +1,36 @@
+import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
+import { EButtonstyletype } from "@Front/Components/DesignSystem/Button";
+import { ArrowDownOnSquareIcon, CheckIcon } from "@heroicons/react/24/outline";
+
+type IProps = {
+ onDownloadAnchoringProof: () => void;
+ onArchive: () => void;
+ isArchived: boolean;
+};
+
+export default function AnchoringAlertSuccess(props: IProps) {
+ const { onDownloadAnchoringProof, onArchive, isArchived } = props;
+ return (
+ ,
+ onClick: onDownloadAnchoringProof,
+ }}
+ secondButton={
+ isArchived
+ ? undefined
+ : {
+ children: "Archiver le dossier",
+ onClick: onArchive,
+ }
+ }
+ variant={EAlertVariant.SUCCESS}
+ icon={ }
+ fullWidth
+ />
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/classes.module.scss
new file mode 100644
index 00000000..f0139309
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/classes.module.scss
@@ -0,0 +1,11 @@
+.anchoring {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+
+ .validate-gif {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/index.tsx
new file mode 100644
index 00000000..071ddc34
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringModal/index.tsx
@@ -0,0 +1,62 @@
+import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
+import ValidateAnchoringGif from "@Front/Assets/images/validate_anchoring.gif";
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import Image from "next/image";
+import React, { useCallback, useState } from "react";
+
+import classes from "./classes.module.scss";
+
+type IProps = {
+ isOpen: boolean;
+ onClose?: () => void;
+ folderUid: string;
+ onAnchorSuccess: () => void;
+};
+
+export default function AnchoringModal(props: IProps) {
+ const { isOpen, onClose, folderUid, onAnchorSuccess } = props;
+ const [isAnchoring, setIsAnchoring] = useState(false);
+
+ const anchor = useCallback(() => {
+ const timeoutDelay = 9800;
+ const timeoutPromise = new Promise((resolve) => {
+ setTimeout(resolve, timeoutDelay);
+ });
+ setIsAnchoring(true);
+ return OfficeFolderAnchors.getInstance()
+ .post(folderUid)
+ .then(() => timeoutPromise)
+ .then(() => setIsAnchoring(false))
+ .then(onAnchorSuccess)
+ .then(onClose)
+ .catch((e) => {
+ console.warn(e);
+ setIsAnchoring(false);
+ });
+ }, [folderUid, onAnchorSuccess, onClose]);
+
+ return (
+
+ {!isAnchoring ? (
+
+ La certification et l'ancrage de ce dossier dans la blockchain sont des actions définitives et garantiront la sécurité
+ et l'authenticité de tous les documents. Veuillez confirmer que vous souhaitez continuer.
+
+ ) : (
+
+
+ Vos documents sont en train d'être ancrés dans la blockchain. Cela peut prendre quelques instants. Merci de votre
+ patience.
+
+
+
+ )}
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringProcessingInfo/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringProcessingInfo/index.tsx
new file mode 100644
index 00000000..599d4173
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/AnchoringProcessingInfo/index.tsx
@@ -0,0 +1,14 @@
+import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
+import { ArrowPathIcon } from "@heroicons/react/24/outline";
+
+export default function AnchoringProcessingInfo() {
+ return (
+ }
+ fullWidth
+ />
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveAlertWarning/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveAlertWarning/index.tsx
new file mode 100644
index 00000000..89828c08
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveAlertWarning/index.tsx
@@ -0,0 +1,48 @@
+import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
+import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
+import { EButtonstyletype } from "@Front/Components/DesignSystem/Button";
+import Module from "@Front/Config/Module";
+import { ArchiveBoxArrowDownIcon, ArchiveBoxIcon, ArrowDownOnSquareIcon } from "@heroicons/react/24/outline";
+import { useRouter } from "next/router";
+import { useCallback } from "react";
+
+type IProps = {
+ onDownloadAnchoringProof: () => void;
+ folderUid: string;
+};
+
+export default function ArchiveAlertWarning(props: IProps) {
+ const { onDownloadAnchoringProof, folderUid } = props;
+
+ const router = useRouter();
+
+ const restoreArchive = useCallback(() => {
+ Folders.getInstance()
+ .restore(folderUid)
+ .then(() => router.push(Module.getInstance().get().modules.pages.Folder.props.path))
+ .catch((e) => {
+ console.warn(e);
+ });
+ }, [folderUid, router]);
+
+ return (
+ ,
+ onClick: onDownloadAnchoringProof,
+ }}
+ secondButton={{
+ children: "Restaurer le dossier",
+ onClick: restoreArchive,
+ rightIcon: ,
+ }}
+ variant={EAlertVariant.WARNING}
+ icon={ }
+ fullWidth
+ />
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/classes.module.scss
new file mode 100644
index 00000000..455f1de2
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/classes.module.scss
@@ -0,0 +1,5 @@
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-lg);
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/index.tsx
new file mode 100644
index 00000000..177ddf8e
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/ArchiveModal/index.tsx
@@ -0,0 +1,48 @@
+import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
+import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import Module from "@Front/Config/Module";
+import { useRouter } from "next/router";
+import React, { useCallback } from "react";
+import classes from "./classes.module.scss";
+
+type IProps = {
+ isOpen: boolean;
+ onClose?: () => void;
+ folderUid: string;
+};
+
+export default function ArchiveModal(props: IProps) {
+ const { isOpen, onClose, folderUid } = props;
+ const router = useRouter();
+
+ const archive = useCallback(() => {
+ const description = (document.querySelector("textarea[name='archived_description']") as HTMLTextAreaElement).value ?? "";
+
+ Folders.getInstance()
+ .archive(folderUid, description)
+ .then(onClose)
+ .then(() => router.push(Module.getInstance().get().modules.pages.Folder.props.path))
+ .catch((e) => {
+ console.warn(e);
+ });
+ }, [folderUid, onClose, router]);
+
+ return (
+
+
+
+ Archiver ce dossier le déplacera dans la section des dossiers archivés. Vous pouvez ajouter une note de dossier avant
+ d'archiver si vous le souhaitez.
+
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/DownloadAnchoringProofModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/DownloadAnchoringProofModal/index.tsx
new file mode 100644
index 00000000..910076a4
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/DownloadAnchoringProofModal/index.tsx
@@ -0,0 +1,47 @@
+import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import React, { useCallback } from "react";
+
+type IProps = {
+ isOpen: boolean;
+ onClose?: () => void;
+ folder: OfficeFolder;
+};
+
+export default function DownloadAnchoringProofModal(props: IProps) {
+ const { isOpen, onClose, folder } = props;
+
+ const downloadAnchoringProof = useCallback(async () => {
+ if (!folder?.uid) return;
+ try {
+ const file = await OfficeFolderAnchors.getInstance().download(folder.uid);
+ const url = window.URL.createObjectURL(file);
+ const a = document.createElement("a");
+ a.style.display = "none";
+ a.href = url;
+ a.download = `anchoring_proof_${folder?.folder_number}_${folder?.name}.zip`;
+ document.body.appendChild(a);
+ a.click();
+ window.URL.revokeObjectURL(url);
+ onClose?.();
+ } catch (e) {
+ console.warn(e);
+ }
+ }, [folder?.folder_number, folder?.name, folder.uid, onClose]);
+
+ return (
+
+
+ Votre dossier a été validé et ancré dans la blockchain. Vous pouvez maintenant télécharger la preuve d'ancrage pour vos
+ archives.
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/classes.module.scss
new file mode 100644
index 00000000..f0139309
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/classes.module.scss
@@ -0,0 +1,11 @@
+.anchoring {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+
+ .validate-gif {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/index.tsx
new file mode 100644
index 00000000..d6273927
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/elements/RequireAnchoringModal/index.tsx
@@ -0,0 +1,32 @@
+import Modal from "@Front/Components/DesignSystem/Modal";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import React, { useCallback } from "react";
+
+type IProps = {
+ isOpen: boolean;
+ onClose: () => void;
+ onAnchor: () => void;
+};
+
+export default function RequireAnchoringModal(props: IProps) {
+ const { isOpen, onClose, onAnchor: onAnchorProps } = props;
+
+ const onAnchor = useCallback(() => {
+ onAnchorProps();
+ onClose();
+ }, [onAnchorProps, onClose]);
+
+ return (
+
+
+ Pour archiver ce dossier, il est nécessaire de l'ancrer dans la blockchain afin de garantir la sécurité et l'authenticité
+ des documents. Veuillez procéder à l'ancrage avant de continuer.
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/PasswordForgotten/classes.module.scss b/src/front/Components/Layouts/Login/PasswordForgotten/classes.module.scss
new file mode 100644
index 00000000..e0a34824
--- /dev/null
+++ b/src/front/Components/Layouts/Login/PasswordForgotten/classes.module.scss
@@ -0,0 +1,29 @@
+@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;
+ }
+ }
+
+ .form {
+ margin-top: 32px;
+
+ .password_indication {
+ margin-top: 8px;
+ margin-bottom: 24px;
+ }
+ .submit_button {
+ margin-top: 32px;
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Login/PasswordForgotten/index.tsx b/src/front/Components/Layouts/Login/PasswordForgotten/index.tsx
new file mode 100644
index 00000000..074ee9ee
--- /dev/null
+++ b/src/front/Components/Layouts/Login/PasswordForgotten/index.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import classes from "./classes.module.scss";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import Form from "@Front/Components/DesignSystem/Form";
+import TextField from "@Front/Components/DesignSystem/Form/TextField";
+import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { ValidationError } from "class-validator";
+type IProps = {
+ onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void;
+ validationErrors: ValidationError[];
+};
+
+export default function PasswordForgotten(props: IProps) {
+ const { onSubmit, validationErrors } = props;
+
+ return (
+
+
+ Réinitialisez votre mot de passe
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/StepEmail/classes.module.scss b/src/front/Components/Layouts/Login/StepEmail/classes.module.scss
new file mode 100644
index 00000000..2826c0af
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepEmail/classes.module.scss
@@ -0,0 +1,32 @@
+.root {
+ width: 472px;
+ margin: auto;
+ margin-top: 80px;
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+
+ .header {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-sm, 8px);
+ }
+
+ .content {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl, 32px);
+
+ .section {
+ .section-title {
+ margin-bottom: var(--spacing-xl, 32px);
+ }
+
+ > form {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-md, 16px);
+ }
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Login/StepEmail/index.tsx b/src/front/Components/Layouts/Login/StepEmail/index.tsx
new file mode 100644
index 00000000..68d351d2
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepEmail/index.tsx
@@ -0,0 +1,194 @@
+import React, { useCallback, useEffect, useState } from "react";
+import classes from "./classes.module.scss";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+//import Image from "next/image";
+import Form from "@Front/Components/DesignSystem/Form";
+import TextField from "@Front/Components/DesignSystem/Form/TextField";
+import Button from "@Front/Components/DesignSystem/Button";
+//import franceConnectLogo from "../france-connect.svg";
+// import { useRouter } from "next/router";
+// import Customers from "@Front/Api/Auth/Id360/Customers/Customers";
+import { ValidationError } from "class-validator";
+import Image from "next/image";
+import LogoSmallBlue from "@Assets/logo_small_blue.svg";
+
+import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
+import { useRouter } from "next/router";
+import { FrontendVariables } from "@Front/Config/VariablesFront";
+import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
+
+type IProps = {
+ onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void;
+ validationErrors: ValidationError[];
+};
+
+export default function StepEmail(props: IProps) {
+ const { onSubmit, validationErrors } = props;
+ const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
+ /* const router = useRouter();
+ const redirectCustomerOnConnection = useCallback(() => {
+ async function getCustomer() {
+ try {
+ const loginRes = await Customers.getInstance().login();
+ router.push(loginRes.enrollment.franceConnectUrl);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ getCustomer();
+ }, [router]); */
+
+ const router = useRouter();
+ const error = router.query["error"];
+ const redirectUserOnConnection = useCallback(() => {
+ const variables = FrontendVariables.getInstance();
+ router.push(
+ `${variables.IDNOT_BASE_URL + variables.IDNOT_AUTHORIZE_ENDPOINT}?client_id=${variables.IDNOT_CLIENT_ID}&redirect_uri=${
+ variables.FRONT_APP_HOST
+ }/authorized-client&scope=openid,profile&response_type=code`,
+ );
+ }, [router]);
+
+ const openErrorModal = useCallback((index: number) => {
+ setIsErrorModalOpen(index);
+ }, []);
+
+ const closeErrorModal = useCallback(() => {
+ setIsErrorModalOpen(0);
+ }, []);
+
+ const closeNoEmailModal = useCallback(() => {
+ setIsErrorModalOpen(0);
+ router.push("https://connexion.idnot.fr/");
+ }, [router]);
+
+ const closeContactAdminModal = () => {
+ setIsErrorModalOpen(0);
+ window.open("https://www.lecoffre.io/contact", "_blank");
+ };
+
+ useEffect(() => {
+ openErrorModal(parseInt(error as string));
+ }, [error, openErrorModal]);
+
+ return (
+
+
+
+
+ Bienvenue !
+
+ Connectez-vous pour accéder à votre espace sécurisé.
+
+
+
+
+ Pour les notaires :
+
+ }>
+ S'identifier avec ID.not
+
+
+
+
+ Pour les clients :
+
+
+
+
+
+
+
+ Vous ne disposez pas d'un abonnement, veuillez contacter l'administrateur de votre office.
+
+
+
+
+
+
+ Veuillez vous reconnecter.
+
+
+
+
+
+
+ Votre compte ID.not doit être associé à une adresse email @notaires.fr (onglet Mettre à jour mes données
+ professionnelles)
+
+
+
+
+
+
+ L'accès à la version bêta de lecoffre.io est limité à un groupe restreint d'utilisateurs autorisés.
+
+
+
+
+ Si vous êtes intéressé par la participation à notre programme de bêta-test, veuillez nous compléter le
+ formulaire :{" "}
+
+ https://www.lecoffre.io/contact
+
+
+
+
+
+
+ Si vous avez déjà un compte bêta-testeur, veuillez vous connecter sur{" "}
+
+ https://compte.idnot.fr/home
+ {" "}
+ et vérifier que l'adresse mail renseignée sur votre espace est identique à celle que vous nous avez
+ communiquée.
+
+
+
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/StepNewPassword/classes.module.scss b/src/front/Components/Layouts/Login/StepNewPassword/classes.module.scss
new file mode 100644
index 00000000..e0a34824
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepNewPassword/classes.module.scss
@@ -0,0 +1,29 @@
+@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;
+ }
+ }
+
+ .form {
+ margin-top: 32px;
+
+ .password_indication {
+ margin-top: 8px;
+ margin-bottom: 24px;
+ }
+ .submit_button {
+ margin-top: 32px;
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Login/StepNewPassword/index.tsx b/src/front/Components/Layouts/Login/StepNewPassword/index.tsx
new file mode 100644
index 00000000..94ba48c3
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepNewPassword/index.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import classes from "./classes.module.scss";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import Form from "@Front/Components/DesignSystem/Form";
+import TextField from "@Front/Components/DesignSystem/Form/TextField";
+import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { ValidationError } from "class-validator";
+type IProps = {
+ onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void;
+ validationErrors: ValidationError[];
+};
+
+export default function StepNewPassword(props: IProps) {
+ const { onSubmit, validationErrors } = props;
+
+ return (
+
+
+ Configurez votre mot de passe
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/StepPassword/classes.module.scss b/src/front/Components/Layouts/Login/StepPassword/classes.module.scss
new file mode 100644
index 00000000..0244c442
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepPassword/classes.module.scss
@@ -0,0 +1,30 @@
+@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;
+ }
+ }
+
+ .form {
+ margin-top: 32px;
+ .submit_button {
+ margin-top: 32px;
+ }
+
+ .forgot-password {
+ margin-top: 8px;
+ text-decoration: underline;
+ cursor: pointer;
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Login/StepPassword/index.tsx b/src/front/Components/Layouts/Login/StepPassword/index.tsx
new file mode 100644
index 00000000..f05511a9
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepPassword/index.tsx
@@ -0,0 +1,70 @@
+import React from "react";
+import classes from "./classes.module.scss";
+import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
+import Form from "@Front/Components/DesignSystem/Form";
+import TextField from "@Front/Components/DesignSystem/Form/TextField";
+import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { ValidationError } from "class-validator";
+import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
+type IProps = {
+ onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void;
+ validationErrors: ValidationError[];
+ onPasswordForgotClicked: () => void;
+};
+
+export default function StepPassword(props: IProps) {
+ const { onSubmit, validationErrors, onPasswordForgotClicked } = props;
+ const [isModalOpened, setIsModalOpened] = React.useState(false);
+
+ const closeModal = () => {
+ setIsModalOpened(false);
+ };
+
+ const openModal = () => {
+ setIsModalOpened(true);
+ };
+
+ const onModalAccept = () => {
+ onPasswordForgotClicked();
+ setIsModalOpened(false);
+ };
+
+ return (
+
+
+ Entrez votre mot de passe
+
+
+
+
+
+ Un code à usage unique va vous être envoyé par sms pour réinitialiser votre mot de passe.
+
+
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/StepTotp/classes.module.scss b/src/front/Components/Layouts/Login/StepTotp/classes.module.scss
new file mode 100644
index 00000000..fabaa162
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepTotp/classes.module.scss
@@ -0,0 +1,43 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ flex-direction: column;
+ max-width: 530px;
+ margin: 220px auto;
+
+ .title {
+ text-align: left;
+
+ @media (max-width: $screen-s) {
+ font-family: 48px;
+ }
+ }
+
+ .form {
+ margin-top: 32px;
+ .submit_button {
+ margin-top: 32px;
+ }
+ }
+
+ .ask-another-code {
+ margin-top: 48px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ align-items: flex-start;
+
+ .new-code-button {
+ &[data-disabled="true"] {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+ }
+ .new-code-timer {
+ display: flex;
+ gap: 6px;
+ align-items: center;
+ }
+ }
+}
diff --git a/src/front/Components/Layouts/Login/StepTotp/index.tsx b/src/front/Components/Layouts/Login/StepTotp/index.tsx
new file mode 100644
index 00000000..d954ef82
--- /dev/null
+++ b/src/front/Components/Layouts/Login/StepTotp/index.tsx
@@ -0,0 +1,77 @@
+import React, { useEffect } from "react";
+import classes from "./classes.module.scss";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import Form from "@Front/Components/DesignSystem/Form";
+import TextField from "@Front/Components/DesignSystem/Form/TextField";
+import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import { ValidationError } from "class-validator";
+type IProps = {
+ onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void;
+ validationErrors: ValidationError[];
+ partialPhoneNumber: string;
+ onSendAnotherCode: () => void;
+};
+
+export default function StepTotp(props: IProps) {
+ const { onSubmit, validationErrors, partialPhoneNumber, onSendAnotherCode } = props;
+ const [disableNewCodeButton, setDisableNewCodeButton] = React.useState(false);
+ const [secondsBeforeNewCode, setSecondsBeforeNewCode] = React.useState(0);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ if (secondsBeforeNewCode > 0) {
+ setSecondsBeforeNewCode(secondsBeforeNewCode - 1);
+ if (secondsBeforeNewCode === 1) {
+ setDisableNewCodeButton(false);
+ }
+ }
+ }, 1000);
+ return () => clearInterval(interval);
+ }, [secondsBeforeNewCode]);
+
+ const sendAnotherCode = () => {
+ onSendAnotherCode();
+ setDisableNewCodeButton(true);
+ setSecondsBeforeNewCode(30);
+ };
+
+ return (
+
+
+
+ Votre code a été envoyé par SMS au ** ** ** {partialPhoneNumber.replace(/(.{2})/g, "$1 ")}
+
+
+
+
+ Vous n'avez rien reçu ?
+
+ Envoyer un nouveau code
+
+ {secondsBeforeNewCode !== 0 && (
+
+ Redemandez un code dans
+
+ 00:{secondsBeforeNewCode < 10 ? `0${secondsBeforeNewCode}` : secondsBeforeNewCode}
+
+
+ )}
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Login/france-connect.svg b/src/front/Components/Layouts/Login/france-connect.svg
new file mode 100644
index 00000000..98ae5ac4
--- /dev/null
+++ b/src/front/Components/Layouts/Login/france-connect.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/front/Themes/constants-old.scss b/src/front/Themes/constants-old.scss
new file mode 100644
index 00000000..975d1a60
--- /dev/null
+++ b/src/front/Themes/constants-old.scss
@@ -0,0 +1,44 @@
+// $screen-xl: 2559px;
+$screen-l: 1439px;
+$screen-ls: 1280px;
+$screen-m: 1023px;
+$screen-s: 767px;
+// $screen-xs: 424px;
+
+$custom-easing: cubic-bezier(0.645, 0.045, 0.355, 1);
+
+// Generic colors
+$black: #000000;
+$white: #ffffff;
+
+// Flash colors
+$green-flash: #12bf4d;
+$blue-flash: #005176;
+$turquoise-flash: #3fa79e;
+$purple-flash: #320756;
+$purple-hover: #4e1480;
+$orange-flash: #ffb017;
+$red-flash: #a63a23;
+$re-hover: #cc4c31;
+$pink-flash: #bd4b91;
+$pink-hover: #e34ba9;
+
+// Soft colors
+$green-soft: #baf2cd;
+$blue-soft: #a7c6d4;
+$orange-soft: #ffdc99;
+$turquoise-soft: #c3eae6;
+$purple-soft: #c5b2d4;
+$orange-soft: #ffdc99;
+$red-soft: #f08771;
+$pink-soft: #f8b9df;
+
+$orange-soft-hover: #ffd078;
+$grey: #939393;
+$grey-medium: #e7e7e7;
+$grey-soft: #f9f9f9;
+
+$modal-background: rgba(0, 0, 0, 0.44);
+
+$shadow-nav: 0px 8px 10px rgba(0, 0, 0, 0.07);
+$shadow-tooltip: 0px 4px 24px rgba(0, 0, 0, 0.15);
diff --git a/src/front/Themes/variables-old.scss b/src/front/Themes/variables-old.scss
new file mode 100644
index 00000000..31b5f274
--- /dev/null
+++ b/src/front/Themes/variables-old.scss
@@ -0,0 +1,36 @@
+@import "@Themes/constants.scss";
+
+:root {
+ --root-max-width: 1440px;
+ --root-margin: auto;
+ --root-padding: 64px 120px;
+
+ --font-text-family: "Inter", sans-serif;
+
+ --green-flash: #{$green-flash};
+ --blue-flash: #{$blue-flash};
+ --turquoise-flash: #{$turquoise-flash};
+ --purple-flash: #{$purple-flash};
+ --purple-hover: #{$purple-hover};
+ --orange-flash: #{$orange-flash};
+ --red-flash: #{$red-flash};
+ --re-hover: #{$re-hover};
+ --pink-flash: #{$pink-flash};
+ --pink-hover: #{$pink-hover};
+
+ --green-soft: #{$green-soft};
+ --blue-soft: #{$blue-soft};
+ --turquoise-soft: #{$turquoise-soft};
+ --purple-soft: #{$purple-soft};
+ --orange-soft: #{$orange-soft};
+ --orange-soft-hover: #{$orange-soft-hover};
+ --red-soft: #{$red-soft};
+ --pink-soft: #{$pink-soft};
+
+ --grey: #{$grey};
+ --grey-medium: #{$grey-medium};
+ --grey-soft: #{$grey-soft};
+
+ --black: #{$black};
+ --white: #{$white};
+}
diff --git a/src/pages/design-system/index.tsx b/src/pages/design-system/index.tsx
new file mode 100644
index 00000000..d3835570
--- /dev/null
+++ b/src/pages/design-system/index.tsx
@@ -0,0 +1,5 @@
+import DesignSystem from "@Front/Components/Layouts/DesignSystem";
+
+export default function Route() {
+ return ;
+}