Merge branch 'dev' into staging

This commit is contained in:
Max S 2024-07-23 14:23:31 +02:00
commit d6e7e973dc
28 changed files with 327 additions and 98 deletions

View File

@ -0,0 +1,22 @@
<svg width="348" height="908" viewBox="0 0 348 908" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_38_40737)">
<path opacity="0.5" d="M-138.555 -474.276L170.214 -474.276C109.669 -351.818 93.546 -207.015 135.77 -65.7741C179.425 80.2311 277.292 195.115 400.753 263.411L400.753 583.806L246.578 630.046C218.728 638.402 189.388 622.559 181.045 594.667L-138.555 -474.276Z" fill="#005BCB"/>
</g>
<g clip-path="url(#clip1_38_40737)">
<g clip-path="url(#clip2_38_40737)">
<path d="M410.131 204.837L590.401 385.106C483.532 421.279 389.548 496.437 331.709 603.579C271.923 714.34 261.964 838.575 294.157 950.543L107.033 1137.67L-9.98578 1074.66C-31.126 1063.28 -39.0026 1036.9 -27.5838 1015.74L410.131 204.837Z" stroke="#005BCB" stroke-width="2" stroke-miterlimit="10"/>
</g>
</g>
<defs>
<clipPath id="clip0_38_40737">
<rect width="653.982" height="348" fill="white" transform="translate(3.05176e-05 653.981) rotate(-90)"/>
</clipPath>
<clipPath id="clip1_38_40737">
<rect width="348" height="626.271" fill="white" transform="translate(3.05176e-05 281.729)"/>
</clipPath>
<clipPath id="clip2_38_40737">
<rect width="796.226" height="435.126" fill="white" transform="translate(-198 811.836) rotate(-45)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -83,4 +83,8 @@
stroke: var(--alerts-badge-contrast-neutral); stroke: var(--alerts-badge-contrast-neutral);
} }
} }
&.fullwidth {
width: 100%;
}
} }

View File

@ -16,6 +16,7 @@ type IProps = {
firstButton?: IButtonProps; firstButton?: IButtonProps;
secondButton?: IButtonProps; secondButton?: IButtonProps;
closeButton?: boolean; closeButton?: boolean;
fullWidth?: boolean;
}; };
export enum EAlertVariant { export enum EAlertVariant {
@ -36,12 +37,12 @@ const variantButtonMap: Record<EAlertVariant, EButtonVariant> = {
export default function Alert(props: IProps) { export default function Alert(props: IProps) {
const { isOpen, close } = useOpenable({ defaultOpen: true }); const { isOpen, close } = useOpenable({ defaultOpen: true });
const { variant = EAlertVariant.INFO, title, description, firstButton, secondButton, closeButton, icon } = props; const { variant = EAlertVariant.INFO, title, description, firstButton, secondButton, closeButton, icon, fullWidth } = props;
if (!isOpen) return null; if (!isOpen) return null;
return ( return (
<div className={classNames(classes["root"], classes[variant])}> <div className={classNames(classes["root"], classes[variant], fullWidth && classes["fullwidth"])}>
<span className={classes["icon"]}>{icon ?? <InformationCircleIcon />}</span> <span className={classes["icon"]}>{icon ?? <InformationCircleIcon />}</span>
<div className={classes["content"]}> <div className={classes["content"]}>
<div className={classes["text-container"]}> <div className={classes["text-container"]}>

View File

@ -13,6 +13,14 @@
text-align: center; text-align: center;
.text {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: var(--spacing-md, 16px);
}
svg { svg {
width: 32px; width: 32px;
stroke: var(--primary-weak-contrast); stroke: var(--primary-weak-contrast);

View File

@ -16,12 +16,14 @@ export default function EmptyAlert(props: IProps) {
return ( return (
<div className={classes["root"]}> <div className={classes["root"]}>
{icon} {icon}
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} color={ETypoColor.COLOR_NEUTRAL_950}> <div className={classes["text"]}>
{title} <Typography typo={ETypo.TEXT_LG_SEMIBOLD} color={ETypoColor.COLOR_NEUTRAL_950}>
</Typography> {title}
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}> </Typography>
{description} <Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}>
</Typography> {description}
</Typography>
</div>
{footer} {footer}
</div> </div>
); );

View File

@ -11,6 +11,7 @@
} }
.root { .root {
position: relative;
.content { .content {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
@ -114,4 +115,19 @@
} }
} }
} }
.background-image-container {
position: fixed;
top: 0;
right: 0;
@media (max-width: $screen-l) {
display: none;
}
.background-image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
} }

View File

@ -1,20 +1,20 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders, { IGetFoldersParams } from "@Front/Api/LeCoffreApi/Notary/Folders/Folders"; import Folders, { IGetFoldersParams } from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderListContainer from "@Front/Components/DesignSystem/FolderListContainer";
import FolderArchivedListContainer from "@Front/Components/DesignSystem/FolderArchivedListContainer"; import FolderArchivedListContainer from "@Front/Components/DesignSystem/FolderArchivedListContainer";
import FolderListContainer from "@Front/Components/DesignSystem/FolderListContainer";
import Header from "@Front/Components/DesignSystem/Header"; import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version"; import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore"; import WindowStore from "@Front/Stores/WindowStore";
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
import classNames from "classnames"; import classNames from "classnames";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus"; import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import Image from "next/image"; import Image, { StaticImageData } from "next/image";
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
type IProps = { type IProps = {
title: string; title: string;
@ -24,6 +24,7 @@ type IProps = {
hasBackArrow: boolean; hasBackArrow: boolean;
backArrowUrl?: string; backArrowUrl?: string;
mobileBackText?: string; mobileBackText?: string;
image?: StaticImageData;
}; };
type IState = { type IState = {
folders: OfficeFolder[] | null; folders: OfficeFolder[] | null;
@ -96,6 +97,11 @@ export default class DefaultNotaryDashboard extends React.Component<IProps, ISta
)} )}
{this.props.children} {this.props.children}
</div> </div>
{this.props.image && (
<div className={classes["background-image-container"]}>
<Image alt={"right side image"} src={this.props.image} className={classes["background-image"]} priority />
</div>
)}
</div> </div>
<Version /> <Version />
</div> </div>

View File

@ -7,21 +7,15 @@ type IProps = {
isOpen: boolean; isOpen: boolean;
onClose?: () => void; onClose?: () => void;
onDeleteSuccess: (uid: string) => void; onDelete: (customerUid: string) => void;
}; };
export default function DeleteCustomerModal(props: IProps) { export default function DeleteCustomerModal(props: IProps) {
const { isOpen, onClose, customerUid, onDeleteSuccess } = props; const { isOpen, onClose, onDelete } = props;
const onDelete = useCallback( const handleDelete = useCallback(() => {
() => onDeleteSuccess(customerUid), onDelete(props.customerUid);
// Documents.getInstance() }, [onDelete, props.customerUid]);
// .delete(documentUid)
// .then(() => onDeleteSuccess(documentUid))
// .then(onClose)
// .catch((error) => console.warn(error)),
[customerUid, onDeleteSuccess],
);
return ( return (
<Modal <Modal
@ -29,7 +23,7 @@ export default function DeleteCustomerModal(props: IProps) {
onClose={onClose} onClose={onClose}
title={"Êtes-vous sûr de vouloir supprimer ce client du dossier ?"} title={"Êtes-vous sûr de vouloir supprimer ce client du dossier ?"}
firstButton={{ children: "Annuler", onClick: onClose }} firstButton={{ children: "Annuler", onClick: onClose }}
secondButton={{ children: "Supprimer le client", onClick: onDelete }}> secondButton={{ children: "Supprimer le client", onClick: handleDelete }}>
<Typography typo={ETypo.TEXT_MD_light}> <Typography typo={ETypo.TEXT_MD_light}>
Cette action retirera le client de ce dossier. Vous ne pourrez plus récupérer les informations associées à ce client dans ce 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. dossier une fois supprimées.

View File

@ -9,10 +9,14 @@
background: var(--primary-weak-higlight, #e5eefa); background: var(--primary-weak-higlight, #e5eefa);
min-width: 300px; min-width: 300px;
.header{ .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 100%; width: 100%;
} }
.delete-button {
margin: auto;
}
} }

View File

@ -1,4 +1,4 @@
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton"; import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import useOpenable from "@Front/Hooks/useOpenable"; import useOpenable from "@Front/Hooks/useOpenable";
@ -8,33 +8,53 @@ import { ICustomer } from "..";
import { AnchorStatus } from "../.."; import { AnchorStatus } from "../..";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import DeleteCustomerModal from "./DeleteCustomerModal"; import DeleteCustomerModal from "./DeleteCustomerModal";
import Link from "next/link";
import Module from "@Front/Config/Module";
import { useCallback } from "react";
import { Note } from "le-coffre-resources/dist/Customer";
type IProps = { type IProps = {
customer: ICustomer; customer: ICustomer;
anchorStatus: AnchorStatus; anchorStatus: AnchorStatus;
folderUid: string | undefined;
customerNote: Note | null;
onDelete: (customerUid: string) => void;
}; };
export default function ClientBox(props: IProps) { export default function ClientBox(props: IProps) {
const { customer, anchorStatus } = props; const { customer, anchorStatus, folderUid, customerNote } = props;
const { isOpen, open, close } = useOpenable(); const { isOpen, open, close } = useOpenable();
const handleDelete = useCallback(
(customerUid: string) => {
props.onDelete(customerUid);
},
[props],
);
return ( return (
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["header"]}> <div className={classes["header"]}>
<Typography typo={ETypo.TEXT_LG_BOLD} color={ETypoColor.COLOR_PRIMARY_500}> <Typography typo={ETypo.TEXT_LG_BOLD} color={ETypoColor.COLOR_PRIMARY_500}>
{customer.contact?.last_name} {customer.contact?.first_name} {customer.contact?.last_name}
</Typography> </Typography>
{anchorStatus === AnchorStatus.NOT_ANCHORED && ( {anchorStatus === AnchorStatus.NOT_ANCHORED && (
<IconButton variant={EIconButtonVariant.NEUTRAL} icon={<PencilSquareIcon />} /> <Link
href={Module.getInstance()
.get()
.modules.pages.Folder.pages.EditClient.props.path.replace("[folderUid]", folderUid ?? "")
.replace("[customerUid]", customer.uid ?? "")}>
<IconButton variant={EIconButtonVariant.NEUTRAL} icon={<PencilSquareIcon />} />
</Link>
)} )}
</div> </div>
<div> <div>
<Typography typo={ETypo.TEXT_LG_BOLD} color={ETypoColor.COLOR_NEUTRAL_700}> <Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}>
Numéro de téléphone Numéro de téléphone
</Typography> </Typography>
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_PRIMARY_500}> <Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}>
{customer.contact?.phone_number} {customer.contact?.cell_phone_number ?? customer.contact?.phone_number ?? "_"}
</Typography> </Typography>
</div> </div>
<div> <div>
@ -42,7 +62,7 @@ export default function ClientBox(props: IProps) {
E-mail E-mail
</Typography> </Typography>
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}> <Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}>
{customer.contact?.email} {customer.contact?.email ?? "_"}
</Typography> </Typography>
</div> </div>
<div> <div>
@ -50,7 +70,7 @@ export default function ClientBox(props: IProps) {
Note client Note client
</Typography> </Typography>
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}> <Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}>
{customer.notes?.[0]?.content ?? "_"} {customerNote?.content ?? "-"}
</Typography> </Typography>
</div> </div>
@ -58,13 +78,14 @@ export default function ClientBox(props: IProps) {
<> <>
<Button <Button
className={classes["delete-button"]} className={classes["delete-button"]}
size={EButtonSize.SM}
variant={EButtonVariant.ERROR} variant={EButtonVariant.ERROR}
styletype={EButtonstyletype.TEXT} styletype={EButtonstyletype.TEXT}
rightIcon={<TrashIcon />} rightIcon={<TrashIcon />}
onClick={open}> onClick={open}>
Supprimer le client Supprimer le client
</Button> </Button>
<DeleteCustomerModal isOpen={isOpen} onClose={close} customerUid={customer.uid ?? ""} onDeleteSuccess={() => {}} /> <DeleteCustomerModal isOpen={isOpen} onClose={close} customerUid={customer.uid ?? ""} onDelete={handleDelete} />
</> </>
)} )}
</div> </div>

View File

@ -5,18 +5,20 @@ import Table from "@Front/Components/DesignSystem/Table";
import { IHead, IRowProps } from "@Front/Components/DesignSystem/Table/MuiTable"; import { IHead, IRowProps } from "@Front/Components/DesignSystem/Table/MuiTable";
import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag"; import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import Module from "@Front/Config/Module";
import useOpenable from "@Front/Hooks/useOpenable"; import useOpenable from "@Front/Hooks/useOpenable";
import { ArrowDownTrayIcon, EyeIcon, TrashIcon } from "@heroicons/react/24/outline"; import { ArrowDownTrayIcon, EyeIcon, TrashIcon } from "@heroicons/react/24/outline";
import { Document, File } from "le-coffre-resources/dist/Customer"; import { Document } from "le-coffre-resources/dist/Customer";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import DeleteAskedDocumentModal from "./DeleteAskedDocumentModal"; import DeleteAskedDocumentModal from "./DeleteAskedDocumentModal";
import FilePreviewModal from "./FilePreviewModal";
type IProps = { type IProps = {
documents: Document[]; documents: Document[];
folderUid: string;
}; };
const header: readonly IHead[] = [ const header: readonly IHead[] = [
@ -46,11 +48,9 @@ const tradDocumentStatus: Record<EDocumentStatus, string> = {
}; };
export default function DocumentTables(props: IProps) { export default function DocumentTables(props: IProps) {
const { documents: documentsProps } = props; const { documents: documentsProps, folderUid } = props;
const [documents, setDocuments] = useState<Document[]>(documentsProps); const [documents, setDocuments] = useState<Document[]>(documentsProps);
const [documentUid, setDocumentUid] = useState<string | null>(null); const [documentUid, setDocumentUid] = useState<string | null>(null);
const previewModal = useOpenable();
const [file, setFile] = useState<{ file: File; blob: Blob } | null>(null);
const deleteAskedOocumentModal = useOpenable(); const deleteAskedOocumentModal = useOpenable();
@ -67,19 +67,6 @@ export default function DocumentTables(props: IProps) {
[deleteAskedOocumentModal], [deleteAskedOocumentModal],
); );
const onPreview = useCallback(
(document: Document) => {
const file = document.files?.[0];
if (!file || !file?.uid) return;
return Files.getInstance()
.download(file.uid)
.then((blob) => setFile({ file, blob }))
.then(() => previewModal.open())
.catch((e) => console.warn(e));
},
[previewModal],
);
const onDownload = useCallback((doc: Document) => { const onDownload = useCallback((doc: Document) => {
const file = doc.files?.[0]; const file = doc.files?.[0];
if (!file || !file?.uid) return; if (!file || !file?.uid) return;
@ -136,11 +123,19 @@ export default function DocumentTables(props: IProps) {
/> />
), ),
created_at: document.created_at, created_at: document.created_at,
actions: <IconButton onClick={() => onPreview(document)} icon={<EyeIcon />} />, actions: (
<Link
href={Module.getInstance()
.get()
.modules.pages.Folder.pages.ViewDocuments.props.path.replace("[folderUid]", folderUid)
.replace("[documentUid]", document.uid ?? "")}>
<IconButton icon={<EyeIcon />} />
</Link>
),
}; };
}) })
.filter((document) => document !== null) as IRowProps[], .filter((document) => document !== null) as IRowProps[],
[documents, onPreview], [documents, folderUid],
); );
const validatedDocuments: IRowProps[] = useMemo( const validatedDocuments: IRowProps[] = useMemo(
@ -161,14 +156,20 @@ export default function DocumentTables(props: IProps) {
created_at: document.created_at, created_at: document.created_at,
actions: ( actions: (
<div className={classes["actions"]}> <div className={classes["actions"]}>
<IconButton onClick={() => onPreview(document)} icon={<EyeIcon />} /> <Link
href={Module.getInstance()
.get()
.modules.pages.Folder.pages.ViewDocuments.props.path.replace("[folderUid]", folderUid)
.replace("[documentUid]", document.uid ?? "")}>
<IconButton icon={<EyeIcon />} />
</Link>
<IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} /> <IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} />
</div> </div>
), ),
}; };
}) })
.filter((document) => document !== null) as IRowProps[], .filter((document) => document !== null) as IRowProps[],
[documents, onDownload, onPreview], [documents, folderUid, onDownload],
); );
const refusedDocuments: IRowProps[] = useMemo( const refusedDocuments: IRowProps[] = useMemo(
@ -208,7 +209,7 @@ export default function DocumentTables(props: IProps) {
</Typography> </Typography>
<CircleProgress percentage={progress} /> <CircleProgress percentage={progress} />
</div> </div>
<Table header={header} rows={askedDocuments} /> {askedDocuments.length > 0 && <Table header={header} rows={askedDocuments} />}
{toValidateDocuments.length > 0 && <Table header={header} rows={toValidateDocuments} />} {toValidateDocuments.length > 0 && <Table header={header} rows={toValidateDocuments} />}
{validatedDocuments.length > 0 && <Table header={header} rows={validatedDocuments} />} {validatedDocuments.length > 0 && <Table header={header} rows={validatedDocuments} />}
{refusedDocuments.length > 0 && <Table header={header} rows={refusedDocuments} />} {refusedDocuments.length > 0 && <Table header={header} rows={refusedDocuments} />}
@ -220,14 +221,6 @@ export default function DocumentTables(props: IProps) {
documentUid={documentUid} documentUid={documentUid}
/> />
)} )}
{file && (
<FilePreviewModal
isOpen={previewModal.isOpen}
onClose={previewModal.close}
file={file.file}
url={URL.createObjectURL(file.blob)}
/>
)}
</div> </div>
); );
} }

View File

@ -10,6 +10,10 @@
gap: var(--spacing-md, 16px); gap: var(--spacing-md, 16px);
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.tabs {
width: calc(100% - 210px);
}
} }
.content { .content {

View File

@ -2,7 +2,7 @@ import Tabs from "@Front/Components/Elements/Tabs";
import Customer from "le-coffre-resources/dist/Customer"; import Customer from "le-coffre-resources/dist/Customer";
import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import { useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { AnchorStatus } from ".."; import { AnchorStatus } from "..";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
@ -13,6 +13,7 @@ import Module from "@Front/Config/Module";
import Link from "next/link"; import Link from "next/link";
import NoDocument from "./NoDocument"; import NoDocument from "./NoDocument";
import DocumentTables from "./DocumentTables"; import DocumentTables from "./DocumentTables";
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
type IProps = { type IProps = {
folder: OfficeFolder; folder: OfficeFolder;
@ -47,10 +48,26 @@ export default function ClientView(props: IProps) {
const doesCustomerHaveDocument = useMemo(() => customer.documents && customer.documents.length > 0, [customer]); 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<OfficeFolder>({
...folder,
customers: folder.customers?.filter((customer) => customer.uid !== customerUid),
}),
);
window.location.reload();
},
[folder],
);
return ( return (
<section className={classes["root"]}> <section className={classes["root"]}>
<div className={classes["tab-container"]}> <div className={classes["tab-container"]}>
{tabs && <Tabs<ICustomer> tabs={tabs} onSelect={setCustomer} />} <div className={classes["tabs"]}>{tabs && <Tabs<ICustomer> tabs={tabs} onSelect={setCustomer} />}</div>
{anchorStatus === AnchorStatus.NOT_ANCHORED && ( {anchorStatus === AnchorStatus.NOT_ANCHORED && (
<Link <Link
href={Module.getInstance() href={Module.getInstance()
@ -68,7 +85,13 @@ export default function ClientView(props: IProps) {
</div> </div>
<div className={classes["content"]}> <div className={classes["content"]}>
<div className={classes["client-box"]}> <div className={classes["client-box"]}>
<ClientBox customer={customer} anchorStatus={anchorStatus} /> <ClientBox
customer={customer}
anchorStatus={anchorStatus}
folderUid={folder.uid}
onDelete={handleClientDelete}
customerNote={folder.notes!.find((value) => value.customer?.uid === customer.uid) ?? null}
/>
{anchorStatus === AnchorStatus.NOT_ANCHORED && ( {anchorStatus === AnchorStatus.NOT_ANCHORED && (
<Link <Link
href={Module.getInstance() href={Module.getInstance()
@ -81,7 +104,11 @@ export default function ClientView(props: IProps) {
</Link> </Link>
)} )}
</div> </div>
{doesCustomerHaveDocument ? <DocumentTables documents={customer.documents ?? []}/> : <NoDocument />} {doesCustomerHaveDocument ? (
<DocumentTables documents={customer.documents ?? []} folderUid={folder?.uid ?? ""} />
) : (
<NoDocument />
)}
</div> </div>
</section> </section>
); );

View File

@ -5,7 +5,7 @@
gap: var(--spacing-lg, 40px); gap: var(--spacing-lg, 40px);
.info-box1 { .info-box1 {
display: flex; display: flex;
width: 648px; width: 100%;
flex-direction: column; flex-direction: column;
gap: var(--spacing-sm, 8px); gap: var(--spacing-sm, 8px);
@ -19,7 +19,11 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--spacing-lg, 24px); gap: var(--spacing-lg, 24px);
width: 100%;
max-width: 400px;
.progress-container { .progress-container {
width: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@ -29,6 +33,13 @@
gap: var(--spacing-md, 8px); gap: var(--spacing-md, 8px);
} }
} }
.description-container {
.text {
max-height: 60px;
overflow-y: auto;
}
}
} }
.separator { .separator {

View File

@ -76,11 +76,13 @@ export default function InformationSection(props: IProps) {
)} )}
</div> </div>
</div> </div>
<div> <div className={classes["description-container"]}>
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}> <Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}>
Notre dossier Note du dossier
</Typography>
<Typography typo={ETypo.TEXT_LG_REGULAR} className={classes["text"]}>
{folder?.description}
</Typography> </Typography>
<Typography typo={ETypo.TEXT_LG_REGULAR}>Travaux de rénovation en cours. </Typography>
</div> </div>
</div> </div>
</section> </section>

View File

@ -1,4 +1,4 @@
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import EmptyAlert from "@Front/Components/DesignSystem/EmptyAlert"; import EmptyAlert from "@Front/Components/DesignSystem/EmptyAlert";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { UserPlusIcon } from "@heroicons/react/24/outline"; import { UserPlusIcon } from "@heroicons/react/24/outline";
@ -24,7 +24,7 @@ export default function AddClientSection(props: IProps) {
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." 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={ footer={
<Link href={addClientPath}> <Link href={addClientPath}>
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.OUTLINED}> <Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.OUTLINED} size={EButtonSize.MD}>
Ajouter un client Ajouter un client
</Button> </Button>
</Link> </Link>

View File

@ -1,4 +1,4 @@
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import { TrashIcon } from "@heroicons/react/24/outline"; import { TrashIcon } from "@heroicons/react/24/outline";
import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import { useMemo } from "react"; import { useMemo } from "react";
@ -30,7 +30,8 @@ export default function NoClientView(props: IProps) {
variant={EButtonVariant.ERROR} variant={EButtonVariant.ERROR}
styletype={EButtonstyletype.TEXT} styletype={EButtonstyletype.TEXT}
rightIcon={<TrashIcon />} rightIcon={<TrashIcon />}
onClick={deleteFolderModal.open}> onClick={deleteFolderModal.open}
size={EButtonSize.SM}>
Supprimer le dossier Supprimer le dossier
</Button> </Button>
<DeleteFolderModal isOpen={deleteFolderModal.isOpen} onClose={deleteFolderModal.close} folder={folder} /> <DeleteFolderModal isOpen={deleteFolderModal.isOpen} onClose={deleteFolderModal.close} folder={folder} />

View File

@ -19,6 +19,7 @@ export default function AnchoringAlertInfo(props: IProps) {
onClick: onAnchor, onClick: onAnchor,
}} }}
variant={EAlertVariant.INFO} variant={EAlertVariant.INFO}
fullWidth
/> />
); );
} }

View File

@ -30,6 +30,7 @@ export default function AnchoringAlertSuccess(props: IProps) {
} }
variant={EAlertVariant.SUCCESS} variant={EAlertVariant.SUCCESS}
icon={<CheckIcon />} icon={<CheckIcon />}
fullWidth
/> />
); );
} }

View File

@ -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 (
<Alert
title="Dossier archivé"
description="Ce dossier a été archivé et ne peut plus être modifié. Vous pouvez télécharger la preuve d'ancrage pour vos archives ou restaurer le dossier si nécessaire."
firstButton={{
children: "Télécharger la preuve dancrage",
styletype: EButtonstyletype.CONTAINED,
rightIcon: <ArrowDownOnSquareIcon />,
onClick: onDownloadAnchoringProof,
}}
secondButton={{
children: "Restaurer le dossier",
onClick: restoreArchive,
rightIcon: <ArchiveBoxArrowDownIcon />,
}}
variant={EAlertVariant.WARNING}
icon={<ArchiveBoxIcon />}
fullWidth
/>
);
}

View File

@ -18,7 +18,6 @@ export default function ArchiveModal(props: IProps) {
const router = useRouter(); const router = useRouter();
const archive = useCallback(() => { const archive = useCallback(() => {
if (!folderUid) return;
const description = (document.querySelector("textarea[name='archived_description']") as HTMLTextAreaElement).value ?? ""; const description = (document.querySelector("textarea[name='archived_description']") as HTMLTextAreaElement).value ?? "";
Folders.getInstance() Folders.getInstance()

View File

@ -2,6 +2,7 @@ import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors"; import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
import Loader from "@Front/Components/DesignSystem/Loader"; import Loader from "@Front/Components/DesignSystem/Loader";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import useOpenable from "@Front/Hooks/useOpenable";
import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document"; import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
@ -9,15 +10,15 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import ClientView from "./ClientView"; import ClientView from "./ClientView";
import InformationSection from "./InformationSection";
import NoClientView from "./NoClientView";
import AnchoringAlertInfo from "./elements/AnchoringAlertInfo"; import AnchoringAlertInfo from "./elements/AnchoringAlertInfo";
import AnchoringModal from "./elements/AnchoringModal";
import useOpenable from "@Front/Hooks/useOpenable";
import AnchoringAlertSuccess from "./elements/AnchoringAlertSuccess"; import AnchoringAlertSuccess from "./elements/AnchoringAlertSuccess";
import AnchoringModal from "./elements/AnchoringModal";
import ArchiveAlertWarning from "./elements/ArchiveAlertWarning";
import ArchiveModal from "./elements/ArchiveModal";
import DownloadAnchoringProofModal from "./elements/DownloadAnchoringProofModal"; import DownloadAnchoringProofModal from "./elements/DownloadAnchoringProofModal";
import RequireAnchoringModal from "./elements/RequireAnchoringModal"; import RequireAnchoringModal from "./elements/RequireAnchoringModal";
import ArchiveModal from "./elements/ArchiveModal"; import InformationSection from "./InformationSection";
import NoClientView from "./NoClientView";
export enum AnchorStatus { export enum AnchorStatus {
"VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN", "VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
@ -138,13 +139,16 @@ export default function FolderInformation(props: IProps) {
{progress === 100 && anchorStatus === AnchorStatus.NOT_ANCHORED && ( {progress === 100 && anchorStatus === AnchorStatus.NOT_ANCHORED && (
<AnchoringAlertInfo onAnchor={anchoringModal.open} /> <AnchoringAlertInfo onAnchor={anchoringModal.open} />
)} )}
{anchorStatus === AnchorStatus.VERIFIED_ON_CHAIN && ( {!isArchived && anchorStatus === AnchorStatus.VERIFIED_ON_CHAIN && (
<AnchoringAlertSuccess <AnchoringAlertSuccess
onDownloadAnchoringProof={downloadAnchoringProofModal.open} onDownloadAnchoringProof={downloadAnchoringProofModal.open}
onArchive={archiveModal.open} onArchive={archiveModal.open}
isArchived={isArchived} isArchived={isArchived}
/> />
)} )}
{isArchived && folderUid && (
<ArchiveAlertWarning folderUid={folderUid} onDownloadAnchoringProof={downloadAnchoringProofModal.open} />
)}
{folder && !doesFolderHaveClient && <NoClientView folder={folder} anchorStatus={anchorStatus} />} {folder && !doesFolderHaveClient && <NoClientView folder={folder} anchorStatus={anchorStatus} />}
{folder && doesFolderHaveClient && <ClientView folder={folder} anchorStatus={anchorStatus} />} {folder && doesFolderHaveClient && <ClientView folder={folder} anchorStatus={anchorStatus} />}
{folderUid && anchorStatus === AnchorStatus.NOT_ANCHORED && ( {folderUid && anchorStatus === AnchorStatus.NOT_ANCHORED && (

View File

@ -105,9 +105,11 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
)} )}
</div> </div>
<div className={classes["footer"]}> <div className={classes["footer"]}>
<div className={classes["alert"]}> {this.state.document?.document_status === EDocumentStatus.DEPOSITED && (
<MessageBox type={"info"}>Veuillez valider le document afin de pouvoir le télécharger.</MessageBox> <div className={classes["alert"]}>
</div> <MessageBox type={"info"}>Veuillez valider le document afin de pouvoir le télécharger.</MessageBox>
</div>
)}
{/* {this.state.document?.document_type?.name === "Document d'identité" && ( {/* {this.state.document?.document_type?.name === "Document d'identité" && (
<div className={classes["ocr-container"]}> <div className={classes["ocr-container"]}>
<OcrResult percentage={this.state.validatedPercentage} /> <OcrResult percentage={this.state.validatedPercentage} />

View File

@ -1,34 +1,64 @@
import backgroundImage from "@Assets/images/background_refonte_reverse.svg";
import LogoIcon from "@Assets/logo_small_blue.svg"; import LogoIcon from "@Assets/logo_small_blue.svg";
import Button, { EButtonVariant, EButtonstyletype } from "@Front/Components/DesignSystem/Button"; import Users from "@Front/Api/LeCoffreApi/Notary/Users/Users";
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Module from "@Front/Config/Module";
import JwtService from "@Front/Services/JwtService/JwtService";
import { DocumentIcon } from "@heroicons/react/24/outline"; import { DocumentIcon } from "@heroicons/react/24/outline";
import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import User, { OfficeFolder } from "le-coffre-resources/dist/Notary";
import { useCallback, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Link from "next/link";
import Module from "@Front/Config/Module";
export default function Folder() { export default function Folder() {
const [_folder, setFolder] = useState<OfficeFolder | null>(null); const [_folder, setFolder] = useState<OfficeFolder | null>(null);
const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true); const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true);
const [activeUser, setActiveUser] = useState<User | null>();
const onSelectedFolder = useCallback((folder: OfficeFolder): void => { const onSelectedFolder = useCallback((folder: OfficeFolder): void => {
setFolder(folder); setFolder(folder);
}, []); }, []);
useEffect(() => {
const decodedJwt = JwtService.getInstance().decodeJwt();
if (!decodedJwt) return;
Users.getInstance()
.getByUid(decodedJwt.userId, {
q: {
contact: true,
},
})
.then((user) => {
setActiveUser(user);
});
}, []);
return ( return (
<DefaultNotaryDashboard title={"Dossier"} onSelectedFolder={onSelectedFolder} mobileBackText={"Liste des dossiers"}> <DefaultNotaryDashboard
title={"Dossier"}
onSelectedFolder={onSelectedFolder}
mobileBackText={"Liste des dossiers"}
image={backgroundImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["content"]}> <div className={classes["content"]}>
<div className={classes["title-container"]}> <div className={classes["title-container"]}>
<Image src={LogoIcon} alt="logo" /> <Image src={LogoIcon} alt="logo" />
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.COLOR_PRIMARY_500}> {activeUser && activeUser.contact && (
Bonjour John, bienvenue sur LeCoffre.io <Typography typo={ETypo.TITLE_H1} color={ETypoColor.COLOR_PRIMARY_500}>
</Typography> Bonjour {activeUser.contact.first_name}, bienvenue sur LeCoffre.io
</Typography>
)}
{!activeUser ||
(!activeUser.contact && (
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.COLOR_PRIMARY_500}>
Bonjour, bienvenue sur LeCoffre.io
</Typography>
))}
<Typography typo={ETypo.TEXT_LG_REGULAR}> <Typography typo={ETypo.TEXT_LG_REGULAR}>
Commencez par créer votre{" "} Commencez par créer votre{" "}
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} type="span"> <Typography typo={ETypo.TEXT_LG_SEMIBOLD} type="span">
@ -56,7 +86,7 @@ export default function Folder() {
<div className={classes["box"]}> <div className={classes["box"]}>
<Typography typo={ETypo.TEXT_MD_SEMIBOLD}>Besoin d'aide ?</Typography> <Typography typo={ETypo.TEXT_MD_SEMIBOLD}>Besoin d'aide ?</Typography>
<Typography typo={ETypo.TEXT_MD_REGULAR}>Consultez nos guides pour bien démarrer.</Typography> <Typography typo={ETypo.TEXT_MD_REGULAR}>Consultez nos guides pour bien démarrer.</Typography>
<Button variant={EButtonVariant.WARNING} styletype={EButtonstyletype.TEXT}> <Button variant={EButtonVariant.SECONDARY} styletype={EButtonstyletype.TEXT} size={EButtonSize.MD}>
Accéder aux guides Accéder aux guides
</Button> </Button>
</div> </div>
@ -65,7 +95,7 @@ export default function Folder() {
<div className={classes["box"]}> <div className={classes["box"]}>
<Typography typo={ETypo.TEXT_MD_SEMIBOLD}>Vous avez des questions ?</Typography> <Typography typo={ETypo.TEXT_MD_SEMIBOLD}>Vous avez des questions ?</Typography>
<Typography typo={ETypo.TEXT_MD_REGULAR}>Notre équipe de support est pour vous aider.</Typography> <Typography typo={ETypo.TEXT_MD_REGULAR}>Notre équipe de support est pour vous aider.</Typography>
<Button variant={EButtonVariant.WARNING} styletype={EButtonstyletype.TEXT}> <Button variant={EButtonVariant.SECONDARY} styletype={EButtonstyletype.TEXT} size={EButtonSize.MD}>
Contactez le support Contactez le support
</Button> </Button>
</div> </div>

View File

@ -66,6 +66,13 @@
"labelKey": "ask_documents" "labelKey": "ask_documents"
} }
}, },
"ViewDocuments": {
"enabled": true,
"props": {
"path": "/folders/[folderUid]/documents/[documentUid]",
"labelKey": "ask_documents"
}
},
"EditDescription": { "EditDescription": {
"enabled": true, "enabled": true,
"props": { "props": {

View File

@ -66,6 +66,13 @@
"labelKey": "ask_documents" "labelKey": "ask_documents"
} }
}, },
"ViewDocuments": {
"enabled": true,
"props": {
"path": "/folders/[folderUid]/documents/[documentUid]",
"labelKey": "ask_documents"
}
},
"EditDescription": { "EditDescription": {
"enabled": true, "enabled": true,
"props": { "props": {

View File

@ -66,6 +66,13 @@
"labelKey": "ask_documents" "labelKey": "ask_documents"
} }
}, },
"ViewDocuments": {
"enabled": true,
"props": {
"path": "/folders/[folderUid]/documents/[documentUid]",
"labelKey": "ask_documents"
}
},
"EditDescription": { "EditDescription": {
"enabled": true, "enabled": true,
"props": { "props": {

View File

@ -66,6 +66,13 @@
"labelKey": "ask_documents" "labelKey": "ask_documents"
} }
}, },
"ViewDocuments": {
"enabled": true,
"props": {
"path": "/folders/[folderUid]/documents/[documentUid]",
"labelKey": "ask_documents"
}
},
"EditDescription": { "EditDescription": {
"enabled": true, "enabled": true,
"props": { "props": {