diff --git a/src/front/Components/DesignSystem/EmptyAlert/classes.module.scss b/src/front/Components/DesignSystem/EmptyAlert/classes.module.scss
new file mode 100644
index 00000000..d356ef14
--- /dev/null
+++ b/src/front/Components/DesignSystem/EmptyAlert/classes.module.scss
@@ -0,0 +1,20 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: var(--spacing-3, 24px);
+ gap: var(--spacing-lg, 24px);
+
+ border-radius: var(--radius-minimal, 8px);
+ background: var(--primary-weak-higlight, #e5eefa);
+
+ text-align: center;
+
+ svg {
+ width: 32px;
+ stroke: var(--primary-weak-contrast);
+ }
+}
diff --git a/src/front/Components/DesignSystem/EmptyAlert/index.tsx b/src/front/Components/DesignSystem/EmptyAlert/index.tsx
new file mode 100644
index 00000000..878db597
--- /dev/null
+++ b/src/front/Components/DesignSystem/EmptyAlert/index.tsx
@@ -0,0 +1,28 @@
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import React from "react";
+
+import classes from "./classes.module.scss";
+
+type IProps = {
+ icon: React.ReactNode;
+ title: string;
+ description: string;
+ footer?: React.ReactNode;
+};
+
+export default function EmptyAlert(props: IProps) {
+ const { icon, title, description, footer } = props;
+
+ return (
+
+ {icon}
+
+ {title}
+
+
+ {description}
+
+ {footer}
+
+ );
+}
diff --git a/src/front/Components/DesignSystem/Modal/classes.module.scss b/src/front/Components/DesignSystem/Modal/classes.module.scss
index da05f014..f674201d 100644
--- a/src/front/Components/DesignSystem/Modal/classes.module.scss
+++ b/src/front/Components/DesignSystem/Modal/classes.module.scss
@@ -21,7 +21,7 @@
.header {
display: flex;
- align-items: center;
+ align-items: flex-start;
justify-content: space-between;
padding: var(--spacing-md, 16px) var(--modal-spacing, 16px);
gap: var(--spacing-md, 16px);
diff --git a/src/front/Components/Elements/Tabs/index.tsx b/src/front/Components/Elements/Tabs/index.tsx
index 5d7eaae8..7906b750 100644
--- a/src/front/Components/Elements/Tabs/index.tsx
+++ b/src/front/Components/Elements/Tabs/index.tsx
@@ -8,7 +8,7 @@ import useOpenable from "@Front/Hooks/useOpenable";
export type ITabValue = T & {
id: unknown;
-};
+}
type ITabInternal = ITab & {
key?: string;
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..55503cfe
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/classes.module.scss
@@ -0,0 +1,11 @@
+@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;
+}
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..891c64cd
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/ClientBox/index.tsx
@@ -0,0 +1,57 @@
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import { ICustomer } from "..";
+import classes from "./classes.module.scss";
+import { PencilSquareIcon, TrashIcon } from "@heroicons/react/24/outline";
+import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
+import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+
+type IProps = {
+ customer: ICustomer;
+};
+
+export default function ClientBox(props: IProps) {
+ const { customer } = props;
+
+ return (
+
+
+
+ {customer.contact?.last_name}
+
+ } />
+
+
+
+ Numéro de téléphone
+
+
+ {customer.contact?.phone_number}
+
+
+
+
+ E-mail
+
+
+ {customer.contact?.email}
+
+
+
+
+ Note client
+
+
+ TODO
+
+
+
+
}>
+ Supprimer le client
+
+
+ );
+}
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..73f090b5
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/DocumentTables/index.tsx
@@ -0,0 +1,155 @@
+import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
+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 { 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 { useCallback, useEffect, useMemo, useState } from "react";
+
+import classes from "./classes.module.scss";
+
+type IProps = {
+ documents: Document[];
+ totalOfDocumentTypes: number;
+};
+
+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",
+ },
+];
+
+export default function DocumentTables(props: IProps) {
+ const { documents: documentsProps, totalOfDocumentTypes } = props;
+ const [documents, setDocuments] = useState(documentsProps);
+
+ useEffect(() => {
+ setDocuments(documentsProps);
+ }, [documentsProps]);
+
+ const deleteAskedDocument = useCallback(
+ (uid: string | undefined) => {
+ if (!uid) return;
+ return Documents.getInstance()
+ .delete(uid)
+ .then(() => setDocuments(documents.filter((document) => document.uid !== uid)))
+ .catch((error) => console.warn(error));
+ },
+ [documents],
+ );
+
+ const askDocuments: 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: deleteAskedDocument(document.uid)} />} />,
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [deleteAskedDocument, documents],
+ );
+
+ 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,
+ actions: } />,
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents],
+ );
+
+ 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,
+ actions: (
+
+ } />
+ } />
+
+ ),
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents],
+ );
+
+ 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,
+ actions: "",
+ };
+ })
+ .filter((document) => document !== null) as IRowProps[],
+ [documents],
+ );
+
+ const progressValidated = useMemo(() => validatedDocuments.length / totalOfDocumentTypes, [validatedDocuments, totalOfDocumentTypes]);
+
+ return (
+
+
+
+ Documents
+
+
+
+
+ {toValidateDocuments.length > 0 &&
}
+ {validatedDocuments.length > 0 &&
}
+ {refusedDocuments.length > 0 &&
}
+
+ );
+}
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..b55462e7
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/classes.module.scss
@@ -0,0 +1,26 @@
+@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;
+ }
+
+ .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..71119c08
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientView/index.tsx
@@ -0,0 +1,92 @@
+import Tabs from "@Front/Components/Elements/Tabs";
+import Customer from "le-coffre-resources/dist/Customer";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+
+import { 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";
+
+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 totalOfDocumentTypes = useMemo(() => folder.deed?.document_types?.length ?? 0, [folder]);
+
+ return (
+
+
+ {tabs && tabs={tabs} onSelect={setCustomer} />}
+ {anchorStatus === AnchorStatus.NOT_ANCHORED && (
+
+ }
+ variant={EButtonVariant.PRIMARY}
+ styletype={EButtonstyletype.TEXT}>
+ Ajouter un client
+
+
+ )}
+
+
+
+
+
+ } variant={EButtonVariant.PRIMARY} fullwidth>
+ Demander un document
+
+
+
+ {doesCustomerHaveDocument ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/ClientSection/classes.module.scss
similarity index 100%
rename from src/front/Components/Layouts/Folder/FolderInformation/ClientSection/classes.module.scss
rename to src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/ClientSection/classes.module.scss
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/ClientSection/index.tsx
similarity index 100%
rename from src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx
rename to src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/ClientSection/index.tsx
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/classes.module.scss
new file mode 100644
index 00000000..a274d91e
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/classes.module.scss
@@ -0,0 +1,113 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ min-height: 100%;
+
+ .no-folder-selected {
+ width: 100%;
+
+ .choose-a-folder {
+ margin-top: 96px;
+ text-align: center;
+ }
+ }
+
+ .folder-informations {
+ width: 100%;
+ min-height: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-direction: column;
+ flex-grow: 1;
+
+ .folder-header {
+ width: 100%;
+
+ .header {
+ margin-bottom: 32px;
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ @media (max-width: $screen-m) {
+ flex-wrap: wrap;
+
+ .title {
+ margin-bottom: 24px;
+ }
+ }
+ }
+ }
+ }
+
+ .second-box {
+ margin-top: 24px;
+ margin-bottom: 32px;
+ }
+
+ .progress-bar {
+ margin-bottom: 32px;
+ }
+
+ .button-container {
+ width: 100%;
+ display: flex;
+ gap: 16px;
+ text-align: center;
+ justify-content: center;
+
+ .delete-folder {
+ display: flex;
+ margin-left: 12px;
+ }
+
+ @media (max-width: $screen-m) {
+ display: flex;
+ flex-direction: column;
+
+ .delete-folder {
+ margin-left: 0;
+ margin-top: 12px;
+
+ > * {
+ flex: 1;
+ }
+ }
+
+ > * {
+ width: 100%;
+ }
+ }
+ }
+
+ .modal-title {
+ margin-bottom: 24px;
+ }
+}
+
+.validate-document-container {
+ .document-validating-container {
+ .validate-gif {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+ }
+}
+
+.loader-container {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ .loader {
+ width: 21px;
+ height: 21px;
+ }
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/index.tsx
new file mode 100644
index 00000000..7d943dd7
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/FolderInformationOld/index.tsx
@@ -0,0 +1,541 @@
+import ChevronIcon from "@Assets/Icons/chevron.svg";
+import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
+import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
+import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
+import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
+import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
+import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
+import Module from "@Front/Config/Module";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
+import Link from "next/link";
+import { NextRouter, useRouter } from "next/router";
+import { ChangeEvent, useCallback, useEffect, useState } from "react";
+import Image from "next/image";
+import ValidateAnchoringGif from "@Front/Assets/images/validate_anchoring.gif";
+
+import BasePage from "../../../Base";
+import classes from "./classes.module.scss";
+import ClientSection from "./ClientSection";
+import Loader from "@Front/Components/DesignSystem/Loader";
+import Newsletter from "@Front/Components/DesignSystem/Newsletter";
+import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
+
+export enum AnchorStatus {
+ "VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
+ "ANCHORING" = "ANCHORING",
+ "NOT_ANCHORED" = "NOT_ANCHORED",
+}
+
+type IProps = {};
+
+type IPropsClass = IProps & {
+ router: NextRouter;
+ selectedFolderUid: string;
+ isAnchored: AnchorStatus;
+ isLoading: boolean;
+ selectedFolder: OfficeFolder | null;
+ getAnchoringStatus: () => Promise;
+ getFolderCallback: () => Promise;
+ openedCustomer?: string;
+};
+
+type IState = {
+ isArchivedModalOpen: boolean;
+ inputArchivedDescripton: string;
+ isValidateModalVisible: boolean;
+ hasValidateAnchoring: boolean;
+ isVerifDeleteModalVisible: boolean;
+ isPreventArchiveModalOpen: boolean;
+ loadingAnchoring: boolean;
+};
+class FolderInformationClass extends BasePage {
+ public constructor(props: IPropsClass) {
+ super(props);
+ this.state = {
+ isArchivedModalOpen: false,
+ inputArchivedDescripton: "",
+ isValidateModalVisible: false,
+ hasValidateAnchoring: false,
+ isVerifDeleteModalVisible: false,
+ isPreventArchiveModalOpen: false,
+ loadingAnchoring: false,
+ };
+ this.openArchivedModal = this.openArchivedModal.bind(this);
+ this.closeArchivedModal = this.closeArchivedModal.bind(this);
+ this.onArchivedModalAccepted = this.onArchivedModalAccepted.bind(this);
+ this.onPreventArchiveModalAccepted = this.onPreventArchiveModalAccepted.bind(this);
+ this.getCompletionNumber = this.getCompletionNumber.bind(this);
+ this.onArchivedDescriptionInputChange = this.onArchivedDescriptionInputChange.bind(this);
+ this.deleteFolder = this.deleteFolder.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.validateAnchoring = this.validateAnchoring.bind(this);
+ this.openValidateModal = this.openValidateModal.bind(this);
+ this.openVerifDeleteFolder = this.openVerifDeleteFolder.bind(this);
+ this.closeVerifDeleteFolder = this.closeVerifDeleteFolder.bind(this);
+ this.closePreventArchiveModal = this.closePreventArchiveModal.bind(this);
+ }
+
+ // TODO: Message if the user has not created any folder yet
+ // TODO: get the selected folder from the api in componentDidMount
+ public override render(): JSX.Element {
+ const redirectPathEditCollaborators = Module.getInstance()
+ .get()
+ .modules.pages.Folder.pages.EditCollaborators.props.path.replace("[folderUid]", this.props.selectedFolderUid);
+ return (
+
+ {!this.props.isLoading && (
+
+ {this.props.selectedFolder ? (
+
+
+
+
+ Informations du dossier
+
+
+
+ Modifier les collaborateurs
+
+
+
+
+
+
+
+
+
+
+ {this.doesFolderHaveCustomer() && (
+
+ )}
+
+
+ {!this.doesFolderHaveCustomer() && (
+
+ )}
+
+
+
+ Archiver le dossier
+
+ {this.everyDocumentValidated() && !this.props.isLoading && (
+ <>
+ {this.props.isAnchored === AnchorStatus.NOT_ANCHORED && (
+
+ Valider et ancrer
+
+ )}
+ {this.props.isAnchored === AnchorStatus.ANCHORING && (
+
+ Demande d'ancrage envoyée...
+
+
+ )}
+ {this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (
+
this.downloadAnchoringProof(this.props.selectedFolder?.uid)}
+ disabled={this.state.loadingAnchoring}>
+ Télécharger la preuve d'ancrage
+ {this.state.loadingAnchoring && (
+
+ )}
+
+ )}
+ >
+ )}
+ {this.canDeleteFolder() && (
+
+ Supprimer le dossier
+
+ )}
+
+
+
+ Souhaitez-vous vraiment archiver le dossier ?
+
+
+
+
+
+
+ Vous êtes en train d’archiver le dossier sans avoir l’ancré, êtes-vous sûr de vouloir le faire ?
+
+
+
+
+
+
+ Cette action sera irréversible.
+
+
+
+
+ ) : (
+
+
Informations du dossier
+
+
+ Sélectionnez un dossier
+
+
+
+ )}
+
+ )}
+ {this.props.isLoading && (
+
+ )}
+
+
+ {!this.state.hasValidateAnchoring && (
+
+ Les documents du dossier seront certifiés sur la blockchain. Pensez à bien télécharger l'ensemble des
+ documents du dossier ainsi que le fichier de preuve d'ancrage pour les mettre dans la GED de votre logiciel
+ de rédaction d'actes.
+
+ )}
+ {this.state.hasValidateAnchoring && (
+
+
+ Veuillez revenir sur le dossier dans 5 minutes et rafraîchir la page pour télécharger le dossier de
+ preuve d'ancrage et le glisser dans la GED de votre logiciel de rédaction d'acte.
+
+
+
+ )}
+
+
+
+ );
+ }
+
+ private closePreventArchiveModal() {
+ this.setState({
+ isPreventArchiveModalOpen: false,
+ });
+ }
+
+ public openVerifDeleteFolder() {
+ this.setState({
+ isVerifDeleteModalVisible: true,
+ });
+ }
+
+ public closeVerifDeleteFolder() {
+ this.setState({
+ isVerifDeleteModalVisible: false,
+ });
+ }
+
+ private closeModal() {
+ this.setState({
+ isValidateModalVisible: false,
+ });
+ }
+
+ private openValidateModal() {
+ this.setState({
+ isValidateModalVisible: true,
+ });
+ }
+
+ private async validateAnchoring() {
+ this.setState({
+ hasValidateAnchoring: true,
+ });
+
+ try {
+ const timeoutDelay = 9800;
+ await this.anchorFolder();
+ setTimeout(() => {
+ this.setState({
+ isValidateModalVisible: false,
+ });
+ }, timeoutDelay);
+
+ setTimeout(() => {
+ this.setState({
+ hasValidateAnchoring: false,
+ });
+ }, timeoutDelay + 1000);
+ } catch (e) {
+ this.setState({
+ isValidateModalVisible: false,
+ hasValidateAnchoring: false,
+ });
+ console.error(e);
+ }
+ }
+
+ private async anchorFolder() {
+ if (!this.props.selectedFolder?.uid) return;
+ await OfficeFolderAnchors.getInstance().post(this.props.selectedFolder.uid);
+ this.props.getAnchoringStatus();
+ }
+
+ private async downloadAnchoringProof(uid?: string) {
+ if (!uid) return;
+ this.setState({ loadingAnchoring: true });
+ try {
+ const file: Blob = await OfficeFolderAnchors.getInstance().download(uid);
+ const url = window.URL.createObjectURL(file);
+ const a = document.createElement("a");
+ a.style.display = "none";
+ a.href = url;
+ // the filename you want
+ a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.zip`;
+ document.body.appendChild(a);
+ a.click();
+ window.URL.revokeObjectURL(url);
+ this.setState({ loadingAnchoring: false });
+ } catch (e) {
+ this.setState({ loadingAnchoring: false });
+ console.error(e);
+ }
+ }
+
+ private everyDocumentValidated(): boolean {
+ if (!this.props.selectedFolder?.documents) return false;
+ return (
+ this.props.selectedFolder?.documents?.length >= 1 &&
+ this.props.selectedFolder?.documents.every((document) => document.document_status === EDocumentStatus.VALIDATED)
+ );
+ }
+
+ private async deleteFolder() {
+ if (!this.props.selectedFolder?.uid) return;
+ await Folders.getInstance().delete(this.props.selectedFolder.uid);
+ this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
+ }
+
+ private getCompletionNumber() {
+ const documents = this.props.selectedFolder?.documents;
+ if (!documents) return 0;
+ const totalDocuments = documents.length;
+ const refusedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.REFUSED).length ?? 0;
+ const askedDocuments =
+ documents.filter(
+ (document) => document.document_status === EDocumentStatus.ASKED || document.document_status === EDocumentStatus.DEPOSITED,
+ ).length ?? 0;
+ const depositedDocuments = totalDocuments - askedDocuments - refusedDocuments;
+ const percentage = (depositedDocuments / totalDocuments) * 100;
+ return isNaN(percentage) ? 0 : percentage;
+ }
+
+ private doesFolderHaveCustomer(): boolean {
+ if (!this.props.selectedFolder?.customers) return false;
+ return this.props.selectedFolder?.customers!.length > 0;
+ }
+
+ private canDeleteFolder(): boolean {
+ return (this.props.selectedFolder?.customers?.length ?? 0) === 0 && (this.props.selectedFolder?.documents?.length ?? 0) === 0;
+ }
+
+ private openArchivedModal(): void {
+ if (this.everyDocumentValidated() && this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN) {
+ this.setState({ isArchivedModalOpen: true });
+ } else {
+ this.setState({ isPreventArchiveModalOpen: true });
+ }
+ }
+
+ private closeArchivedModal(): void {
+ this.setState({ isArchivedModalOpen: false });
+ }
+
+ private onArchivedDescriptionInputChange(e: ChangeEvent) {
+ this.setState({ inputArchivedDescripton: e.target.value });
+ }
+
+ private async onArchivedModalAccepted() {
+ if (!this.props.selectedFolder) return;
+ await Folders.getInstance().archive(this.props.selectedFolder.uid ?? "", this.state.inputArchivedDescripton);
+ this.closeArchivedModal();
+ this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
+ }
+
+ private async onPreventArchiveModalAccepted() {
+ if (!this.props.selectedFolder) return;
+ await Folders.getInstance().archive(this.props.selectedFolder.uid ?? "", this.state.inputArchivedDescripton);
+ this.closePreventArchiveModal();
+ this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
+ }
+}
+
+export default function FolderInformation(props: IProps) {
+ const router = useRouter();
+ const [isAnchored, setIsAnchored] = useState(AnchorStatus.NOT_ANCHORED);
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedFolder, setSelectedFolder] = useState(null);
+
+ let { folderUid } = router.query;
+ const customerUid = router.query["customerUid"] as string | undefined;
+ folderUid = folderUid as string;
+
+ const getAnchoringStatus = useCallback(async () => {
+ if (!folderUid) return;
+ try {
+ const anchorStatus = await OfficeFolderAnchors.getInstance().getByUid(folderUid as string);
+ setIsAnchored(anchorStatus.status === "VERIFIED_ON_CHAIN" ? AnchorStatus.VERIFIED_ON_CHAIN : AnchorStatus.ANCHORING);
+ } catch (e) {
+ setIsAnchored(AnchorStatus.NOT_ANCHORED);
+ }
+ }, [folderUid]);
+
+ const getFolder = useCallback(async () => {
+ if (!folderUid) return;
+ setIsLoading(true);
+ const query = {
+ q: {
+ deed: { include: { deed_type: true } },
+ office: true,
+ customers: {
+ include: {
+ contact: true,
+ documents: {
+ include: {
+ folder: true,
+ document_type: true,
+ files: true,
+ },
+ },
+ },
+ },
+ documents: {
+ include: {
+ depositor: {
+ include: {
+ contact: true,
+ },
+ },
+ },
+ },
+ folder_anchor: true,
+ notes: {
+ include: {
+ customer: true,
+ },
+ },
+ },
+ };
+
+ const folder = await Folders.getInstance().getByUid(folderUid as string, query);
+ if (folder) {
+ setSelectedFolder(folder);
+ getAnchoringStatus();
+ }
+
+ setIsLoading(false);
+ }, [folderUid, getAnchoringStatus]);
+
+ useEffect(() => {
+ setIsLoading(true);
+ getFolder();
+ }, [getFolder]);
+
+ return (
+
+ );
+}
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..5abf5a01
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/classes.module.scss
@@ -0,0 +1,39 @@
+@import "@Themes/constants.scss";
+
+.root {
+ display: flex;
+ gap: var(--spacing-lg, 40px);
+ .info-box1 {
+ display: flex;
+ width: 648px;
+ 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);
+ .progress-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .icon-container {
+ display: flex;
+ gap: var(--spacing-md, 8px);
+ }
+ }
+ }
+
+ .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..e6a6656c
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/InformationSection/index.tsx
@@ -0,0 +1,71 @@
+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 { UserGroupIcon, PencilSquareIcon, ArchiveBoxIcon } from "@heroicons/react/24/outline";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
+import { useCallback } from "react";
+
+import classes from "./classes.module.scss";
+import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
+
+type IProps = {
+ folder: OfficeFolder | null;
+};
+
+export default function InformationSection(props: IProps) {
+ const { folder } = props;
+ const getCompletionNumber = useCallback(() => {
+ const documents = folder?.documents;
+ if (!documents) return 0;
+ const totalDocuments = documents.length;
+ const refusedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.REFUSED).length ?? 0;
+ const askedDocuments =
+ documents.filter(
+ (document) => document.document_status === EDocumentStatus.ASKED || document.document_status === EDocumentStatus.DEPOSITED,
+ ).length ?? 0;
+ const depositedDocuments = totalDocuments - askedDocuments - refusedDocuments;
+ const percentage = (depositedDocuments / totalDocuments) * 100;
+ return isNaN(percentage) ? 0 : percentage;
+ }, [folder]);
+
+ return (
+
+
+
+ {folder?.folder_number}
+ {folder?.name}
+
+
+
+
+ Ouverture du dossier
+
+ {folder?.created_at ? new Date(folder.created_at).toLocaleDateString() : ""}
+
+
+
+
+
+
+
+
+
+ } variant={EIconButtonVariant.NEUTRAL} />
+ }
+ variant={EIconButtonVariant.NEUTRAL}
+ />
+ } variant={EIconButtonVariant.ERROR} />
+
+
+
+
+ Notre dossier
+
+ Travaux de rénovation en cours.
+
+
+
+ );
+}
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..3d9f7da1
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx
@@ -0,0 +1,34 @@
+import Button, { 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..4e2e74c1
--- /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 navigate = 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(() => navigate.push(Module.getInstance().get().modules.pages.Folder.props.path))
+ .then(onClose);
+ }, [folder, navigate, 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..3273eeb6
--- /dev/null
+++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/index.tsx
@@ -0,0 +1,41 @@
+import Button, { 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}>
+ Supprimer le dossier
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/classes.module.scss b/src/front/Components/Layouts/Folder/FolderInformation/classes.module.scss
index a274d91e..82b505c7 100644
--- a/src/front/Components/Layouts/Folder/FolderInformation/classes.module.scss
+++ b/src/front/Components/Layouts/Folder/FolderInformation/classes.module.scss
@@ -2,91 +2,13 @@
.root {
display: flex;
- align-items: center;
flex-direction: column;
- min-height: 100%;
+ gap: var(--spacing-xl, 32px);
- .no-folder-selected {
- width: 100%;
-
- .choose-a-folder {
- margin-top: 96px;
- text-align: center;
- }
- }
-
- .folder-informations {
- width: 100%;
- min-height: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-direction: column;
- flex-grow: 1;
-
- .folder-header {
- width: 100%;
-
- .header {
- margin-bottom: 32px;
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- @media (max-width: $screen-m) {
- flex-wrap: wrap;
-
- .title {
- margin-bottom: 24px;
- }
- }
- }
- }
- }
-
- .second-box {
- margin-top: 24px;
- margin-bottom: 32px;
- }
-
- .progress-bar {
- margin-bottom: 32px;
- }
-
- .button-container {
- width: 100%;
- display: flex;
- gap: 16px;
- text-align: center;
- justify-content: center;
-
- .delete-folder {
- display: flex;
- margin-left: 12px;
- }
-
- @media (max-width: $screen-m) {
- display: flex;
- flex-direction: column;
-
- .delete-folder {
- margin-left: 0;
- margin-top: 12px;
-
- > * {
- flex: 1;
- }
- }
-
- > * {
- width: 100%;
- }
- }
- }
-
- .modal-title {
- margin-bottom: 24px;
+ .separator {
+ background-color: var(--separator-stroke-light);
+ width: 1px;
+ align-self: stretch;
}
}
diff --git a/src/front/Components/Layouts/Folder/FolderInformation/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/index.tsx
index 5d0d988e..07c80935 100644
--- a/src/front/Components/Layouts/Folder/FolderInformation/index.tsx
+++ b/src/front/Components/Layouts/Folder/FolderInformation/index.tsx
@@ -1,27 +1,15 @@
-import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
-import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
-import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
-import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
-import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
-import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
-import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
-import Module from "@Front/Config/Module";
-import { OfficeFolder } from "le-coffre-resources/dist/Notary";
-import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
-import Link from "next/link";
-import { NextRouter, useRouter } from "next/router";
-import { ChangeEvent, useCallback, useEffect, useState } from "react";
-import Image from "next/image";
-import ValidateAnchoringGif from "@Front/Assets/images/validate_anchoring.gif";
-
-import BasePage from "../../Base";
-import classes from "./classes.module.scss";
-import ClientSection from "./ClientSection";
import Loader from "@Front/Components/DesignSystem/Loader";
-import Newsletter from "@Front/Components/DesignSystem/Newsletter";
-import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
+import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
+import { OfficeFolder } from "le-coffre-resources/dist/Notary";
+import { useParams } from "next/navigation";
+import { useCallback, useEffect, useMemo, useState } from "react";
+
+import classes from "./classes.module.scss";
+import InformationSection from "./InformationSection";
+import NoClientView from "./NoClientView";
+import ClientView from "./ClientView";
export enum AnchorStatus {
"VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
@@ -31,455 +19,19 @@ export enum AnchorStatus {
type IProps = {};
-type IPropsClass = IProps & {
- router: NextRouter;
- selectedFolderUid: string;
- isAnchored: AnchorStatus;
- isLoading: boolean;
- selectedFolder: OfficeFolder | null;
- getAnchoringStatus: () => Promise;
- getFolderCallback: () => Promise;
- openedCustomer?: string;
-};
-
-type IState = {
- isArchivedModalOpen: boolean;
- inputArchivedDescripton: string;
- isValidateModalVisible: boolean;
- hasValidateAnchoring: boolean;
- isVerifDeleteModalVisible: boolean;
- isPreventArchiveModalOpen: boolean;
- loadingAnchoring: boolean;
-};
-class FolderInformationClass extends BasePage {
- public constructor(props: IPropsClass) {
- super(props);
- this.state = {
- isArchivedModalOpen: false,
- inputArchivedDescripton: "",
- isValidateModalVisible: false,
- hasValidateAnchoring: false,
- isVerifDeleteModalVisible: false,
- isPreventArchiveModalOpen: false,
- loadingAnchoring: false,
- };
- this.openArchivedModal = this.openArchivedModal.bind(this);
- this.closeArchivedModal = this.closeArchivedModal.bind(this);
- this.onArchivedModalAccepted = this.onArchivedModalAccepted.bind(this);
- this.onPreventArchiveModalAccepted = this.onPreventArchiveModalAccepted.bind(this);
- this.getCompletionNumber = this.getCompletionNumber.bind(this);
- this.onArchivedDescriptionInputChange = this.onArchivedDescriptionInputChange.bind(this);
- this.deleteFolder = this.deleteFolder.bind(this);
- this.closeModal = this.closeModal.bind(this);
- this.validateAnchoring = this.validateAnchoring.bind(this);
- this.openValidateModal = this.openValidateModal.bind(this);
- this.openVerifDeleteFolder = this.openVerifDeleteFolder.bind(this);
- this.closeVerifDeleteFolder = this.closeVerifDeleteFolder.bind(this);
- this.closePreventArchiveModal = this.closePreventArchiveModal.bind(this);
- }
-
- // TODO: Message if the user has not created any folder yet
- // TODO: get the selected folder from the api in componentDidMount
- public override render(): JSX.Element {
- const redirectPathEditCollaborators = Module.getInstance()
- .get()
- .modules.pages.Folder.pages.EditCollaborators.props.path.replace("[folderUid]", this.props.selectedFolderUid);
- return (
-
- {!this.props.isLoading && (
-
- {this.props.selectedFolder ? (
-
-
-
-
- Informations du dossier
-
-
-
}>
- Modifier les collaborateurs
-
-
-
-
-
-
-
-
-
-
- {this.doesFolderHaveCustomer() && (
-
- )}
-
-
- {!this.doesFolderHaveCustomer() && (
-
- )}
-
-
-
- Archiver le dossier
-
- {this.everyDocumentValidated() && !this.props.isLoading && (
- <>
- {this.props.isAnchored === AnchorStatus.NOT_ANCHORED && (
-
- Valider et ancrer
-
- )}
- {this.props.isAnchored === AnchorStatus.ANCHORING && (
-
- Demande d'ancrage envoyée...
-
-
- )}
- {this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (
-
this.downloadAnchoringProof(this.props.selectedFolder?.uid)}
- disabled={this.state.loadingAnchoring}>
- Télécharger la preuve d'ancrage
- {this.state.loadingAnchoring && (
-
- )}
-
- )}
- >
- )}
- {this.canDeleteFolder() && (
-
- Supprimer le dossier
-
- )}
-
-
-
- Souhaitez-vous vraiment archiver le dossier ?
-
-
-
-
-
-
- Vous êtes en train d’archiver le dossier sans avoir l’ancré, êtes-vous sûr de vouloir le faire ?
-
-
-
-
-
-
- Cette action sera irréversible.
-
-
-
-
- ) : (
-
-
Informations du dossier
-
-
- Sélectionnez un dossier
-
-
-
- )}
-
- )}
- {this.props.isLoading && (
-
- )}
-
-
- {!this.state.hasValidateAnchoring && (
-
- Les documents du dossier seront certifiés sur la blockchain. Pensez à bien télécharger l'ensemble des
- documents du dossier ainsi que le fichier de preuve d'ancrage pour les mettre dans la GED de votre logiciel
- de rédaction d'actes.
-
- )}
- {this.state.hasValidateAnchoring && (
-
-
- Veuillez revenir sur le dossier dans 5 minutes et rafraîchir la page pour télécharger le dossier de
- preuve d'ancrage et le glisser dans la GED de votre logiciel de rédaction d'acte.
-
-
-
- )}
-
-
-
- );
- }
-
- private closePreventArchiveModal() {
- this.setState({
- isPreventArchiveModalOpen: false,
- });
- }
-
- public openVerifDeleteFolder() {
- this.setState({
- isVerifDeleteModalVisible: true,
- });
- }
-
- public closeVerifDeleteFolder() {
- this.setState({
- isVerifDeleteModalVisible: false,
- });
- }
-
- private closeModal() {
- this.setState({
- isValidateModalVisible: false,
- });
- }
-
- private openValidateModal() {
- this.setState({
- isValidateModalVisible: true,
- });
- }
-
- private async validateAnchoring() {
- this.setState({
- hasValidateAnchoring: true,
- });
-
- try {
- const timeoutDelay = 9800;
- await this.anchorFolder();
- setTimeout(() => {
- this.setState({
- isValidateModalVisible: false,
- });
- }, timeoutDelay);
-
- setTimeout(() => {
- this.setState({
- hasValidateAnchoring: false,
- });
- }, timeoutDelay + 1000);
- } catch (e) {
- this.setState({
- isValidateModalVisible: false,
- hasValidateAnchoring: false,
- });
- console.error(e);
- }
- }
-
- private async anchorFolder() {
- if (!this.props.selectedFolder?.uid) return;
- await OfficeFolderAnchors.getInstance().post(this.props.selectedFolder.uid);
- this.props.getAnchoringStatus();
- }
-
- private async downloadAnchoringProof(uid?: string) {
- if (!uid) return;
- this.setState({ loadingAnchoring: true });
- try {
- const file: Blob = await OfficeFolderAnchors.getInstance().download(uid);
- const url = window.URL.createObjectURL(file);
- const a = document.createElement("a");
- a.style.display = "none";
- a.href = url;
- // the filename you want
- a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.zip`;
- document.body.appendChild(a);
- a.click();
- window.URL.revokeObjectURL(url);
- this.setState({ loadingAnchoring: false });
- } catch (e) {
- this.setState({ loadingAnchoring: false });
- console.error(e);
- }
- }
-
- private everyDocumentValidated(): boolean {
- if (!this.props.selectedFolder?.documents) return false;
- return (
- this.props.selectedFolder?.documents?.length >= 1 &&
- this.props.selectedFolder?.documents.every((document) => document.document_status === EDocumentStatus.VALIDATED)
- );
- }
-
- private async deleteFolder() {
- if (!this.props.selectedFolder?.uid) return;
- await Folders.getInstance().delete(this.props.selectedFolder.uid);
- this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
- }
-
- private getCompletionNumber() {
- const documents = this.props.selectedFolder?.documents;
- if (!documents) return 0;
- const totalDocuments = documents.length;
- const refusedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.REFUSED).length ?? 0;
- const askedDocuments =
- documents.filter(
- (document) => document.document_status === EDocumentStatus.ASKED || document.document_status === EDocumentStatus.DEPOSITED,
- ).length ?? 0;
- const depositedDocuments = totalDocuments - askedDocuments - refusedDocuments;
- const percentage = (depositedDocuments / totalDocuments) * 100;
- return isNaN(percentage) ? 0 : percentage;
- }
-
- private doesFolderHaveCustomer(): boolean {
- if (!this.props.selectedFolder?.customers) return false;
- return this.props.selectedFolder?.customers!.length > 0;
- }
-
- private canDeleteFolder(): boolean {
- return (this.props.selectedFolder?.customers?.length ?? 0) === 0 && (this.props.selectedFolder?.documents?.length ?? 0) === 0;
- }
-
- private openArchivedModal(): void {
- if (this.everyDocumentValidated() && this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN) {
- this.setState({ isArchivedModalOpen: true });
- } else {
- this.setState({ isPreventArchiveModalOpen: true });
- }
- }
-
- private closeArchivedModal(): void {
- this.setState({ isArchivedModalOpen: false });
- }
-
- private onArchivedDescriptionInputChange(e: ChangeEvent) {
- this.setState({ inputArchivedDescripton: e.target.value });
- }
-
- private async onArchivedModalAccepted() {
- if (!this.props.selectedFolder) return;
- await Folders.getInstance().archive(this.props.selectedFolder.uid ?? "", this.state.inputArchivedDescripton);
- this.closeArchivedModal();
- this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
- }
-
- private async onPreventArchiveModalAccepted() {
- if (!this.props.selectedFolder) return;
- await Folders.getInstance().archive(this.props.selectedFolder.uid ?? "", this.state.inputArchivedDescripton);
- this.closePreventArchiveModal();
- this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
- }
-}
-
export default function FolderInformation(props: IProps) {
- const router = useRouter();
- const [isAnchored, setIsAnchored] = useState(AnchorStatus.NOT_ANCHORED);
+ const [anchorStatus, setAnchorStatus] = useState(AnchorStatus.NOT_ANCHORED);
const [isLoading, setIsLoading] = useState(true);
- const [selectedFolder, setSelectedFolder] = useState(null);
+ const [folder, setFolder] = useState(null);
- let { folderUid } = router.query;
- const customerUid = router.query["customerUid"] as string | undefined;
- folderUid = folderUid as string;
+ const params = useParams();
+ const folderUid = params["folderUid"] as string;
- const getAnchoringStatus = useCallback(async () => {
+ const fetchFolder = useCallback(async () => {
if (!folderUid) return;
- try {
- const anchorStatus = await OfficeFolderAnchors.getInstance().getByUid(folderUid as string);
- setIsAnchored(anchorStatus.status === "VERIFIED_ON_CHAIN" ? AnchorStatus.VERIFIED_ON_CHAIN : AnchorStatus.ANCHORING);
- } catch (e) {
- setIsAnchored(AnchorStatus.NOT_ANCHORED);
- }
- }, [folderUid]);
-
- const getFolder = useCallback(async () => {
- if (!folderUid) return;
- setIsLoading(true);
const query = {
q: {
- deed: { include: { deed_type: true } },
+ deed: { include: { deed_type: true, document_types: true } },
office: true,
customers: {
include: {
@@ -511,31 +63,46 @@ export default function FolderInformation(props: IProps) {
},
};
- const folder = await Folders.getInstance().getByUid(folderUid as string, query);
- if (folder) {
- setSelectedFolder(folder);
- getAnchoringStatus();
- }
+ return Folders.getInstance()
+ .getByUid(folderUid, query)
+ .then((folder) => setFolder(folder));
+ }, [folderUid]);
- setIsLoading(false);
- }, [folderUid, getAnchoringStatus]);
+ const fetchAnchorStatus = useCallback(() => {
+ return OfficeFolderAnchors.getInstance()
+ .getByUid(folderUid)
+ .then((anchorStatus) =>
+ setAnchorStatus(anchorStatus.status === "VERIFIED_ON_CHAIN" ? AnchorStatus.VERIFIED_ON_CHAIN : AnchorStatus.ANCHORING),
+ )
+ .catch(() => setAnchorStatus(AnchorStatus.NOT_ANCHORED));
+ }, [folderUid]);
useEffect(() => {
setIsLoading(true);
- getFolder();
- }, [getFolder]);
+ fetchFolder()
+ .then(() => fetchAnchorStatus())
+ .catch((e) => console.error(e))
+ .finally(() => setIsLoading(false));
+ }, [fetchAnchorStatus, fetchFolder, folderUid]);
+
+ const doesFolderHaveClient = useMemo(() => folder?.customers?.length !== 0, [folder]);
return (
-
+
+ {!isLoading && (
+
+
+ {folder && !doesFolderHaveClient && }
+ {folder && doesFolderHaveClient && }
+
+ )}
+ {isLoading && (
+
+ )}
+
);
}