2023-10-02 11:41:31 +02:00

445 lines
15 KiB
TypeScript

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 } 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 CheckBox from "@Front/Components/DesignSystem/CheckBox";
type IProps = {};
type IPropsClass = IProps & {
router: NextRouter;
selectedFolderUid: string;
};
type IState = {
selectedFolder: OfficeFolder | null;
isArchivedModalOpen: boolean;
inputArchivedDescripton: string;
isValidateModalVisible: boolean;
hasValidateAnchoring: boolean;
isVerifDeleteModalVisible: boolean;
isAnchored: boolean | null;
isPreventArchiveModalOpen: boolean;
};
class FolderInformationClass extends BasePage<IPropsClass, IState> {
public constructor(props: IPropsClass) {
super(props);
this.state = {
selectedFolder: null,
isArchivedModalOpen: false,
inputArchivedDescripton: "",
isValidateModalVisible: false,
hasValidateAnchoring: false,
isVerifDeleteModalVisible: false,
isAnchored: null,
isPreventArchiveModalOpen: false,
};
this.onSelectedFolder = this.onSelectedFolder.bind(this);
this.openArchivedModal = this.openArchivedModal.bind(this);
this.closeArchivedModal = this.closeArchivedModal.bind(this);
this.onArchivedModalAccepted = this.onArchivedModalAccepted.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.verifyAnchorStatus = this.verifyAnchorStatus.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"}
onSelectedFolder={this.onSelectedFolder}
isArchived={false}
mobileBackText="Retour aux dossiers">
<div className={classes["root"]}>
{this.state.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 folder={this.state.selectedFolder} type={EFolderBoxInformationType.INFORMATIONS} />
<div className={classes["second-box"]}>
<FolderBoxInformation folder={this.state.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.state.selectedFolder} />}
</div>
{!this.doesFolderHaveCustomer() && <ClientSection folder={this.state.selectedFolder} />}
<div className={classes["button-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={this.openArchivedModal}>
Archiver le dossier
</Button>
{this.everyDocumentValidated() && (
<>
{this.state.isAnchored === null && (
<Button variant={EButtonVariant.PRIMARY} onClick={this.openValidateModal}>
Ancrer le dossier
</Button>
)}
{this.state.isAnchored === true && (
<Button
variant={EButtonVariant.PRIMARY}
onClick={() => this.downloadAnchoringProof(this.state.selectedFolder?.uid)}>
Télécharger la preuve d'ancrage
</Button>
)}
</>
)}
{!this.doesFolderHaveCustomer() && (
<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.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>
<Confirm
isOpen={this.state.isPreventArchiveModalOpen}
onAccept={this.closePreventArchiveModal}
onClose={this.closePreventArchiveModal}
closeBtn
header={"Vous devez valider et certifier un dossier avant de pouvoir l'archiver"}
cancelText={"Annuler"}
confirmText={"J'ai compris"}>
<div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>
Pour valider un dossier, toutes les pièces envoyées par vos clients doivent être validées (vert). Si certains
documents sont en attente (orange), alors, veuillez les valider ou les refuser et veillez à ce qu'aucun document
ne soit encore en demandé au client (gris)
</Typography>
</div>
</Confirm>
<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"]}>
Vous pouvez désormais télécharger les feuilles d'ancrage et les mettre dans la GED de votre logiciel de
rédaction d'acte.
</Typography>
<Image src={ValidateAnchoringGif} alt="Anchoring animation" className={classes["validate-gif"]} />
<div className={classes["dont-show-again"]}>
<CheckBox option={{ label: "Ne plus afficher ce message", value: false }} />
</div>
</div>
)}
</div>
</Confirm>
</DefaultNotaryDashboard>
);
}
public override async componentDidMount() {
const selectedFolder = await this.getFolder();
this.setState(
{
selectedFolder,
},
() => {
this.verifyAnchorStatus();
},
);
}
private closePreventArchiveModal() {
this.setState({
isPreventArchiveModalOpen: false,
});
}
private async verifyAnchorStatus() {
if (!this.state.selectedFolder || !this.state.selectedFolder.uid) return;
try {
const anchorStatus = await OfficeFolderAnchors.getInstance().get(this.state.selectedFolder.uid!);
this.setState({
isAnchored: anchorStatus.status === "VERIFIED_ON_CHAIN",
});
} catch (e) {
this.setState({
isAnchored: null,
});
}
}
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.state.selectedFolder?.uid) return;
return await OfficeFolderAnchors.getInstance().post(this.state.selectedFolder.uid);
}
private async downloadAnchoringProof(uid?: string) {
if (!uid) return;
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.state.selectedFolder?.folder_number}_${this.state.selectedFolder?.name}.pdf`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
} catch (e) {
console.error(e);
}
}
private everyDocumentValidated(): boolean {
if (!this.state.selectedFolder?.documents) return false;
return (
this.state.selectedFolder?.documents?.length >= 1 &&
this.state.selectedFolder?.documents.every((document) => document.document_status === EDocumentStatus.VALIDATED)
);
}
private async deleteFolder() {
if (!this.state.selectedFolder?.uid) return;
await Folders.getInstance().delete(this.state.selectedFolder.uid);
this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
}
private getCompletionNumber() {
const documents = this.state.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.state.selectedFolder?.customers) return false;
return this.state.selectedFolder?.customers!.length > 0;
}
private onSelectedFolder(folder: OfficeFolder): void {
this.setState({ selectedFolder: folder });
}
private openArchivedModal(): void {
if (this.everyDocumentValidated() && this.state.isAnchored) {
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.state.selectedFolder) return;
const ressourceFolder = OfficeFolder.hydrate<OfficeFolder>(this.state.selectedFolder);
ressourceFolder.archived_description = this.state.inputArchivedDescripton;
await Folders.getInstance().archive(this.state.selectedFolder.uid ?? "", ressourceFolder);
this.closeArchivedModal();
this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
}
private async getFolder(): Promise<OfficeFolder> {
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(this.props.selectedFolderUid, query);
return folder;
}
}
export default function FolderInformation(props: IProps) {
const router = useRouter();
let { folderUid } = router.query;
folderUid = folderUid as string;
return <FolderInformationClass {...props} selectedFolderUid={folderUid} router={router} />;
}