Merge 'dev' in 'staging' (#49)

This commit is contained in:
Arnaud D. Natali 2023-08-18 15:40:21 +02:00 committed by GitHub
commit ed53861659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 669 additions and 141 deletions

View File

@ -22,7 +22,7 @@ lecoffreFront:
tls: tls:
hosts: hosts:
- app.ppd.lecoffre.smart-chain.fr - app.ppd.lecoffre.smart-chain.fr
secretName: api-tls secretName: front-tls
annotations: annotations:
kubernetes.io/ingress.class: nginx kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod

View File

@ -22,7 +22,7 @@ lecoffreFront:
tls: tls:
hosts: hosts:
- app.lecoffre.smart-chain.fr - app.lecoffre.smart-chain.fr
secretName: api-tls secretName: front-tls
annotations: annotations:
kubernetes.io/ingress.class: nginx kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod

View File

@ -22,7 +22,7 @@ lecoffreFront:
tls: tls:
hosts: hosts:
- app.stg.lecoffre.smart-chain.fr - app.stg.lecoffre.smart-chain.fr
secretName: api-tls secretName: front-tls
annotations: annotations:
kubernetes.io/ingress.class: nginx kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod

View File

@ -32,7 +32,7 @@ spec:
ports: ports:
- port: 80 - port: 80
name: http name: http
targetPort: 3001 targetPort: 3000
selector: selector:
app: lecoffre-front app: lecoffre-front
--- ---

View File

@ -1,16 +1 @@
apiVersion: external-secrets.io/v1beta1 ## same secret as back
kind: ExternalSecret
metadata:
name: {{ .Values.lecoffreFront.envSecrets }}
spec:
refreshInterval: 20s
secretStoreRef:
kind: SecretStore
name: secret-store
data:
{{ range $v := .Values.lecoffreFront.env }}
- secretKey: {{ $v.key }}
remoteRef:
key: {{ $v.scwID}}
version: latest_enabled
{{ end }}

View File

@ -24,7 +24,7 @@
"eslint-config-next": "13.2.4", "eslint-config-next": "13.2.4",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.66", "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.68",
"next": "13.2.4", "next": "13.2.4",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"react": "18.2.0", "react": "18.2.0",

Binary file not shown.

View File

@ -0,0 +1,38 @@
import BaseApiService from "@Front/Api/BaseApiService";
export default class Customer extends BaseApiService {
private static instance: Customer;
private readonly baseURl = this.getBaseUrl().concat("/france-connect/customer");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Customer();
} else {
return this.instance;
}
}
public async login(email: string) {
const url = new URL(this.baseURl.concat("/login/").concat(email));
try {
return await this.postRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> {
const url = new URL(this.baseURl.concat("/refresh-token"));
try {
return await this.postRequest(url, {}, refreshToken);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

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.getBaseUrl().concat("/customers"); protected readonly namespaceUrl = this.getBaseUrl().concat("/customer");
} }

View File

@ -0,0 +1,67 @@
import Customer, { Contact } from "le-coffre-resources/dist/Customer";
import BaseCustomer from "../BaseCustomer";
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetCustomersparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutCustomersParams = {
uid?: Customer["uid"];
contact?: Customer["contact"];
};
export interface IPostCustomersParams {
first_name: string;
last_name: string;
email: string;
cell_phone_number: string;
civility: ECivility;
address?: Contact["address"];
}
export default class Customers extends BaseCustomer {
private static instance: Customers;
private readonly baseURl = this.namespaceUrl.concat("/customers");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q: IGetCustomersparams): Promise<Customer[]> {
const url = new URL(this.baseURl);
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Customer[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Customer> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Customer>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { Document } from "le-coffre-resources/dist/Customer";
import BaseCustomer from "../BaseCustomer";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDocumentsparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutDocumentsParams = {
document_status?: EDocumentStatus;
refused_reason?: string;
};
export interface IPostDocumentsParams {}
export default class Documents extends BaseCustomer {
private static instance: Documents;
private readonly baseURl = this.namespaceUrl.concat("/documents");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q: IGetDocumentsparams): Promise<Document[]> {
const url = new URL(this.baseURl);
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Document[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a Document
*/
public async post(body: any): Promise<Document> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<Document>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Document> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Document>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDocumentsParams): Promise<Document> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Document>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async delete(uid: string): Promise<Document> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.deleteRequest<Document>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { File } from "le-coffre-resources/dist/Customer";
import BaseCustomer from "../BaseCustomer";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetFilesparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutFilesParams = {};
export interface IPostFilesParams {}
export default class Files extends BaseCustomer {
private static instance: Files;
private readonly baseURl = this.namespaceUrl.concat("/files");
private constructor() {
super();
}
public static getInstance() {
return (this.instance ??= new this());
}
public async get(q: IGetFilesparams): Promise<File[]> {
const url = new URL(this.baseURl);
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
const files = await this.getRequest<File[]>(url);
return files;
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a File
*/
public async post(body: any): Promise<File> {
const url = new URL(this.baseURl);
try {
return await this.postRequestFormData<File>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public getUploadLink(uid: string): string {
return this.baseURl.concat(`/download/${uid}`);
}
public async getByUid(uid: string, q?: any): Promise<File> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
const file = await this.getRequest<File>(url);
return file;
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutFilesParams): Promise<File> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<File>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Delete a folder only if the folder don't contains customers
*/
public async delete(uid: string): Promise<File> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.deleteRequest<File>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,57 @@
import { type OfficeFolder } from "le-coffre-resources/dist/Customer";
import BaseCustomer from "../BaseCustomer";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetFoldersParams {
q?: {
select?: {};
where?: {};
include?: {};
};
}
export default class Folders extends BaseCustomer {
private static instance: Folders;
private readonly baseURl = this.namespaceUrl.concat("/folders");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Get all folders
*/
public async get(q: IGetFoldersParams): Promise<OfficeFolder[]> {
const url = new URL(this.baseURl);
Object.entries(q).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<OfficeFolder[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
if (q) Object.entries(q).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<OfficeFolder>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -10,7 +10,7 @@ import Tooltip from "../ToolTip";
import Typography, { ITypo, ITypoColor } from "../Typography"; import Typography, { ITypo, ITypoColor } from "../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { Document, DocumentHistory, File as FileCustomer } from "le-coffre-resources/dist/Customer"; import { Document, DocumentHistory, File as FileCustomer } from "le-coffre-resources/dist/Customer";
import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files"; import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import classNames from "classnames"; import classNames from "classnames";
import Confirm from "../Modal/Confirm"; import Confirm from "../Modal/Confirm";

View File

@ -1,95 +1,74 @@
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers"; "use client";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents"; import Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument"; import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
import TextField from "@Front/Components/DesignSystem/Form/TextField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Base from "@Front/Components/Layouts/Base";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate"; import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer"; import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer";
import React from "react"; import React, { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { useRouter } from "next/router";
import JwtService from "@Front/Services/JwtService/JwtService";
type IProps = { type IProps = {};
targetedCustormer: string; // MOCK
};
type IState = {
isAddDocumentModalVisible: boolean;
documents: Document[];
mockedCustomer: Customer | null;
};
export default class ClientDashboard extends Base<IProps, IState> { export default function ClientDashboard(props: IProps) {
public constructor(props: IProps) { const router = useRouter();
super(props); let { folderUid } = router.query;
this.state = { const [documents, setDocuments] = useState<Document[] | null>(null);
isAddDocumentModalVisible: false, const [customer, setCustomer] = useState<Customer | null>(null);
documents: [], const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false);
mockedCustomer: null,
};
this.onCloseModalAddDocument = this.onCloseModalAddDocument.bind(this);
this.onOpenModalAddDocument = this.onOpenModalAddDocument.bind(this);
}
public override render(): JSX.Element { const onCloseModalAddDocument = useCallback(() => {
return ( setIsAddDocumentModalVisible(false);
<DefaultTemplate title={"Mon compte"} isPadding={false} hasHeaderLinks={false}> }, []);
<div className={classes["root"]}>
{this.renderHeader()}
<div className={classes["sub-container"]}>
<div className={classes["content"]}>
{this.state.documents?.map((document) => (
<DepositDocument document={document} key={document.uid} defaultFiles={document.files ?? []} />
))}
</div>
<Typography typo={ITypo.H2}>Documents supplémentaires (facultatif)</Typography>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer d'autres documents à votre notaire ?
</Typography>
<Button variant={EButtonVariant.GHOST} className={classes["button"]} onClick={this.onOpenModalAddDocument}>
Ajouter d'autres documents
</Button>
</div>
<Confirm
isOpen={this.state.isAddDocumentModalVisible}
onClose={this.onCloseModalAddDocument}
onAccept={this.onOpenModalAddDocument}
closeBtn
header={"Ajouter un document"}
cancelText={"Annuler"}
confirmText={"Déposer le document"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer un autre document à votre notaire ?
</Typography>
<TextField placeholder="Nom du document" />
<Typography typo={ITypo.P_16} className={classes["text"]}>
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant.
</Typography>
<DepositDocument
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
}),
})}
/>
</div>
</Confirm>
</div>
</DefaultTemplate>
);
}
private renderHeader(): JSX.Element { const onOpenModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(true);
}, []);
useEffect(() => {
async function getDocuments() {
let jwt;
if (typeof document !== "undefined") {
jwt = JwtService.getInstance().decodeJwt();
}
if (!jwt || !jwt.email) return;
const customers = await Customers.getInstance().get({
where: { contact: { email: jwt.email }, office_folders: { some: { uid: folderUid } } },
});
const actualCustomer: Customer = customers[0]!;
const query: IGetDocumentsparams = {
where: { depositor: { uid: actualCustomer.uid }, folder_uid: folderUid as string },
include: {
files: true,
document_history: true,
document_type: true,
depositor: true,
},
};
const documentList = await Documents.getInstance().get(query);
setDocuments(documentList);
setCustomer(actualCustomer);
}
getDocuments();
}, [folderUid]);
const renderHeader = useCallback(() => {
return ( return (
<div className={classes["header"]}> <div className={classes["header"]}>
<div className={classes["text-container"]}> <div className={classes["text-container"]}>
{/* TODO Get name from userStore */} {/* TODO Get name from userStore */}
<Typography typo={ITypo.H1} className={classes["title"]}> <Typography typo={ITypo.H1} className={classes["title"]}>
Bonjour {this.state.mockedCustomer?.contact?.first_name.concat(" ", this.state.mockedCustomer?.contact?.last_name)} Bonjour {customer?.contact?.first_name.concat(" ", customer?.contact?.last_name)}
</Typography> </Typography>
<Typography typo={ITypo.H2} className={classes["subtitle"]}> <Typography typo={ITypo.H2} className={classes["subtitle"]}>
@ -108,31 +87,53 @@ export default class ClientDashboard extends Base<IProps, IState> {
</a> </a>
</div> </div>
); );
} }, [customer]);
public override async componentDidMount() { return (
// TODO Get documents of the current customer according to userStore <DefaultTemplate title={"Mon compte"} isPadding={false} hasHeaderLinks={false}>
// REMOVE this mock <div className={classes["root"]}>
const mockedCustomers = await Customers.getInstance().get({ where: { contact: { email: this.props.targetedCustormer } } }); {renderHeader()}
const mockedCustomer: Customer = mockedCustomers[0]!; <div className={classes["sub-container"]}>
<div className={classes["content"]}>
const query: IGetDocumentsparams = { {documents?.map((document) => (
where: { depositor: { uid: mockedCustomer.uid } }, <DepositDocument document={document} key={document.uid} defaultFiles={document.files ?? []} />
include: { ))}
files: true, </div>
document_history: true, <Typography typo={ITypo.H2}>Documents supplémentaires (facultatif)</Typography>
document_type: true, <Typography typo={ITypo.P_16} className={classes["text"]}>
}, Vous souhaitez envoyer d'autres documents à votre notaire ?
}; </Typography>
const documents: Document[] = await Documents.getInstance().get(query); <Button variant={EButtonVariant.GHOST} className={classes["button"]} onClick={onOpenModalAddDocument}>
this.setState({ documents, mockedCustomer }); Ajouter d'autres documents
} </Button>
</div>
private onCloseModalAddDocument() { <Confirm
this.setState({ isAddDocumentModalVisible: false }); isOpen={isAddDocumentModalVisible}
} onClose={onCloseModalAddDocument}
onAccept={onOpenModalAddDocument}
private onOpenModalAddDocument() { closeBtn
this.setState({ isAddDocumentModalVisible: true }); header={"Ajouter un document"}
} cancelText={"Annuler"}
confirmText={"Déposer le document"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer un autre document à votre notaire ?
</Typography>
<TextField placeholder="Nom du document" />
<Typography typo={ITypo.P_16} className={classes["text"]}>
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant.
</Typography>
<DepositDocument
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
}),
})}
/>
</div>
</Confirm>
</div>
</DefaultTemplate>
);
} }

View File

@ -0,0 +1,141 @@
import Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Base from "@Front/Components/Layouts/Base";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer";
import React from "react";
import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
type IProps = {};
type IState = {
isAddDocumentModalVisible: boolean;
documents: Document[];
mockedCustomer: Customer | null;
};
export default class ClientDashboard extends Base<IProps, IState> {
public constructor(props: IProps) {
super(props);
this.state = {
isAddDocumentModalVisible: false,
documents: [],
mockedCustomer: null,
};
this.onCloseModalAddDocument = this.onCloseModalAddDocument.bind(this);
this.onOpenModalAddDocument = this.onOpenModalAddDocument.bind(this);
}
public override render(): JSX.Element {
return (
<DefaultTemplate title={"Mon compte"} isPadding={false} hasHeaderLinks={false}>
<div className={classes["root"]}>
{this.renderHeader()}
<div className={classes["sub-container"]}>
<div className={classes["content"]}>
{this.state.documents?.map((document) => (
<DepositDocument document={document} key={document.uid} defaultFiles={document.files ?? []} />
))}
</div>
<Typography typo={ITypo.H2}>Documents supplémentaires (facultatif)</Typography>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer d'autres documents à votre notaire ?
</Typography>
<Button variant={EButtonVariant.GHOST} className={classes["button"]} onClick={this.onOpenModalAddDocument}>
Ajouter d'autres documents
</Button>
</div>
<Confirm
isOpen={this.state.isAddDocumentModalVisible}
onClose={this.onCloseModalAddDocument}
onAccept={this.onOpenModalAddDocument}
closeBtn
header={"Ajouter un document"}
cancelText={"Annuler"}
confirmText={"Déposer le document"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer un autre document à votre notaire ?
</Typography>
<TextField placeholder="Nom du document" />
<Typography typo={ITypo.P_16} className={classes["text"]}>
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant.
</Typography>
<DepositDocument
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
}),
})}
/>
</div>
</Confirm>
</div>
</DefaultTemplate>
);
}
private renderHeader(): JSX.Element {
return (
<div className={classes["header"]}>
<div className={classes["text-container"]}>
{/* TODO Get name from userStore */}
<Typography typo={ITypo.H1} className={classes["title"]}>
Bonjour {this.state.mockedCustomer?.contact?.first_name.concat(" ", this.state.mockedCustomer?.contact?.last_name)}
</Typography>
<Typography typo={ITypo.H2} className={classes["subtitle"]}>
Documents à envoyer
</Typography>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Votre notaire est dans l'attente de documents pour valider votre dossier. Voici la liste des documents.Veuillez
glisser / déposez chaque document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant. Si un des documents demandés ne vous concernent pas, veuillez contacter votre notaire à
laide du bouton ci-dessus.
</Typography>
</div>
<a href="mailto:g.texier@notaires.fr" target="_blank">
<Button className={classes["button"]}>Contacter mon notaire</Button>
</a>
</div>
);
}
public override async componentDidMount() {
// TODO Get documents of the current customer according to userStore
// REMOVE this mock
const jwt = JwtService.getInstance().decodeJwt();
const mockedCustomers = await Customers.getInstance().get({
where: { contact: { email: jwt?.email } },
});
const mockedCustomer: Customer = mockedCustomers[0]!;
const query: IGetDocumentsparams = {
where: { depositor: { uid: mockedCustomer.uid } },
include: {
files: true,
document_history: true,
document_type: true,
},
};
const documents: Document[] = await Documents.getInstance().get(query);
this.setState({ documents, mockedCustomer });
}
private onCloseModalAddDocument() {
this.setState({ isAddDocumentModalVisible: false });
}
private onOpenModalAddDocument() {
this.setState({ isAddDocumentModalVisible: true });
}
}

View File

@ -15,6 +15,7 @@ import LandingImage from "./landing-connect.jpeg";
export default function Login() { export default function Login() {
const router = useRouter(); const router = useRouter();
const redirectUserOnConnection = useCallback(() => { const redirectUserOnConnection = useCallback(() => {
async function getUser() { async function getUser() {
try { try {
@ -28,6 +29,21 @@ export default function Login() {
getUser(); getUser();
}, [router]); }, [router]);
const redirectCustomerOnConnection = useCallback(() => {
async function getCustomer() {
try {
await UserStore.instance.connectCustomer("antoine.bernard@outlook.com");
await JwtService.getInstance().checkJwt();
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
} catch (e) {
console.error(e);
}
}
getCustomer();
}, [router]);
return ( return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}> <DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -38,6 +54,9 @@ export default function Login() {
<Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}> <Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier avec ID.not S'identifier avec ID.not
</Button> </Button>
<Button onClick={redirectCustomerOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier en tant que customer
</Button>
<Typography typo={ITypo.P_18}> <Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div> <div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography> </Typography>

View File

@ -6,7 +6,7 @@ import Form from "@Front/Components/DesignSystem/Form";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultRoleDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard"; import DefaultRoleDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
import { Role, Rule } from "le-coffre-resources/dist/Admin"; import { OfficeRole, Rule } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import React from "react"; import React from "react";
@ -21,7 +21,7 @@ export default function RolesInformations(props: IProps) {
const router = useRouter(); const router = useRouter();
let { roleUid } = router.query; let { roleUid } = router.query;
const [roleSelected, setRoleSelected] = useState<Role | null>(null); const [roleSelected, setRoleSelected] = useState<OfficeRole | null>(null);
const [rulesCheckboxes, setRulesCheckboxes] = useState<RuleCheckbox[]>([]); const [rulesCheckboxes, setRulesCheckboxes] = useState<RuleCheckbox[]>([]);
const [selectAll, setSelectAll] = useState<boolean>(false); const [selectAll, setSelectAll] = useState<boolean>(false);
@ -56,7 +56,7 @@ export default function RolesInformations(props: IProps) {
} }
return { ...rule, checked: false }; return { ...rule, checked: false };
}) })
.sort((ruleA, ruleB) => (ruleA.name < ruleB.name ? 1 : -1)) .sort((ruleA, ruleB) => (ruleA.label < ruleB.label ? 1 : -1))
.sort((rule) => (rule.checked ? -1 : 1)); .sort((rule) => (rule.checked ? -1 : 1));
const selectAll = rulesCheckboxes.every((rule) => rule.checked); const selectAll = rulesCheckboxes.every((rule) => rule.checked);
@ -134,7 +134,7 @@ export default function RolesInformations(props: IProps) {
{rulesCheckboxes.map((rule) => ( {rulesCheckboxes.map((rule) => (
<div className={classes["right"]} key={rule.uid}> <div className={classes["right"]} key={rule.uid}>
<CheckBox <CheckBox
option={{ label: rule.name, value: rule.uid }} option={{ label: rule.label, value: rule.uid }}
checked={rule.checked} checked={rule.checked}
onChange={handleRuleChange} onChange={handleRuleChange}
/> />

View File

@ -1,4 +1,4 @@
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList"; import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
@ -8,6 +8,7 @@ import { useCallback, useEffect, useState } from "react";
import LandingImage from "../Login/landing-connect.jpeg"; import LandingImage from "../Login/landing-connect.jpeg";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
export default function SelectFolder() { export default function SelectFolder() {
const [folders, setFolders] = useState<OfficeFolder[]>([]); const [folders, setFolders] = useState<OfficeFolder[]>([]);
@ -15,8 +16,21 @@ export default function SelectFolder() {
useEffect(() => { useEffect(() => {
async function getFolders() { async function getFolders() {
const folders = await Folders.getInstance().get({}); const jwt = JwtService.getInstance().decodeJwt();
setFolders(folders.slice(0, 3)); if (!jwt) return;
const folders = await Folders.getInstance().get({
q: {
where: {
customers: {
some: {
uid: jwt.userId,
},
},
},
},
});
setFolders(folders);
} }
getFolders(); getFolders();
@ -24,7 +38,7 @@ export default function SelectFolder() {
const handleSelectBlock = useCallback( const handleSelectBlock = useCallback(
(block: IBlock) => { (block: IBlock) => {
router.push("/client-dashboard"); router.push("/client-dashboard/" + block.id);
}, },
[router], [router],
); );

View File

@ -182,9 +182,9 @@ export default function UserInformations(props: IProps) {
setRoleModalOpened(false); setRoleModalOpened(false);
setSelectedOption({ setSelectedOption({
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid, value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!, label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.label!,
}); });
}, [userSelected?.office_role, userSelected?.role?.name, userSelected?.role?.uid]); }, [userSelected?.office_role, userSelected?.role?.label, userSelected?.role?.uid]);
const changeRole = useCallback(async () => { const changeRole = useCallback(async () => {
await Users.getInstance().put( await Users.getInstance().put(
@ -271,7 +271,7 @@ export default function UserInformations(props: IProps) {
onChange={handleRoleChange} onChange={handleRoleChange}
selectedOption={{ selectedOption={{
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid, value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!, label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.label!,
}} }}
/> />
</div> </div>

View File

@ -8,6 +8,7 @@ enum PROVIDER_OPENID {
interface IUserJwtPayload { interface IUserJwtPayload {
userId: string; userId: string;
email: string | null;
openId: { openId: {
providerName: PROVIDER_OPENID; providerName: PROVIDER_OPENID;
userId: string | number; userId: string | number;
@ -38,7 +39,8 @@ export default class JwtService {
*/ */
public async checkJwt() { public async checkJwt() {
const decodedToken = this.decodeJwt(); const decodedToken = this.decodeJwt();
if(!decodedToken) return;
if (!decodedToken) return;
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import User from "@Front/Api/Auth/IdNot/User"; import User from "@Front/Api/Auth/IdNot/User";
import Customer from "@Front/Api/Auth/franceConnect/Customer";
import CookieService from "@Front/Services/CookieService/CookieService"; import CookieService from "@Front/Services/CookieService/CookieService";
import EventEmitter from "@Front/Services/EventEmitter"; import EventEmitter from "@Front/Services/EventEmitter";
@ -33,6 +34,23 @@ export default class UserStore {
return true; return true;
} }
public async connectCustomer(email: string) {
try {
//call connection function
const customer: any = await Customer.getInstance().login(email);
//Save tokens in cookies
CookieService.getInstance().setCookie("leCoffreAccessToken", customer.accessToken);
CookieService.getInstance().setCookie("leCoffreRefreshToken", customer.refreshToken);
this.event.emit("connection", this.accessToken);
} catch (error) {
console.error(error);
return false;
}
return true;
}
public async disconnect() { public async disconnect() {
try { try {
//Remove tokens from cookies //Remove tokens from cookies

View File

@ -1,5 +1,5 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="lucie.chevalier@outlook.com" />; return <ClientDashboard />;
} }

View File

@ -1,4 +1,4 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index2";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="manon.simon@gmail.com" />; return <ClientDashboard targetedCustormer="manon.simon@gmail.com" />;

View File

@ -1,4 +1,4 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index2";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="vincent.brognard@gmail.com" />; return <ClientDashboard targetedCustormer="vincent.brognard@gmail.com" />;

View File

@ -1,4 +1,4 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index2";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="maxime.lalo@gmail.com" />; return <ClientDashboard targetedCustormer="maxime.lalo@gmail.com" />;

View File

@ -1,4 +1,4 @@
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard"; import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index2";
export default function Route() { export default function Route() {
return <ClientDashboard targetedCustormer="kevin.hautefaye@gmail.com" />; return <ClientDashboard targetedCustormer="kevin.hautefaye@gmail.com" />;