DefaultNotaryDashboard done, front services init

This commit is contained in:
Hugo Lextrait 2023-04-12 17:42:32 +02:00
parent 5c1eb01aa2
commit 48cc9e23b5
35 changed files with 580 additions and 170 deletions

View File

@ -1,3 +1,6 @@
NEXT_PUBLIC_API_URL= BACK_API_PROTOCOL=
NEXT_PUBLIC_RPC_GATEWAY_MAINNET_URL= BACK_API_HOSTNAME=
NEXT_PUBLIC_RPC_GATEWAY_TESTNET_URL= BACK_API_PORT=
BACK_API_ROOT_URL=
BACK_API_VERSION=

View File

@ -1,12 +1,15 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
publicRuntimeConfig: {
// Will be available on both server and client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_RPC_GATEWAY_MAINNET_URL: process.env.NEXT_PUBLIC_RPC_GATEWAY_MAINNET_URL,
NEXT_PUBLIC_RPC_GATEWAY_TESTNET_URL: process.env.NEXT_PUBLIC_RPC_GATEWAY_TESTNET_URL,
},
}
module.exports = nextConfig const nextConfig = {
reactStrictMode: false,
publicRuntimeConfig: {
// Will be available on both server and client
BACK_API_PROTOCOL: process.env.BACK_API_PROTOCOL,
BACK_API_HOST: process.env.BACK_API_HOST,
BACK_API_PORT: process.env.BACK_API_PORT,
BACK_API_ROOT_URL: process.env.BACK_API_ROOT_URL,
BACK_API_VERSION: process.env.BACK_API_VERSION,
},
};
module.exports = nextConfig;

View File

@ -1,21 +1,26 @@
import { FrontendVariables } from "@Front/Config/VariablesFront";
export enum ContentType { export enum ContentType {
JSON = "application/json", JSON = "application/json",
FORM_DATA = "multipart/form-data;", FORM_DATA = "multipart/form-data;",
} }
export default abstract class BaseApiService { export default abstract class BaseApiService {
protected readonly backUrl = private static baseUrl: string;
process.env["NEXT_PUBLIC_API_HOSTNAME"] + protected readonly variables = FrontendVariables.getInstance();
":" +
process.env["NEXT_PUBLIC_API_PORT"] +
process.env["NEXT_PUBLIC_API_ROOT_URL"];
protected readonly proxyUrl =
process.env["NEXT_PUBLIC_RPC_GATEWAY_HOSTNAME"] +
":" +
process.env["NEXT_PUBLIC_RPC_GATEWAY_PORT"] +
process.env["NEXT_PUBLIC_RPC_GATEWAY_ROOT_URL"];
protected constructor() {} protected constructor() {
BaseApiService.baseUrl ??=
FrontendVariables.getInstance().BACK_API_PROTOCOL +
FrontendVariables.getInstance().BACK_API_HOST +
":" +
FrontendVariables.getInstance().BACK_API_PORT ?? "" +
FrontendVariables.getInstance().BACK_API_ROOT_URL +
FrontendVariables.getInstance().BACK_API_VERSION;
}
protected getBaseUrl() {
return BaseApiService.baseUrl;
}
protected buildHeaders(contentType: ContentType) { protected buildHeaders(contentType: ContentType) {
const headers = new Headers(); const headers = new Headers();
@ -35,7 +40,6 @@ export default abstract class BaseApiService {
await fetch(url, { await fetch(url, {
method: "GET", method: "GET",
headers: this.buildHeaders(ContentType.JSON), headers: this.buildHeaders(ContentType.JSON),
mode: "no-cors",
}); });
console.log(request); console.log(request);
return this.sendRequest<T>(request); return this.sendRequest<T>(request);

View File

@ -1,5 +1,5 @@
import BaseApiService from "@Front/Api/BaseApiService"; import BaseApiService from "@Front/Api/BaseApiService";
export default abstract class BaseNotary extends BaseApiService { export default abstract class BaseNotary extends BaseApiService {
protected readonly namespaceUrl = this.backUrl.concat("/customers"); protected readonly namespaceUrl = this.getBaseUrl().concat("/customers");
} }

View File

@ -1,5 +1,5 @@
import BaseApiService from "@Front/Api/BaseApiService"; import BaseApiService from "@Front/Api/BaseApiService";
export default abstract class BaseNotary extends BaseApiService { export default abstract class BaseNotary extends BaseApiService {
protected readonly namespaceUrl = this.backUrl.concat("/notary"); protected readonly namespaceUrl = this.getBaseUrl().concat("/notary");
} }

View File

@ -45,7 +45,7 @@ export default class Users extends BaseNotary {
} }
} }
public async getOne(uid: string): Promise<User> { public async getByUid(uid: string): Promise<User> {
const url = new URL(this.baseURl.concat("/").concat(uid)); const url = new URL(this.baseURl.concat("/").concat(uid));
try { try {
return await this.getRequest<User>(url); return await this.getRequest<User>(url);

View File

@ -0,0 +1,5 @@
import BaseApiService from "@Front/Api/BaseApiService";
export default abstract class BaseSuperAdmin extends BaseApiService {
protected readonly namespaceUrl = this.getBaseUrl().concat("/super-admin");
}

View File

@ -0,0 +1,67 @@
import { Service } from "typedi";
import User from "le-coffre-resources/dist/Notary";
import BaseSuperAdmin from "../BaseSuperAdmin";
@Service()
export default class Users extends BaseSuperAdmin {
private static instance: Users;
private readonly baseURl = this.namespaceUrl.concat("/Users");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Users();
} else {
return this.instance;
}
}
public async get(): Promise<User[]> {
const url = new URL(this.baseURl);
try {
return await this.getRequest<User[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getAuthorizationCode(): Promise<any> {
try {
const url =
new URL(`https://qual-connexion.idnot.fr/IdPOAuth2/authorize/idnot_idp_v1?
client_id=4501646203F3EF67
&redirect_uri=https://app.stg.lecoffre.smart-chain.fr/
&scope=openid,profile,offline_access
&response_type=code`);
// const url = new URL("https://jsonplaceholder.typicode.com/todos/1");
await this.getRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string): Promise<User> {
const url = new URL(this.baseURl.concat("/").concat(uid));
try {
return await this.getRequest<User>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
// public async post(params: User): Promise<User> {
// const url = new URL(this.baseURl);
// try {
// return await this.postRequest<User>(url, params);
// } catch (err) {
// this.onError(err);
// return Promise.reject(err);
// }
// }
}

View File

@ -1,5 +1,6 @@
import React from "react"; import React, { CSSProperties } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Image from "next/image";
export enum EButtonVariant { export enum EButtonVariant {
PRIMARY = "primary", PRIMARY = "primary",
@ -13,7 +14,8 @@ type IProps = {
children?: React.ReactNode; children?: React.ReactNode;
variant?: EButtonVariant; variant?: EButtonVariant;
fullwidth?: "true" | "false"; fullwidth?: "true" | "false";
icon?: React.ReactNode; icon? : string;
iconStyle?: CSSProperties;
disabled?: boolean; disabled?: boolean;
type: "button" | "submit"; type: "button" | "submit";
isloading: string; isloading: string;
@ -33,12 +35,10 @@ export default class Button extends React.Component<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
const attributes = { ...this.props }; const attributes = { ...this.props };
delete attributes.icon; delete attributes.icon;
// let icon = this.props.isloading === "true" ? <Loader /> : this.props.icon; // Notion de loader
let icon = this.props.icon;
return ( return (
<button {...attributes} onClick={this.props.onClick} className={classes["root"]} type={this.props.type}> <button {...attributes} onClick={this.props.onClick} className={classes["root"]} type={this.props.type}>
{this.props.children} {this.props.children}
{this.props.icon && icon} {this.props.icon && <Image src={this.props.icon} style={this.props.iconStyle} alt={"button icon"}/>}
</button> </button>
); );
} }

View File

@ -0,0 +1,27 @@
@import "@Themes/constants.scss";
.root {
background-color: $grey-soft;
padding: 24px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.content {
display:grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 32px;
@media (max-width: $screen-l) {
grid-template-columns: 1fr 1fr;
}
&.isSignleDescription {
grid-template-columns: 1fr;
}
}
.edit-icon{
margin-left: 48px;
}
}

View File

@ -0,0 +1,61 @@
import React from "react";
import classes from "./classes.module.scss";
import classNames from "classnames";
import Image from "next/image";
import PenICon from "@Assets/Icons/pen.svg";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Typography, { ITypo } from "../../Typography";
type IProps = {
folder: IDashBoardFolder;
isDescription: boolean;
};
type IState = {};
export default class FolderBoxInformation extends React.Component<IProps, IState> {
public static defaultProps = {
isDescription: false,
};
public override render(): JSX.Element {
return <div className={classNames(classes["root"], this.props.isDescription && classes["isSignleDescription"])}>
<div className={classes["content"]}>
{this.props.isDescription ?
<div className={classes["text-container"]}>
<Typography typo={ITypo.NAV_INPUT_16}>Note dossier :</Typography>
<Typography typo={ITypo.P_18}>{this.props.folder.description ?? "..."}</Typography>
</div>
:
<>
<div className={classes["text-container"]}>
<Typography typo={ITypo.NAV_INPUT_16}>Intitulé du dossier</Typography>
<Typography typo={ITypo.P_18}>{this.props.folder.name ?? "..."}</Typography>
</div>
<div className={classes["text-container"]}>
<Typography typo={ITypo.NAV_INPUT_16}>Numéro de dossier</Typography>
<Typography typo={ITypo.P_18}>{this.props.folder.folder_number ?? "..."}</Typography>
</div>
<div className={classes["text-container"]}>
<Typography typo={ITypo.NAV_INPUT_16}>Type dacte</Typography>
<Typography typo={ITypo.P_18}>{this.props.folder.deed.deed_type.name ?? "..."}</Typography>
</div>
<div className={classes["text-container"]}>
<Typography typo={ITypo.NAV_INPUT_16}>Ouverture du dossier</Typography>
<Typography typo={ITypo.P_18}>{this.formatDate(this.props.folder.created_at)}</Typography>
</div>
</>
}
</div>
<Image src={PenICon} alt="edit informations" className={classes["edit-icon"]}/>
</div>;
}
private formatDate(date: Date | null): string {
if(!date) return "...";
return date.toLocaleDateString("fr-FR", {
year: "numeric",
month: "long",
day: "numeric",
});
}
}

View File

@ -7,6 +7,7 @@
width: 100%; width: 100%;
padding: 24px; padding: 24px;
border: 1px solid $grey-medium; border: 1px solid $grey-medium;
cursor: pointer;
&:hover{ &:hover{
background-color: $grey-medium; background-color: $grey-medium;

View File

@ -3,20 +3,23 @@ import classes from "./classes.module.scss";
import Typography, { ITypo } from "../Typography"; import Typography, { ITypo } from "../Typography";
import Image from "next/image"; import Image from "next/image";
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import WarningBadge from "../WarningBadge"; import WarningBadge from "../WarningBadge";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
type IProps = { type IProps = {
folder: { folder: IDashBoardFolder;
folder_number: OfficeFolder["folder_number"] ; onSelectedFolder?: (folder: IDashBoardFolder) => void;
documents?: OfficeFolder["documents"];
};
} }
type IState = {}; type IState = {};
export default class FolderContainer extends React.Component<IProps, IState> { export default class FolderContainer extends React.Component<IProps, IState> {
public constructor(props: IProps) {
super(props);
this.onSelectedFolder = this.onSelectedFolder.bind(this);
}
public override render(): JSX.Element { public override render(): JSX.Element {
return <div className={classes["root"]}> return <div className={classes["root"]} onClick={this.onSelectedFolder}>
<div className={classes["left-side"]}> <div className={classes["left-side"]}>
<Typography typo={ITypo.P_16}>{"Dossier ".concat(this.props.folder.folder_number)}</Typography> <Typography typo={ITypo.P_16}>{"Dossier ".concat(this.props.folder.folder_number)}</Typography>
{this.countPendingDocuments() > 0 && <div className={classes["warning"]}><WarningBadge /></div>} {this.countPendingDocuments() > 0 && <div className={classes["warning"]}><WarningBadge /></div>}
@ -29,4 +32,8 @@ export default class FolderContainer extends React.Component<IProps, IState> {
if (!this.props.folder.documents) return 0; if (!this.props.folder.documents) return 0;
return this.props.folder.documents?.filter((document) => document.document_status === "PENDING").length ?? 0; return this.props.folder.documents?.filter((document) => document.document_status === "PENDING").length ?? 0;
} }
private onSelectedFolder(): void {
this.props.onSelectedFolder && this.props.onSelectedFolder(this.props.folder);
}
} }

View File

@ -1,10 +1,11 @@
import React from "react"; import React from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import FolderContainer from "../FolderContainer"; import FolderContainer from "../FolderContainer";
import { IFolder } from "../SearchBar"; import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
type IProps = { type IProps = {
folders: IFolder[]; folders: IDashBoardFolder[];
onSelectedFolder?: (folder: IDashBoardFolder) => void;
}; };
type IState = {}; type IState = {};
@ -12,9 +13,8 @@ type IState = {};
export default class FolderList extends React.Component<IProps, IState> { export default class FolderList extends React.Component<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
return <div className={classes["root"]}> return <div className={classes["root"]}>
{this.props.folders.map((folder, key) => { {this.props.folders.map((folder) => {
return <FolderContainer folder={folder} key={folder.uid} onSelectedFolder={this.props.onSelectedFolder}/>;
return <FolderContainer folder={folder} key={key}/>;
})}; })};
</div>; </div>;
} }

View File

@ -2,6 +2,7 @@
.root { .root {
min-height: 100%; min-height: 100%;
width: 389px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;

View File

@ -1,14 +1,16 @@
import React from "react"; import React from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import SearchBar, { IFolder } from "../SearchBar"; import SearchBar from "../SearchBar";
import Button from "../Button"; import Button from "../Button";
import FolderList from "../FolderList"; import FolderList from "../FolderList";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
type IProps = { type IProps = {
folders: IFolder[]; folders: IDashBoardFolder[];
onSelectedFolder?: (folder: IDashBoardFolder) => void;
}; };
type IState = { type IState = {
filteredFolders: IFolder[]; filteredFolders: IDashBoardFolder[];
}; };
export default class FolderListContainer extends React.Component<IProps, IState> { export default class FolderListContainer extends React.Component<IProps, IState> {
@ -26,7 +28,7 @@ export default class FolderListContainer extends React.Component<IProps, IState>
<div className={classes["searchbar"]}> <div className={classes["searchbar"]}>
<SearchBar folders={this.props.folders} onChange={this.filterFolders} /> <SearchBar folders={this.props.folders} onChange={this.filterFolders} />
</div> </div>
<FolderList folders={this.state.filteredFolders} /> <FolderList folders={this.state.filteredFolders} onSelectedFolder={this.props.onSelectedFolder && this.props.onSelectedFolder} />
</div> </div>
<div> <div>
@ -35,7 +37,7 @@ export default class FolderListContainer extends React.Component<IProps, IState>
</div>; </div>;
} }
private filterFolders(folders: IFolder[]): IFolder[] { private filterFolders(folders: IDashBoardFolder[]): IDashBoardFolder[] {
this.setState({ filteredFolders: folders }) this.setState({ filteredFolders: folders })
return folders; return folders;
} }

View File

@ -15,13 +15,13 @@ type IFolderInformation = {
export default class InformationBox extends React.Component<IProps, IState> { export default class InformationBox extends React.Component<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
return <div className={classes["root"]}> return <div className={classes["root"]}>
{this.props.informations.map((information, key) => { {/* {this.props.informations.map((information, key) => {
const output = <div className={classes["information"] } key={key}> const output = <div className={classes["information"] } key={key}>
<span className={classes["label"]}>{information.label}</span> <span className={classes["label"]}>{information.label}</span>
<span className={classes["value"]}>{information.value}</span> <span className={classes["value"]}>{information.value}</span>
</div>; </div>;
return output; return output;
})}; })}; */}
</div>; </div>;
} }
} }

View File

@ -3,20 +3,16 @@ import classes from "./classes.module.scss";
import LoopIcon from "@Assets/Icons/loop.svg"; import LoopIcon from "@Assets/Icons/loop.svg";
import Image from "next/image"; import Image from "next/image";
import Typography, { ITypo } from "../Typography"; import Typography, { ITypo } from "../Typography";
import { OfficeFolder } from "le-coffre-resources/dist/Customer"; import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
type IProps = { type IProps = {
folders: IFolder[]; folders: IDashBoardFolder[];
onChange?: (folders: IFolder[]) => IFolder[]; onChange?: (folders: IDashBoardFolder[]) => IDashBoardFolder[];
}; };
type IState = { type IState = {
hasValue: boolean; hasValue: boolean;
}; };
export type IFolder = {
folder_number: OfficeFolder["folder_number"];
documents?: OfficeFolder["documents"];
};
export default class SearchBar extends React.Component<IProps, IState> { export default class SearchBar extends React.Component<IProps, IState> {
public constructor(props: IProps) { public constructor(props: IProps) {
@ -47,7 +43,7 @@ export default class SearchBar extends React.Component<IProps, IState> {
} }
private filterFolders(event: React.ChangeEvent<HTMLInputElement>) { private filterFolders(event: React.ChangeEvent<HTMLInputElement>) {
const filteredFolders: IFolder[] = this.props.folders.filter((folder) => folder.folder_number.includes(event.target.value)); const filteredFolders: IDashBoardFolder[] = this.props.folders.filter((folder) => folder.folder_number.includes(event.target.value));
return filteredFolders; return filteredFolders;
} }
} }

View File

@ -10,10 +10,12 @@ import DocumentList from "./DocumentList";
import Button, { EButtonVariant } from "../Button"; import Button, { EButtonVariant } from "../Button";
import PlusIcon from "@Assets/Icons/plus.svg" import PlusIcon from "@Assets/Icons/plus.svg"
import Confirm from "../Modal/Confirm"; import Confirm from "../Modal/Confirm";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
type IProps = { type IProps = {
customer: Customer customer: Customer
animationDelay?: number; animationDelay?: number;
folder: IDashBoardFolder;
}; };
type IState = { type IState = {
isOpen: boolean; isOpen: boolean;
@ -44,7 +46,7 @@ export default class UserFolder extends React.Component<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
const documentsAsked: Document[] | null = this.getDocumentsByStatus("ASKED"); const documentsAsked: Document[] | null = this.getDocumentsByStatus("ASKED");
const otherDocuments: Document[] | null = this.getOtherDocuments(documentsAsked); const otherDocuments: Document[] | null = this.getValidatedAndPendindDocuments();
return <div className={classes["root"]}> return <div className={classes["root"]}>
<Confirm <Confirm
@ -71,7 +73,7 @@ export default class UserFolder extends React.Component<IProps, IState> {
<DocumentList documents={otherDocuments} title="Documents à valider / validés" subtitle="Vous avez des documents à valider." openDeletionModal={this.openDeletionModal} /> <DocumentList documents={otherDocuments} title="Documents à valider / validés" subtitle="Vous avez des documents à valider." openDeletionModal={this.openDeletionModal} />
</div> </div>
<div className={classes["button-container"]}> <div className={classes["button-container"]}>
<Button variant={EButtonVariant.LINE} icon={<Image src={PlusIcon} alt="plus icon" />}>Demander un autre document </Button> <Button variant={EButtonVariant.LINE} icon={PlusIcon}>Demander un autre document </Button>
<Button>Envoyer un mail de demande de documents</Button> <Button>Envoyer un mail de demande de documents</Button>
</div> </div>
</div> </div>
@ -87,23 +89,32 @@ export default class UserFolder extends React.Component<IProps, IState> {
if (!this.props.customer.documents) return 0; if (!this.props.customer.documents) return 0;
const totalDocuments: number = this.props.customer.documents.length; const totalDocuments: number = this.props.customer.documents.length;
const numberDocumentsAsked: number = this.getDocumentsByStatus("ASKED")?.length || 0; const numberDocumentsAsked: number = this.getDocumentsByStatus("ASKED")?.length || 0;
return Math.round((numberDocumentsAsked / totalDocuments) * 100); return Math.round(((totalDocuments - numberDocumentsAsked) / totalDocuments) * 100);
} }
private getDocumentsByStatus(status: string): Document[] | null { private getDocumentsByStatus(status: string): Document[] | null {
if (!this.props.customer.documents) return null; if (!this.props.customer.documents) return null;
return this.props.customer.documents.filter((document) => document.document_status === status); return this.props.customer.documents.filter((document) => document.document_status === status && document.folder.uid === this.props.folder.uid);
} }
private getOtherDocuments(documentToExclude: Document[] | null): Document[] | null {
if (!this.props.customer.documents) return null; // Get all documents Validated, pending
if (!documentToExclude) return this.props.customer.documents; private getValidatedAndPendindDocuments(): Document[] | null {
return this.props.customer.documents.filter((document) => !documentToExclude.includes(document)); const pendingDocuments = this.getDocumentsByStatus("PENDING");
const validatedDocuments = this.getDocumentsByStatus("VALIDATED");
if (!pendingDocuments && !validatedDocuments) return null;
pendingDocuments?.push(...validatedDocuments!);
return pendingDocuments;
} }
// TODO: REFACTO this because the other documents should only be correspond to other documents of the props folders
// private getOtherDocuments(documentToExclude: Document[] | null): Document[] | null {
// if (!this.props.customer.documents) return null;
// if (!documentToExclude) return this.props.customer.documents;
// return this.props.customer.documents.filter((document) => !documentToExclude.includes(document));
// }
private toggleOpen(): void { private toggleOpen(): void {
if (this.state.isOpen) { if (this.state.isOpen) {
this.closeComponent(); this.closeComponent();
} }

View File

@ -2,13 +2,15 @@
.root { .root {
.content { .content {
display: flex;
.left-side { .left-side {
max-width: 389px; width: 389px;
height: calc(100vh - 83px); height: calc(100vh - 83px);
} }
.right-side { .right-side {
width: 100%; width: 100%;
padding: 64px 48px;
} }
} }
} }

View File

@ -1,26 +1,36 @@
import 'reflect-metadata'; import 'reflect-metadata';
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { IFolder } from "@Front/Components/DesignSystem/SearchBar";
import { folders } from "@Front/Components/Layouts/DesignSystem/dummyData"
import FolderListContainer from "@Front/Components/DesignSystem/FolderListContainer"; 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 { folders } from "@Front/Components/Layouts/DesignSystem/dummyData"
import { OfficeFolder } from 'le-coffre-resources/dist/Customer';
type IProps = { type IProps = {
title: string; title: string;
children?: ReactNode; children?: ReactNode;
onSelectedFolder: (folder: IDashBoardFolder) => void;
}; };
type IState = { type IState = {
folders: IFolder[]; folders: IDashBoardFolder[];
}; };
export type IDashBoardFolder = {
uid: OfficeFolder["uid"];
name: OfficeFolder["name"];
folder_number: OfficeFolder["folder_number"];
documents?: OfficeFolder["documents"]
description: OfficeFolder["description"];
deed: OfficeFolder["deed"];
created_at: OfficeFolder["created_at"];
office_folder_has_customers?: OfficeFolder["office_folder_has_customers"];
};
export default class DefaultNotaryDashboard extends React.Component<IProps, IState> { export default class DefaultNotaryDashboard extends React.Component<IProps, IState> {
public static defaultProps = { public static defaultProps = {
scrollTop: 0, scrollTop: 0,
}; };
public constructor(props: IProps) { public constructor(props: IProps) {
super(props); super(props);
this.state = { this.state = {
folders: folders, folders: folders,
@ -32,7 +42,7 @@ export default class DefaultNotaryDashboard extends React.Component<IProps, ISta
<div className={classes["root"]}> <div className={classes["root"]}>
<Header isUserConnected={true} /> <Header isUserConnected={true} />
<div className={classes["content"]}> <div className={classes["content"]}>
<div className={classes["left-side"]}><FolderListContainer folders={this.state.folders} /></div> <div className={classes["left-side"]}><FolderListContainer folders={this.state.folders} onSelectedFolder={this.props.onSelectedFolder}/></div>
<div className={classes["right-side"]}>{this.props.children}</div> <div className={classes["right-side"]}>{this.props.children}</div>
</div> </div>
<Version /> <Version />

View File

@ -8,12 +8,13 @@ import {
Customer, Customer,
DocumentType, DocumentType,
Document, Document,
OfficeFolderHasCustomer,
} from "le-coffre-resources/dist/Notary"; } from "le-coffre-resources/dist/Notary";
import { ECustomerStatus } from "le-coffre-resources/dist/Customer/Customer"; import { ECustomerStatus } from "le-coffre-resources/dist/Customer/Customer";
import { EFolderStatus } from "le-coffre-resources/dist/Customer/OfficeFolder"; import { EFolderStatus } from "le-coffre-resources/dist/Customer/OfficeFolder";
export const address: Address = { export const address: Address = {
uid: "a&23", uid: "a&2azedzaa3",
address: "123", address: "123",
city: "France", city: "France",
zip_code: 78140, zip_code: 78140,
@ -22,7 +23,7 @@ export const address: Address = {
}; };
export const office: Office = { export const office: Office = {
uid: "111213", uid: "111zdazaefez213",
idNot: "12EE12", idNot: "12EE12",
name: "Office 1", name: "Office 1",
crpcen: "AZezdz", crpcen: "AZezdz",
@ -32,7 +33,7 @@ export const office: Office = {
office_status: "ACTIVATED", office_status: "ACTIVATED",
}; };
export const deedType: DeedType = { export const deedType: DeedType = {
uid: "123312", uid: "123azefezgzeg312",
name: "Acte mariage", name: "Acte mariage",
description: "dzsdaf", description: "dzsdaf",
archived_at: new Date(), archived_at: new Date(),
@ -42,14 +43,14 @@ export const deedType: DeedType = {
}; };
export const deed: Deed = { export const deed: Deed = {
uid: "123124", uid: "zegefzeferg",
deed_type: deedType, deed_type: deedType,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
}; };
export const contact: Contact = { export const contact: Contact = {
uid: "123123", uid: "g('yeh(grgrezg",
first_name: "John", first_name: "John",
last_name: "Doe", last_name: "Doe",
email: "johnDoe@gmail.com", email: "johnDoe@gmail.com",
@ -61,9 +62,22 @@ export const contact: Contact = {
civility: "MALE", civility: "MALE",
}; };
export const contact2: Contact = {
uid: "g('yeh(grgrezg",
first_name: "Customer2",
last_name: "Doe",
email: "johnDoe@gmail.com",
address: address,
created_at: new Date(),
updated_at: new Date(),
cell_phone_number: "0132249865",
phone_number: "0132249865",
civility: "MALE",
};
export const docType: DocumentType = { export const docType: DocumentType = {
name: "Acte de naissance", name: "Acte de naissance",
uid: "123123", uid: "fezezfazegezrgrezg",
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
public_description: "Acte de naissance public description", public_description: "Acte de naissance public description",
@ -71,14 +85,14 @@ export const docType: DocumentType = {
archived_at: new Date(), archived_at: new Date(),
}; };
export const customer: Customer = { export const customer: Customer = {
uid: "123123", uid: "erhtgerfzeare",
contact: contact, contact: contact,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
status: ECustomerStatus.VALIDATED, status: ECustomerStatus.VALIDATED,
}; };
export const folder: OfficeFolder = { export const folder: OfficeFolder = {
uid: "11123312", uid: "zfaefergregrezterf",
folder_number: "12331", folder_number: "12331",
name: "Mon dossier", name: "Mon dossier",
status: EFolderStatus.ARCHIVED, status: EFolderStatus.ARCHIVED,
@ -91,17 +105,7 @@ export const folder: OfficeFolder = {
}; };
export const document: Document = { export const document: Document = {
uid: "0", uid: "fzeafergreztyzgrf",
depositor: customer,
document_status: "ASKED",
folder: folder,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const document2: Document = {
uid: "1",
depositor: customer, depositor: customer,
document_status: "ASKED", document_status: "ASKED",
folder: folder, folder: folder,
@ -111,7 +115,7 @@ export const document2: Document = {
}; };
export const documentPending: Document = { export const documentPending: Document = {
uid: "2", uid: "fzefeazdagrtetrury",
depositor: customer, depositor: customer,
document_status: "PENDING", document_status: "PENDING",
folder: folder, folder: folder,
@ -121,7 +125,7 @@ export const documentPending: Document = {
}; };
export const documentDeposited: Document = { export const documentDeposited: Document = {
uid: "3", uid: "uè§u§htfgrthytrgr",
depositor: customer, depositor: customer,
document_status: "VALIDATED", document_status: "VALIDATED",
folder: folder, folder: folder,
@ -131,18 +135,16 @@ export const documentDeposited: Document = {
}; };
export const customer2: Customer = { export const customer2: Customer = {
uid: "123123", uid: "yregrgetergrt",
contact: contact, contact: contact2,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
status: ECustomerStatus.VALIDATED, status: ECustomerStatus.VALIDATED,
documents: [document, document2, documentPending, documentDeposited], documents: [document, documentPending, documentDeposited, ],
}; };
export const folderWithPendingDocument: OfficeFolder = { export const folderWithPendingDocument: OfficeFolder = {
uid: "11123312", uid: "ferzferzfezeefzdd",
folder_number: "00001", folder_number: "00001",
name: "Mon dossier", name: "Mon dossier",
status: EFolderStatus.ARCHIVED, status: EFolderStatus.ARCHIVED,
@ -152,10 +154,10 @@ export const folderWithPendingDocument: OfficeFolder = {
updated_at: new Date(), updated_at: new Date(),
description: "Description", description: "Description",
archived_description: "Archived description", archived_description: "Archived description",
documents: [document, document2, documentPending, documentDeposited], documents: [document, documentPending, documentDeposited],
}; };
export const folderWithPendingDocument1: OfficeFolder = { export const folderWithPendingDocument1: OfficeFolder = {
uid: "11123312", uid: "gtrtyutyhretgytu",
folder_number: "00002", folder_number: "00002",
name: "Mon dossier", name: "Mon dossier",
status: EFolderStatus.ARCHIVED, status: EFolderStatus.ARCHIVED,
@ -168,7 +170,7 @@ export const folderWithPendingDocument1: OfficeFolder = {
documents: [documentDeposited], documents: [documentDeposited],
}; };
export const folderWithPendingDocument2: OfficeFolder = { export const folderWithPendingDocument2: OfficeFolder = {
uid: "11123312", uid: "adzefzefsfrefzrtgtr",
folder_number: "00003", folder_number: "00003",
name: "Mon dossier", name: "Mon dossier",
status: EFolderStatus.ARCHIVED, status: EFolderStatus.ARCHIVED,
@ -178,10 +180,37 @@ export const folderWithPendingDocument2: OfficeFolder = {
updated_at: new Date(), updated_at: new Date(),
description: "Description", description: "Description",
archived_description: "Archived description", archived_description: "Archived description",
documents: [document, document2], documents: [document],
}; };
export const officeFolderHasCustomer1: OfficeFolderHasCustomer = {
uid: "ferzfergrzeyerezrz",
customer: customer,
office_folder: folderWithPendingDocument,
created_at: new Date(),
updated_at: new Date(),
};
export const officeFolderHasCustomer2: OfficeFolderHasCustomer = {
uid: "tezrfzdfgrggeerry",
customer: customer2,
office_folder: folderWithPendingDocument,
created_at: new Date(),
updated_at: new Date(),
};
export const document8: Document = {
uid: "eztreggrgbyunjukhg",
depositor: customer,
document_status: "ASKED",
folder: folderWithPendingDocument,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const folderWithPendingDocument3: OfficeFolder = { export const folderWithPendingDocument3: OfficeFolder = {
uid: "11123312", uid: "mkovrijvrezviev",
folder_number: "00014", folder_number: "00014",
name: "Mon dossier", name: "Mon dossier",
status: EFolderStatus.ARCHIVED, status: EFolderStatus.ARCHIVED,
@ -191,6 +220,24 @@ export const folderWithPendingDocument3: OfficeFolder = {
updated_at: new Date(), updated_at: new Date(),
description: "Description", description: "Description",
archived_description: "Archived description", archived_description: "Archived description",
documents: [document, document2, documentDeposited, documentPending], documents: [document, documentDeposited, documentPending],
office_folder_has_customers: [officeFolderHasCustomer1, officeFolderHasCustomer2],
}; };
export const folders : OfficeFolder[] = [folderWithPendingDocument, folderWithPendingDocument1, folderWithPendingDocument2, folderWithPendingDocument3]
export const document2: Document = {
uid: "mejfihruehfoire",
depositor: customer,
document_status: "ASKED",
folder: folderWithPendingDocument3,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const folders: OfficeFolder[] = [
folderWithPendingDocument,
folderWithPendingDocument1,
folderWithPendingDocument2,
folderWithPendingDocument3,
];

View File

@ -172,7 +172,7 @@ export default class DesignSystem extends BasePage<IProps, IState> {
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Folder with no document to validate</Typography> <Typography typo={ITypo.P_16}>Folder with no document to validate</Typography>
<div className={classes["folder-conatainer"]}> <div className={classes["folder-conatainer"]}>
<FolderContainer folder={folder} /> <FolderContainer folder={folder}/>
</div> </div>
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
@ -225,7 +225,7 @@ export default class DesignSystem extends BasePage<IProps, IState> {
<Typography typo={ITypo.H3}>Notary Documents</Typography> <Typography typo={ITypo.H3}>Notary Documents</Typography>
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<UserFolder customer={customer2} /> <UserFolder customer={customer2} folder={folder}/>
</div> </div>
</div> </div>

View File

@ -0,0 +1,19 @@
@import "@Themes/constants.scss";
.root {
width: 100%;
.no-client{
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.title{
margin-bottom: 16px;
}
}
.client{
display: grid;
gap: 32px;
}
}

View File

@ -0,0 +1,43 @@
import React from "react";
import classes from "./classes.module.scss";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import PlusIcon from "@Assets/Icons/plus.svg";
import UserFolder from "@Front/Components/DesignSystem/UserFolder";
type IProps = {
folder: IDashBoardFolder;
};
type IState = {};
export default class ClientSection extends React.Component<IProps, IState> {
public override render(): JSX.Element {
return <div className={classes["root"]}>
{this.doesFolderHaveCustomer() ?
<div className={classes["client"]}>
{this.renderCustomerFolders()}
</div>
:
<div className={classes["no-client"]}>
<div className={classes["title"]}>
<Typography typo={ITypo.P_18}>Aucun client nest associé au dossier.</Typography>
</div>
<Button variant={EButtonVariant.LINE} icon={PlusIcon}>Ajouter un client</Button>
</div>}
</div>;
}
private renderCustomerFolders() {
const output = this.props.folder.office_folder_has_customers?.map((folderHasCustomer) => {
if (!folderHasCustomer.customer) return null;
// TODO : Les documents ASKED fonctionne mais les autres documents ne doivcent etre seulement ceux qui correspondent au folder
return <div className={classes["user-folder"]}><UserFolder folder={this.props.folder} customer={folderHasCustomer.customer} key={folderHasCustomer.customer.uid} /></div>;
})
return output ?? null;
}
private doesFolderHaveCustomer(): boolean {
return this.props.folder.office_folder_has_customers !== undefined;
}
}

View File

@ -0,0 +1,44 @@
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.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;
}
}
.second-box {
margin-top: 24px;
margin-bottom: 32px;
}
.progress-bar{
margin-bottom: 32px;
}
.button-container{
:first-child{
margin-right: 12px;
}
}
}
}

View File

@ -0,0 +1,69 @@
import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import BasePage from "../Base";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import classes from "./classes.module.scss";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation from "@Front/Components/DesignSystem/Elements/FolderBoxInformation";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import ClientSection from "./ClientSection";
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
type IProps = {};
type IState = {
selectedFolder: IDashBoardFolder | null;
};
export default class Folder extends BasePage<IProps, IState>{
public constructor(props: IProps) {
super(props);
this.state = {
selectedFolder: null,
};
this.onSelectedFolder = this.onSelectedFolder.bind(this);
}
public override render(): JSX.Element {
return (
<DefaultNotaryDashboard title={"Dossier"} onSelectedFolder={this.onSelectedFolder}>
<div className={classes["root"]}>
{this.state.selectedFolder && <div className={classes["folder-informations"]}>
<div className={classes["folder-header"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Informations du dossier</Typography>
<Button variant={EButtonVariant.LINE} icon={ChevronIcon}>Modifier les collaborateurs</Button>
</div>
<FolderBoxInformation folder={this.state.selectedFolder} />
<div className={classes["second-box"]}>
<FolderBoxInformation folder={this.state.selectedFolder} isDescription />
</div>
<div className={classes["progress-bar"]}>
<QuantityProgressBar title="Complétion du dossier" total={100} currentNumber={0} />
</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}>Archiver le dossier</Button>
<Button variant={EButtonVariant.SECONDARY}>Supprimer le dossier</Button>
</div>
</div>}
</div>
</DefaultNotaryDashboard>
);
}
public override async componentDidMount() {
const users = await Users.getInstance().getByUid("5rOlvAleeX");
console.log(users);
}
private doesFolderHaveCustomer(): boolean {
return this.state.selectedFolder?.office_folder_has_customers !== undefined;
}
private onSelectedFolder(folder: IDashBoardFolder): void {
this.setState({ selectedFolder: folder });
}
}

View File

@ -1,16 +0,0 @@
.root {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.title {
margin: 32px 0;
text-align: center;
}
.forget-password {
margin-top: 32px;
margin-bottom: 8px;
}
}

View File

@ -1,11 +0,0 @@
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import BasePage from "../Base";
export default class FolderInfomations extends BasePage {
public override render(): JSX.Element {
return (
<DefaultNotaryDashboard title={""}>
</DefaultNotaryDashboard>
);
}
}

View File

@ -1,31 +1,22 @@
import { Service } from "typedi";
@Service()
export class FrontendVariables { export class FrontendVariables {
private static instance: FrontendVariables; private static instance: FrontendVariables;
public readonly WEB_LABEL: string; public BACK_API_PROTOCOL!: string;
public readonly WEB_PORT!: string; public BACK_API_HOST!: string;
public readonly WEB_ROOT_URL!: string; public BACK_API_PORT!: string;
public readonly NEXT_PUBLIC_API_URL!: string; public BACK_API_ROOT_URL!: string;
public readonly NODE_ENV!: string; public BACK_API_VERSION!: string;
constructor() { private constructor() {}
this.NODE_ENV = process.env["NODE_ENV"]!;
this.WEB_LABEL = process.env["WEB_LABEL"]!;
this.WEB_PORT = process.env["WEB_PORT"]!;
this.WEB_ROOT_URL = process.env["WEB_ROOT_URL"]!;
this.NEXT_PUBLIC_API_URL = process.env["NEXT_PUBLIC_API_URL"]!;
}
public static getInstance(): FrontendVariables { public static getInstance(): FrontendVariables {
if (!this.instance) { if (!this.instance) {
this.instance = new this(); this.instance = new this();
} }
return this.instance; return this.instance;
} }
} }

View File

@ -1,4 +1,5 @@
import { DefaultLayout } from "@Front/Components/LayoutTemplates/DefaultLayout"; import { DefaultLayout } from "@Front/Components/LayoutTemplates/DefaultLayout";
import { FrontendVariables } from "@Front/Config/VariablesFront";
import "@Front/index.scss"; import "@Front/index.scss";
import type { NextPage } from "next"; import type { NextPage } from "next";
import type { AppType, AppProps } from "next/app"; import type { AppType, AppProps } from "next/app";
@ -10,12 +11,34 @@ export type NextPageWithLayout<TProps = Record<string, unknown>, TInitialProps =
type AppPropsWithLayout = AppProps & { type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout; Component: NextPageWithLayout;
} & {
backApiProtocol: string,
backApiHost: string,
backApiPort: string,
backApiRootUrl: string,
backApiVersion: string,
}; };
const MyApp = (({ Component, pageProps }: AppPropsWithLayout) => { const MyApp = (({ Component, pageProps, backApiProtocol, backApiHost, backApiPort, backApiRootUrl, backApiVersion }: AppPropsWithLayout) => {
const getLayout = Component.getLayout ?? ((page) => <DefaultLayout children={page}></DefaultLayout>); const getLayout = Component.getLayout ?? ((page) => <DefaultLayout children={page}></DefaultLayout>);
FrontendVariables.getInstance().BACK_API_PROTOCOL = backApiProtocol;
FrontendVariables.getInstance().BACK_API_HOST = backApiHost;
FrontendVariables.getInstance().BACK_API_PORT = backApiPort;
FrontendVariables.getInstance().BACK_API_ROOT_URL = backApiRootUrl;
FrontendVariables.getInstance().BACK_API_VERSION = backApiVersion;
return getLayout(<Component {...pageProps} />); return getLayout(<Component {...pageProps} />);
}) as AppType; }) as AppType;
MyApp.getInitialProps = async () => {
return {
backApiProtocol: process.env["BACK_API_PROTOCOL"],
backApiHost: process.env["BACK_API_HOST"],
backApiPort: process.env["BACK_API_PORT"],
backApiRootUrl: process.env["BACK_API_ROOT_URL"],
backApiVersion: process.env["BACK_API_VERSION"],
};
}
export default MyApp; export default MyApp;

5
src/pages/dossier.tsx Normal file
View File

@ -0,0 +1,5 @@
import Folder from "@Front/Components/Layouts/Folder";
export default function Route() {
return <Folder />;
}

View File

@ -1,5 +0,0 @@
import FolderInfomations from "@Front/Components/Layouts/FolderInfomations";
export default function Route() {
return <FolderInfomations />;
}

View File

@ -75,3 +75,4 @@
"node_modules" "node_modules"
] ]
} }