Merge branch 'dev' into staging

This commit is contained in:
Max S 2024-08-13 12:36:03 +02:00
commit 1650bdb7a5
10 changed files with 212 additions and 109 deletions

View File

@ -1,39 +1,38 @@
@import "@Themes/constants.scss"; @import "@Themes/constants.scss";
.root { .root {
display: flex; display: flex;
max-width: 357px; width: 100%;
width: 100%; gap: var(--spacing-sm, 8px);
gap: var(--spacing-sm, 8px); justify-content: space-between;
justify-content: space-between; align-items: center;
align-items: center;
.content { .content {
display: flex; display: flex;
gap: var(--spacing-sm, 8px); gap: var(--spacing-sm, 8px);
align-items: center; align-items: center;
.file-name { .file-name {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 277px; max-width: 277px;
} }
} }
svg { svg {
min-width: var(--spacing-3, 24px); min-width: var(--spacing-3, 24px);
min-height: var(--spacing-3, 24px); min-height: var(--spacing-3, 24px);
width: var(--spacing-3, 24px); width: var(--spacing-3, 24px);
height: var(--spacing-3, 24px); height: var(--spacing-3, 24px);
stroke: var(--color-primary-500); stroke: var(--color-primary-500);
} }
.error{ .error {
min-width: var(--spacing-3, 24px); min-width: var(--spacing-3, 24px);
min-height: var(--spacing-3, 24px); min-height: var(--spacing-3, 24px);
width: var(--spacing-3, 24px); width: var(--spacing-3, 24px);
height: var(--spacing-3, 24px); height: var(--spacing-3, 24px);
stroke: var(--color-error-500); stroke: var(--color-error-500);
} }
} }

View File

@ -75,4 +75,8 @@
display: block; display: block;
} }
} }
&[data-fullwidth="true"] {
width: 100%;
}
} }

View File

@ -14,9 +14,10 @@ export type ISearchBlockListProps = {
text: string; text: string;
link: string; link: string;
}; };
fullwidth?: boolean;
}; };
export default function SearchBlockList(props: ISearchBlockListProps) { export default function SearchBlockList(props: ISearchBlockListProps) {
const { blocks, onSelectedBlock, bottomButton } = props; const { blocks, onSelectedBlock, bottomButton, fullwidth = false } = props;
const [selectedBlock, setSelectedBlock] = useState<IBlock | null>(null); const [selectedBlock, setSelectedBlock] = useState<IBlock | null>(null);
const router = useRouter(); const router = useRouter();
@ -69,7 +70,7 @@ export default function SearchBlockList(props: ISearchBlockListProps) {
}, [blocks]); }, [blocks]);
return ( return (
<div className={classes["root"]}> <div className={classes["root"]} data-fullwidth={fullwidth}>
<div className={classes["searchbar"]} ref={searchBarRef}> <div className={classes["searchbar"]} ref={searchBarRef}>
<SearchBar placeholder="Chercher" onChange={handleSearchChange} /> <SearchBar placeholder="Chercher" onChange={handleSearchChange} />
{bottomButton && ( {bottomButton && (

View File

@ -7,7 +7,6 @@ type IProps = {
documentUid: string; documentUid: string;
isOpen: boolean; isOpen: boolean;
onClose?: () => void; onClose?: () => void;
onDeleteSuccess: (uid: string) => void; onDeleteSuccess: (uid: string) => void;
}; };

View File

@ -0,0 +1,36 @@
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
import Modal from "@Front/Components/DesignSystem/Modal";
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
import React, { useCallback } from "react";
type IProps = {
documentUid: string;
isOpen: boolean;
onClose?: () => void;
onDeleteSuccess: (uid: string) => void;
};
export default function DeleteSentDocumentModal(props: IProps) {
const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
const onDelete = useCallback(
() =>
Documents.getInstance()
.delete(documentUid)
.then(() => onDeleteSuccess(documentUid))
.then(onClose)
.catch((error) => console.warn(error)),
[documentUid, onClose, onDeleteSuccess],
);
return (
<Modal
isOpen={isOpen}
onClose={onClose}
title={"Supprimer lenvoi de document ?"}
firstButton={{ children: "Annuler", onClick: onClose }}
secondButton={{ children: "Oui, Supprimer", onClick: onDelete }}>
<Typography typo={ETypo.TEXT_MD_LIGHT}>Cette action annulera l'envoi du document.</Typography>
</Modal>
);
}

View File

@ -11,4 +11,10 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.actions {
display: flex;
align-items: center;
gap: var(--spacing-sm, 8px);
}
} }

View File

@ -15,31 +15,13 @@ 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 DeleteSentDocumentModal from "./DeleteSentDocumentModal";
type IProps = { type IProps = {
documents: Document[]; documents: Document[];
folderUid: string; folderUid: string;
}; };
const header: readonly IHead[] = [
{
key: "document_type",
title: "Type de document",
},
{
key: "document_status",
title: "Statut",
},
{
key: "created_at",
title: "Demandé le",
},
{
key: "actions",
title: "Actions",
},
];
const tradDocumentStatus: Record<EDocumentStatus, string> = { const tradDocumentStatus: Record<EDocumentStatus, string> = {
[EDocumentStatus.ASKED]: "Demandé", [EDocumentStatus.ASKED]: "Demandé",
[EDocumentStatus.DEPOSITED]: "À valider", [EDocumentStatus.DEPOSITED]: "À valider",
@ -52,7 +34,8 @@ export default function DocumentTables(props: IProps) {
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 deleteAskedOocumentModal = useOpenable(); const deleteAskedDocumentModal = useOpenable();
const deleteSentDocumentModal = useOpenable();
useEffect(() => { useEffect(() => {
setDocuments(documentsProps); setDocuments(documentsProps);
@ -62,9 +45,18 @@ export default function DocumentTables(props: IProps) {
(uid: string | undefined) => { (uid: string | undefined) => {
if (!uid) return; if (!uid) return;
setDocumentUid(uid); setDocumentUid(uid);
deleteAskedOocumentModal.open(); deleteAskedDocumentModal.open();
}, },
[deleteAskedOocumentModal], [deleteAskedDocumentModal],
);
const openDeleteSentDocumentModal = useCallback(
(uid: string | undefined) => {
if (!uid) return;
setDocumentUid(uid);
deleteSentDocumentModal.open();
},
[deleteSentDocumentModal],
); );
const onDownload = useCallback((doc: Document) => { const onDownload = useCallback((doc: Document) => {
@ -99,7 +91,7 @@ export default function DocumentTables(props: IProps) {
label={tradDocumentStatus[document.document_status].toUpperCase()} label={tradDocumentStatus[document.document_status].toUpperCase()}
/> />
), ),
created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_", date: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
actions: <IconButton icon={<TrashIcon onClick={() => openDeleteAskedDocumentModal(document.uid)} />} />, actions: <IconButton icon={<TrashIcon onClick={() => openDeleteAskedDocumentModal(document.uid)} />} />,
}; };
}) })
@ -122,7 +114,7 @@ export default function DocumentTables(props: IProps) {
label={tradDocumentStatus[document.document_status].toUpperCase()} label={tradDocumentStatus[document.document_status].toUpperCase()}
/> />
), ),
created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_", date: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
actions: ( actions: (
<Link <Link
href={Module.getInstance() href={Module.getInstance()
@ -153,7 +145,7 @@ export default function DocumentTables(props: IProps) {
label={tradDocumentStatus[document.document_status].toUpperCase()} label={tradDocumentStatus[document.document_status].toUpperCase()}
/> />
), ),
created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_", date: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
actions: ( actions: (
<div className={classes["actions"]}> <div className={classes["actions"]}>
<Link <Link
@ -187,7 +179,7 @@ export default function DocumentTables(props: IProps) {
label={tradDocumentStatus[document.document_status].toUpperCase()} label={tradDocumentStatus[document.document_status].toUpperCase()}
/> />
), ),
created_at: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_", date: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
actions: "", actions: "",
}; };
}) })
@ -195,6 +187,29 @@ export default function DocumentTables(props: IProps) {
[documents], [documents],
); );
//TODO: modify accordingly when the back will handle sent documents
const sentDocuments: IRowProps[] = useMemo(
() =>
documents
.map((document) => {
if (document.document_status !== "sent") return null;
return {
key: document.uid,
document_type: document.document_type?.name ?? "_",
document_status: <Tag color={ETagColor.ERROR} variant={ETagVariant.SEMI_BOLD} label={"Envoyé"} />,
date: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
actions: (
<div className={classes["actions"]}>
<IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} />
<IconButton icon={<TrashIcon onClick={() => openDeleteSentDocumentModal(document.uid)} />} />
</div>
),
};
})
.filter((document) => document !== null) as IRowProps[],
[documents, onDownload, openDeleteSentDocumentModal],
);
const progress = useMemo(() => { const progress = useMemo(() => {
const total = askedDocuments.length + toValidateDocuments.length + validatedDocuments.length + refusedDocuments.length; const total = askedDocuments.length + toValidateDocuments.length + validatedDocuments.length + refusedDocuments.length;
if (total === 0) return 0; if (total === 0) return 0;
@ -217,18 +232,48 @@ export default function DocumentTables(props: IProps) {
</Typography> </Typography>
<CircleProgress percentage={progress} /> <CircleProgress percentage={progress} />
</div> </div>
{askedDocuments.length > 0 && <Table header={header} rows={askedDocuments} />} {askedDocuments.length > 0 && <Table header={getHeader("Demandé le")} rows={askedDocuments} />}
{toValidateDocuments.length > 0 && <Table header={header} rows={toValidateDocuments} />} {toValidateDocuments.length > 0 && <Table header={getHeader("Déposé le")} rows={toValidateDocuments} />}
{validatedDocuments.length > 0 && <Table header={header} rows={validatedDocuments} />} {validatedDocuments.length > 0 && <Table header={getHeader("Validé le")} rows={validatedDocuments} />}
{refusedDocuments.length > 0 && <Table header={header} rows={refusedDocuments} />} {refusedDocuments.length > 0 && <Table header={getHeader("Demandé le")} rows={refusedDocuments} />}
{sentDocuments.length > 0 && <Table header={getHeader("Envoyé le")} rows={sentDocuments} />}
{documentUid && ( {documentUid && (
<DeleteAskedDocumentModal <>
isOpen={deleteAskedOocumentModal.isOpen} <DeleteAskedDocumentModal
onClose={deleteAskedOocumentModal.close} isOpen={deleteAskedDocumentModal.isOpen}
onDeleteSuccess={handleDelete} onClose={deleteAskedDocumentModal.close}
documentUid={documentUid} onDeleteSuccess={handleDelete}
/> documentUid={documentUid}
/>
<DeleteSentDocumentModal
isOpen={deleteSentDocumentModal.isOpen}
onClose={deleteSentDocumentModal.close}
onDeleteSuccess={handleDelete}
documentUid={documentUid}
/>
</>
)} )}
</div> </div>
); );
} }
function getHeader(dateColumnTitle: string): IHead[] {
return [
{
key: "document_type",
title: "Type de document",
},
{
key: "document_status",
title: "Statut",
},
{
key: "date",
title: dateColumnTitle,
},
{
key: "actions",
title: "Action",
},
];
}

View File

@ -62,13 +62,13 @@ export default function Folder() {
<Image src={LogoIcon} alt="logo" /> <Image src={LogoIcon} alt="logo" />
{activeUser && activeUser.contact && ( {activeUser && activeUser.contact && (
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.COLOR_PRIMARY_500}> <Typography typo={ETypo.TITLE_H1} color={ETypoColor.TEXT_ACCENT}>
Bonjour {activeUser.contact.first_name}, bienvenue sur LeCoffre.io Bonjour {activeUser.contact.first_name}, bienvenue sur LeCoffre.io
</Typography> </Typography>
)} )}
{!activeUser || {!activeUser ||
(!activeUser.contact && ( (!activeUser.contact && (
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.COLOR_PRIMARY_500}> <Typography typo={ETypo.TITLE_H1} color={ETypoColor.TEXT_ACCENT}>
Bonjour, bienvenue sur LeCoffre.io Bonjour, bienvenue sur LeCoffre.io
</Typography> </Typography>
))} ))}

View File

@ -1,17 +1,22 @@
.root { @import "@Themes/constants.scss";
position: relative;
.select-folder-container {
max-width: 530px;
padding: 80px 72px;
.root {
margin: 80px auto;
width: 472px;
display: flex;
flex-direction: column;
gap: var(--spacing-xl, 32px);
@media (max-width: $screen-s) {
width: 100%;
margin: 0;
padding: var(--spacing-md, 16px);
}
.title-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; gap: var(--spacing-sm, 8px);
gap: 48px;
margin: auto;
background-color: white;
.title {
text-align: center;
}
} }
} }

View File

@ -1,26 +1,28 @@
import backgroundImage from "@Assets/images/background_refonte.svg";
import LogoIcon from "@Assets/logo_small_blue.svg";
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography"; import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
import { OfficeFolder } from "le-coffre-resources/dist/Customer"; import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import LandingImage from "../Login/landing-connect.jpeg";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import BlockList from "@Front/Components/DesignSystem/SearchBlockList/BlockList";
export default function SelectFolder() { export default function SelectFolder() {
const [folders, setFolders] = useState<OfficeFolder[]>([]); const [folders, setFolders] = useState<OfficeFolder[]>([]);
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
async function getFolders() { const jwt = JwtService.getInstance().decodeCustomerJwt();
const jwt = JwtService.getInstance().decodeCustomerJwt(); if (!jwt) return;
if (!jwt) return;
const folders = await Folders.getInstance().get({ Folders.getInstance()
.get({
q: { q: {
where: { where: {
customers: { customers: {
@ -40,11 +42,8 @@ export default function SelectFolder() {
customers: true, customers: true,
}, },
}, },
}); })
setFolders(folders); .then((folders) => setFolders(folders));
}
getFolders();
}, []); }, []);
const handleSelectBlock = useCallback( const handleSelectBlock = useCallback(
@ -55,26 +54,35 @@ export default function SelectFolder() {
); );
return ( return (
<DefaultDoubleSidePage title="Sélectionner un dossier" image={LandingImage}> <DefaultDoubleSidePage title="Sélectionner un dossier" image={backgroundImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["select-folder-container"]}> <div className={classes["title-container"]}>
<div className={classes["title"]}> <Image src={LogoIcon} alt="logo" width={56} />
<Typography typo={ETypo.DISPLAY_LARGE}>Vos dossiers</Typography> <Typography typo={ETypo.TITLE_H1} color={ETypoColor.TEXT_ACCENT}>
</div> Vos dossiers en cours
<div className={classes["folders-container"]}> </Typography>
<BlockList
onSelectedBlock={handleSelectBlock} <Typography typo={ETypo.TITLE_H5} color={ETypoColor.TEXT_PRIMARY}>
blocks={folders.map((folder) => { Veuillez sélectionner le dossier pour lequel vous souhaitez déposer ou consulter des documents.
return { </Typography>
id: folder.uid!,
primaryText: folder.name!,
selected: false,
};
})}
/>
</div>
</div> </div>
<Typography typo={ETypo.TITLE_H6} color={ETypoColor.TEXT_ACCENT}>
Liste des dossiers disponibles :
</Typography>
<SearchBlockList blocks={getBlocks(folders)} onSelectedBlock={handleSelectBlock} fullwidth />
</div> </div>
</DefaultDoubleSidePage> </DefaultDoubleSidePage>
); );
} }
function getBlocks(folders: OfficeFolder[]): IBlock[] {
return folders.map((folder) => {
return {
id: folder.uid!,
primaryText: folder.name!,
secondaryText: folder.folder_number!,
};
});
}