2024-05-31 15:08:11 +02:00

523 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import Typography, { ITypo, ITypoColor } 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";
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<void>;
getFolderCallback: () => Promise<void>;
openedCustomer?: string;
};
type IState = {
isArchivedModalOpen: boolean;
inputArchivedDescripton: string;
isValidateModalVisible: boolean;
hasValidateAnchoring: boolean;
isVerifDeleteModalVisible: boolean;
isPreventArchiveModalOpen: boolean;
loadingAnchoring: boolean;
};
class FolderInformationClass extends BasePage<IPropsClass, IState> {
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 (
<DefaultNotaryDashboard title={"Dossier"} isArchived={false} mobileBackText="Retour aux dossiers">
{!this.props.isLoading && (
<div className={classes["root"]}>
{this.props.selectedFolder ? (
<div className={classes["folder-informations"]}>
<div className={classes["folder-header"]}>
<div className={classes["header"]}>
<div className={classes["title"]}>
<Typography typo={ITypo.H1Bis}>Informations du dossier</Typography>
</div>
<Link href={redirectPathEditCollaborators}>
<Button variant={EButtonVariant.LINE} icon={ChevronIcon}>
Modifier les collaborateurs
</Button>
</Link>
</div>
<FolderBoxInformation
anchorStatus={this.props.isAnchored}
folder={this.props.selectedFolder}
type={EFolderBoxInformationType.INFORMATIONS}
/>
<div className={classes["second-box"]}>
<FolderBoxInformation
anchorStatus={this.props.isAnchored}
folder={this.props.selectedFolder}
type={EFolderBoxInformationType.DESCRIPTION}
/>
</div>
<div className={classes["progress-bar"]}>
<QuantityProgressBar
title="Complétion du dossier"
total={100}
currentNumber={this.getCompletionNumber()}
/>
</div>
{this.doesFolderHaveCustomer() && (
<ClientSection
folder={this.props.selectedFolder}
anchorStatus={this.props.isAnchored}
getFolderCallback={this.props.getFolderCallback}
openedCustomer={this.props.openedCustomer}
/>
)}
</div>
{!this.doesFolderHaveCustomer() && (
<ClientSection
folder={this.props.selectedFolder}
anchorStatus={this.props.isAnchored}
getFolderCallback={this.props.getFolderCallback}
openedCustomer={this.props.openedCustomer}
/>
)}
<div className={classes["button-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={this.openArchivedModal}>
Archiver le dossier
</Button>
{this.everyDocumentValidated() && !this.props.isLoading && (
<>
{this.props.isAnchored === AnchorStatus.NOT_ANCHORED && (
<Button variant={EButtonVariant.PRIMARY} onClick={this.openValidateModal}>
Valider et ancrer
</Button>
)}
{this.props.isAnchored === AnchorStatus.ANCHORING && (
<Button variant={EButtonVariant.PRIMARY} disabled>
Demande d'ancrage envoyée...&nbsp;&nbsp;
<div className={classes["loader-container"]}>
<div className={classes["loader"]}>
<Loader />
</div>
</div>
</Button>
)}
{this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (
<Button
variant={EButtonVariant.PRIMARY}
onClick={() => this.downloadAnchoringProof(this.props.selectedFolder?.uid)}
disabled={this.state.loadingAnchoring}>
Télécharger la preuve d'ancrage
{this.state.loadingAnchoring && (
<div className={classes["loader-container"]}>
<div className={classes["loader"]}>
<Loader />
</div>
</div>
)}
</Button>
)}
</>
)}
{this.canDeleteFolder() && (
<span className={classes["delete-folder"]} onClick={this.openVerifDeleteFolder}>
<Button variant={EButtonVariant.SECONDARY}>Supprimer le dossier</Button>
</span>
)}
</div>
<Confirm
isOpen={this.state.isArchivedModalOpen}
onAccept={this.onArchivedModalAccepted}
onClose={this.closeArchivedModal}
closeBtn
header={"Archiver le dossier ?"}
cancelText={"Annuler"}
confirmText={"Archiver"}>
<div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>Souhaitez-vous vraiment archiver le dossier ?</Typography>
</div>
<TextAreaField
name="archived_description"
placeholder="Description"
onChange={this.onArchivedDescriptionInputChange}
/>
</Confirm>
<Confirm
isOpen={this.state.isPreventArchiveModalOpen}
onAccept={this.onPreventArchiveModalAccepted}
onClose={this.closePreventArchiveModal}
closeBtn
header={"Archiver le dossier"}
cancelText={"Annuler"}
confirmText={"Archiver"}>
<div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>
Vous êtes en train darchiver le dossier sans avoir lancré, êtes-vous sûr de vouloir le faire ?
</Typography>
</div>
<TextAreaField
name="archived_description"
placeholder="Description"
onChange={this.onArchivedDescriptionInputChange}
/>
</Confirm>
<Confirm
isOpen={this.state.isVerifDeleteModalVisible}
onAccept={this.deleteFolder}
onClose={this.closeVerifDeleteFolder}
closeBtn
header={"Êtes-vous sûr de vouloir supprimer ce dossier ?"}
cancelText={"Annuler"}
confirmText={"Confirmer"}>
<div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>Cette action sera irréversible.</Typography>
</div>
</Confirm>
</div>
) : (
<div className={classes["no-folder-selected"]}>
<Typography typo={ITypo.H1Bis}>Informations du dossier</Typography>
<div className={classes["choose-a-folder"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez un dossier
</Typography>
</div>
</div>
)}
</div>
)}
{this.props.isLoading && (
<div className={classes["loader-container"]}>
<div className={classes["loader"]}>
<Loader />
</div>
</div>
)}
<Confirm
isOpen={this.state.isValidateModalVisible}
onClose={this.closeModal}
onAccept={this.validateAnchoring}
closeBtn={true}
hasContainerClosable={true}
header={
this.state.hasValidateAnchoring
? "Dossier en cours de certification"
: "Êtes-vous sûr de vouloir ancrer et certifier ce dossier ?"
}
cancelText={"Annuler"}
confirmText={"Confirmer"}
showButtons={!this.state.hasValidateAnchoring}>
<div className={classes["validate-document-container"]}>
{!this.state.hasValidateAnchoring && (
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}>
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.
</Typography>
)}
{this.state.hasValidateAnchoring && (
<div className={classes["document-validating-container"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}>
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.
</Typography>
<Image src={ValidateAnchoringGif} alt="Anchoring animation" className={classes["validate-gif"]} />
</div>
)}
</div>
</Confirm>
</DefaultNotaryDashboard>
);
}
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<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
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>(AnchorStatus.NOT_ANCHORED);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [selectedFolder, setSelectedFolder] = useState<OfficeFolder | null>(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,
},
};
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 (
<FolderInformationClass
{...props}
selectedFolderUid={folderUid}
router={router}
isAnchored={isAnchored}
isLoading={isLoading}
selectedFolder={selectedFolder}
getAnchoringStatus={getAnchoringStatus}
getFolderCallback={getFolder}
openedCustomer={customerUid}
/>
);
}