Merge Staging in Preprod

This commit is contained in:
Arnaud D. Natali 2023-10-02 00:35:11 +02:00 committed by GitHub
commit 51191e0f31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 8838 additions and 539 deletions

View File

@ -13,6 +13,7 @@ const nextConfig = {
NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT: process.env.NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT,
NEXT_PUBLIC_IDNOT_CLIENT_ID: process.env.NEXT_PUBLIC_IDNOT_CLIENT_ID,
NEXT_PUBLIC_IDNOT_BASE_URL: process.env.NEXT_PUBLIC_IDNOT_BASE_URL,
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
},
// webpack: config => {
// config.node = {

5015
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@
"eslint-config-next": "13.2.4",
"form-data": "^4.0.0",
"jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.73",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.85",
"next": "13.2.4",
"prettier": "^2.8.7",
"react": "18.2.0",
@ -32,9 +32,7 @@
"react-select": "^5.7.2",
"sass": "^1.59.2",
"sharp": "^0.32.1",
"typescript": "4.9.5"
},
"devDependencies": {}
"typescript": "4.9.5",
"uuidv4": "^6.2.13"
}
}

BIN
public/CGU_LeCoffre_io.pdf Normal file

Binary file not shown.

View File

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

View File

@ -0,0 +1,94 @@
import { DeedType } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IPutDeedTypesParams = {
uid?: DeedType["uid"];
name?: DeedType["name"];
description?: DeedType["description"];
deed?: DeedType["deed"];
office?: DeedType["office"];
archived_at?: DeedType["archived_at"];
document_types?: DeedType["document_types"];
};
export type IPostDeedTypesParams = {
name?: DeedType["name"];
description?: DeedType["description"];
};
export type IGetDeedTypesParams = {
where?: {};
include?: {};
select?: {};
};
export default class DeedTypes extends BaseAdmin {
private static instance: DeedTypes;
private readonly baseURl = this.namespaceUrl.concat("/deed-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new DeedTypes();
} else {
return this.instance;
}
}
public async get(q: IGetDeedTypesParams): Promise<DeedType[]> {
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<DeedType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DeedType> {
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<DeedType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDeedTypesParams) {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DeedType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async post(body: IPostDeedTypesParams) {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DeedType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async delete(uid: string) {
const url = new URL(this.baseURl);
try {
return await this.deleteRequest<DeedType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,49 @@
import { Deed } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IGetDeedsParams = {
where?: {};
include?: {};
select?: {};
};
export default class Deeds extends BaseAdmin {
private static instance: Deeds;
private readonly baseURl = this.namespaceUrl.concat("/deeds");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Deeds();
} else {
return this.instance;
}
}
public async get(q: IGetDeedsParams): Promise<Deed[]> {
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<Deed[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Deed> {
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<Deed>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,86 @@
import { DocumentType } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDocumentTypesparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutDocumentTypesParams = {};
export interface IPostDocumentTypesParams {
name: string;
public_description: string;
private_description: string;
office: {
uid: string;
};
}
export default class DocumentTypes extends BaseAdmin {
private static instance: DocumentTypes;
private readonly baseURl = this.namespaceUrl.concat("/document-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q: IGetDocumentTypesparams): Promise<DocumentType[]> {
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<DocumentType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a Document
*/
public async post(body: DocumentType): Promise<DocumentType> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DocumentType>(url, body as any);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DocumentType> {
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<DocumentType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DocumentType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import { Document } from "le-coffre-resources/dist/SuperAdmin";
import BaseAdmin from "../BaseAdmin";
// 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 BaseAdmin {
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,81 @@
import { OfficeRole } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IGetRolesParams = {
where?: {};
include?: {};
select?: {};
};
export type IPutRoleParams = {
uid: OfficeRole["uid"];
rules: OfficeRole["rules"];
};
export type IPostRoleParams = {
name: OfficeRole["name"];
office: OfficeRole["office"];
};
export default class OfficeRoles extends BaseAdmin {
private static instance: OfficeRoles;
private readonly baseURl = this.namespaceUrl.concat("/office-roles");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new OfficeRoles();
} else {
return this.instance;
}
}
public async get(q?: IGetRolesParams): Promise<OfficeRole[]> {
const url = new URL(this.baseURl);
if (q) {
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<OfficeRole[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<OfficeRole> {
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<OfficeRole>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutRoleParams): Promise<OfficeRole> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<OfficeRole>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async post(body: IPostRoleParams) {
const url = new URL(this.baseURl);
try {
return await this.postRequest<OfficeRole>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,96 @@
import { Role } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IGetRolesParams = {
where?: {};
include?: {};
select?: {};
};
export type IPutRoleParams = {
uid: Role["uid"];
rules: Role["rules"];
};
export type IPostRoleParams = {
name: Role["name"];
};
export default class Roles extends BaseAdmin {
private static instance: Roles;
private readonly baseURl = this.namespaceUrl.concat("/roles");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Roles();
} else {
return this.instance;
}
}
public async get(q?: IGetRolesParams): Promise<Role[]> {
const url = new URL(this.baseURl);
if (q) {
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<Role[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async post(body: IPostRoleParams) {
const url = new URL(this.baseURl);
try {
return await this.postRequest<Role>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getOne(q?: IGetRolesParams): Promise<Role | null> {
const url = new URL(this.baseURl);
if (q) {
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
const res = await this.getRequest<Role[]>(url);
if (!res) return null;
if (res.length > 1) throw new Error("More than one role found");
return res[0] ? res[0] : null;
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Role> {
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<Role>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutRoleParams): Promise<Role> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Role>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,49 @@
import { Rule } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IGetRulesParams = {
where?: {};
include?: {};
select?: {};
};
export default class Rules extends BaseAdmin {
private static instance: Rules;
private readonly baseURl = this.namespaceUrl.concat("/rules");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Rules();
} else {
return this.instance;
}
}
public async get(q: IGetRulesParams): Promise<Rule[]> {
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<Rule[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Rule> {
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<Rule>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,91 @@
import User from "le-coffre-resources/dist/SuperAdmin";
import BaseAdmin from "../BaseAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetUsersparams {
where?: {};
include?: {};
select?: {};
}
// TODO Type getbyuid query params
export type IPutUsersParams = {
uid?: User["uid"];
idNot?: User["idNot"];
contact?: User["contact"];
office_membership?: User["office_membership"];
documents?: User["documents"];
};
export default class Users extends BaseAdmin {
private static instance: Users;
private readonly baseURl = this.namespaceUrl.concat("/users");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Get all Users
*/
public async get(q: IGetUsersparams): Promise<User[]> {
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<User[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<User> {
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<User>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a User
*/
// public async post(body: IPostDeedsParams): Promise<OfficeFolder> {
// const url = new URL(this.baseURl);
// try {
// return await this.postRequest<OfficeFolder>(url, body);
// } catch (err) {
// this.onError(err);
// return Promise.reject(err);
// }
// }
/**
* @description : Update the folder description
*/
public async put(uid: string, body: IPutUsersParams): Promise<User> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<User>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

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

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

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

View File

@ -0,0 +1,90 @@
import { Contact, Customer } from "le-coffre-resources/dist/Notary";
import BaseNotary from "../BaseNotary";
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 BaseNotary {
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);
}
}
/**
* @description : Create a Customer
*/
public async post(body: any): Promise<Customer> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<Customer>(url, body);
} 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);
}
}
public async put(uid: string, body: IPutCustomersParams): Promise<Customer> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Customer>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,84 @@
import { DeedType } from "le-coffre-resources/dist/Notary";
import BaseNotary from "../BaseNotary";
export type IPutDeedTypesParams = {
uid?: DeedType["uid"];
name?: DeedType["name"];
description?: DeedType["description"];
deed?: DeedType["deed"];
office?: DeedType["office"];
archived_at?: DeedType["archived_at"];
document_types?: DeedType["document_types"];
};
export type IPostDeedTypesParams = {
name?: DeedType["name"];
description?: DeedType["description"];
};
export type IGetDeedTypesParams = {
where?: {};
include?: {};
select?: {};
};
export default class DeedTypes extends BaseNotary {
private static instance: DeedTypes;
private readonly baseURl = this.namespaceUrl.concat("/deed-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new DeedTypes();
} else {
return this.instance;
}
}
public async get(q?: IGetDeedTypesParams): Promise<DeedType[]> {
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<DeedType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DeedType> {
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<DeedType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDeedTypesParams) {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DeedType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async post(body: IPostDeedTypesParams) {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DeedType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,72 @@
import { Deed, OfficeFolder } from "le-coffre-resources/dist/Notary";
import BaseAdmin from "../BaseNotary";
export type IGetDeedsParams = {
where?: {};
include?: {};
select?: {};
};
export type IPutDeedsParams = {
uid?: OfficeFolder["uid"];
folder_number?: OfficeFolder["folder_number"];
name?: OfficeFolder["name"];
description?: OfficeFolder["description"];
archived_description?: OfficeFolder["archived_description"];
status?: OfficeFolder["status"];
document_types?: Deed["document_types"];
};
export default class Deeds extends BaseAdmin {
private static instance: Deeds;
private readonly baseURl = this.namespaceUrl.concat("/deeds");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Deeds();
} else {
return this.instance;
}
}
public async get(q: IGetDeedsParams): Promise<Deed[]> {
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<Deed[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<Deed> {
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<Deed>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Update the folder description
*/
public async put(uid: string, body: IPutDeedsParams): Promise<Deed> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Deed>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,86 @@
import { DocumentType } from "le-coffre-resources/dist/Notary";
import BaseNotary from "../BaseNotary";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDocumentTypesparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutDocumentTypesParams = {};
export interface IPostDocumentTypesParams {
name: string;
public_description: string;
private_description: string | null;
office?: {
uid?: string;
};
}
export default class DocumentTypes extends BaseNotary {
private static instance: DocumentTypes;
private readonly baseURl = this.namespaceUrl.concat("/document-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q: IGetDocumentTypesparams): Promise<DocumentType[]> {
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<DocumentType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a Document
*/
public async post(body: IPostDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DocumentType>(url, body as any);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DocumentType> {
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<DocumentType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DocumentType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import { Document } from "le-coffre-resources/dist/Notary";
import BaseNotary from "../BaseNotary";
// 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 BaseNotary {
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,119 @@
import { type OfficeFolder } from "le-coffre-resources/dist/Notary";
import BaseNotary from "../BaseNotary";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetFoldersParams {
q?: {
select?: {};
where?: {};
include?: {};
};
}
export default class Folders extends BaseNotary {
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);
}
}
/**
* @description : Create a folder
*/
public async post(officeFolder: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl);
try {
return await this.postRequest(url, officeFolder);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Update the folder description
*/
public async put(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest(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<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const targetedFolder = await this.getByUid(uid);
if (targetedFolder.customers) return Promise.reject(`The folder ${uid} contains customers`);
try {
return await this.deleteRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async archive(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.ARCHIVED;
try {
return await this.put(uid, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async restore(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.LIVE;
try {
return await this.put(uid, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,48 @@
import BaseNotary from "../BaseNotary";
import User from "le-coffre-resources/dist/Notary";
export type IGetUsersParams = {
where?: {};
include?: {};
select?: {};
};
export default class Users extends BaseNotary {
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(q?: IGetUsersParams): Promise<User[]> {
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<User[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<User> {
const url = new URL(this.baseURl.concat("/").concat(uid));
if (q) Object.entries(q).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<User>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

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,90 @@
import { Contact, Customer } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
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 BaseSuperAdmin {
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);
}
}
/**
* @description : Create a Customer
*/
public async post(body: any): Promise<Customer> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<Customer>(url, body);
} 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);
}
}
public async put(uid: string, body: IPutCustomersParams): Promise<Customer> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Customer>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,92 @@
import { DeedType } from "le-coffre-resources/dist/Notary";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDeedTypesParams {
q?: {};
}
// TODO Type getbyuid query params
export type IPutDeedTypesParams = {
uid?: DeedType["uid"];
name?: DeedType["name"];
description?: DeedType["description"];
deed?: DeedType["deed"];
office?: DeedType["office"];
archived_at?: DeedType["archived_at"];
document_types?: DeedType["document_types"];
};
export default class DeedTypes extends BaseSuperAdmin {
private static instance: DeedTypes;
private readonly baseURl = this.namespaceUrl.concat("/deed-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Get all DeedTypes
*/
public async get(q?: IGetDeedTypesParams): Promise<DeedType[]> {
const url = new URL(this.baseURl);
if(q){
Object.entries(q).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<DeedType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<DeedType> {
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<DeedType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a deed
*/
// public async post(body: IPostDeedTypesParams): Promise<OfficeFolder> {
// const url = new URL(this.baseURl);
// try {
// return await this.postRequest<OfficeFolder>(url, body);
// } catch (err) {
// this.onError(err);
// return Promise.reject(err);
// }
// }
/**
* @description : Update the folder description
*/
public async put(uid: string, body: IPutDeedTypesParams): Promise<DeedType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DeedType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,90 @@
import { Deed, OfficeFolder } from "le-coffre-resources/dist/Notary";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDeedsParams {
q?: {};
}
// TODO Type getbyuid query params
export type IPutDeedsParams = {
uid?: OfficeFolder["uid"];
folder_number?: OfficeFolder["folder_number"];
name?: OfficeFolder["name"];
description?: OfficeFolder["description"];
archived_description?: OfficeFolder["archived_description"];
status?: OfficeFolder["status"];
document_types?: Deed["document_types"];
};
export default class Deeds extends BaseSuperAdmin {
private static instance: Deeds;
private readonly baseURl = this.namespaceUrl.concat("/deeds");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Get all deeds
*/
public async get(q: IGetDeedsParams): Promise<Deed[]> {
const url = new URL(this.baseURl);
Object.entries(q).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Deed[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<Deed> {
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<Deed>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a deed
*/
// public async post(body: IPostDeedsParams): Promise<OfficeFolder> {
// const url = new URL(this.baseURl);
// try {
// return await this.postRequest<OfficeFolder>(url, body);
// } catch (err) {
// this.onError(err);
// return Promise.reject(err);
// }
// }
/**
* @description : Update the folder description
*/
public async put(uid: string, body: IPutDeedsParams): Promise<Deed> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<Deed>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,88 @@
import { DocumentType } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetDocumentTypesparams {
where?: {};
include?: {};
}
// TODO Type getbyuid query params
export type IPutDocumentTypesParams = {};
export interface IPostDocumentTypesParams {
name: string;
public_description: string;
private_description: string;
office: {
uid: string;
};
}
export default class DocumentTypes extends BaseSuperAdmin {
private static instance: DocumentTypes;
private readonly baseURl = this.namespaceUrl.concat("/document-types");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q?: IGetDocumentTypesparams): Promise<DocumentType[]> {
const url = new URL(this.baseURl);
if (q) {
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<DocumentType[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a Document
*/
public async post(body: IPostDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DocumentType>(url, body as any);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DocumentType> {
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<DocumentType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DocumentType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { Document } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
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 BaseSuperAdmin {
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/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// 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 BaseSuperAdmin {
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,119 @@
import { type OfficeFolder } from "le-coffre-resources/dist/Notary";
import BaseSuperAdmin from "../BaseSuperAdmin";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetFoldersParams {
q?: {
select?: {};
where?: {};
include?: {};
};
}
export default class Folders extends BaseSuperAdmin {
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);
}
}
/**
* @description : Create a folder
*/
public async post(officeFolder: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl);
try {
return await this.postRequest(url, officeFolder);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Update the folder description
*/
public async put(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest(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<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const targetedFolder = await this.getByUid(uid);
if (targetedFolder.customers) return Promise.reject(`The folder ${uid} contains customers`);
try {
return await this.deleteRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async archive(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.ARCHIVED;
try {
return await this.put(uid, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async restore(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.LIVE;
try {
return await this.put(uid, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,49 @@
import { Appointment } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetLiveVotessparams {
where?: {};
include?: {};
select?: {};
}
export type IPostLiveVotesParams = {
appointment: Appointment;
};
export type LiveVote = {
uid: string;
appointment: Appointment;
};
export default class LiveVotes extends BaseSuperAdmin {
private static instance: LiveVotes;
private readonly baseURl = this.namespaceUrl.concat("/live-votes");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Create a LiveVotes
*/
public async post(body: IPostLiveVotesParams): Promise<LiveVote> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<LiveVote>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,53 @@
import { Office } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
export interface IGetOfficesparams {
where?: {};
include?: {};
select?: {};
}
export default class Offices extends BaseSuperAdmin {
private static instance: Offices;
private readonly baseURl = this.namespaceUrl.concat("/offices");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q?: IGetOfficesparams): Promise<Office[]> {
const url = new URL(this.baseURl);
if (q) {
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<Office[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<Office> {
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<Office>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,91 @@
import User from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetUsersparams {
where?:{},
include?:{},
select?:{},
}
// TODO Type getbyuid query params
export type IPutUsersParams = {
uid?: User["uid"];
idNot?: User["idNot"];
contact?: User["contact"];
office_membership?: User["office_membership"];
documents?: User["documents"];
};
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 this();
} else {
return this.instance;
}
}
/**
* @description : Get all Users
*/
public async get(q: IGetUsersparams): Promise<User[]> {
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<User[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Get a folder by uid
*/
public async getByUid(uid: string, q?: any): Promise<User> {
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<User>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a User
*/
// public async post(body: IPostDeedsParams): Promise<OfficeFolder> {
// const url = new URL(this.baseURl);
// try {
// return await this.postRequest<OfficeFolder>(url, body);
// } catch (err) {
// this.onError(err);
// return Promise.reject(err);
// }
// }
/**
* @description : Update the folder description
*/
public async put(uid: string, body: IPutUsersParams): Promise<User> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<User>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,44 @@
import { Vote } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetVotessparams {
where?: {};
include?: {};
select?: {};
}
export type IDeleteVotesParams = {
uid: Vote["uid"];
};
export default class Votes extends BaseSuperAdmin {
private static instance: Votes;
private readonly baseURl = this.namespaceUrl.concat("/votes");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
/**
* @description : Create a Votes
*/
public async delete(body: IDeleteVotesParams): Promise<Vote> {
const url = new URL(this.baseURl + "/" + body.uid);
try {
return await this.deleteRequest<Vote>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -3,7 +3,9 @@ import CookieService from "@Front/Services/CookieService/CookieService";
export enum ContentType {
JSON = "application/json",
PDF = "application/pdf",
FORM_DATA = "multipart/form-data;",
PNG = "image/png",
}
export default abstract class BaseApiService {
private static baseUrl: string;
@ -26,7 +28,7 @@ export default abstract class BaseApiService {
const headers = new Headers();
if (contentType === ContentType.JSON) {
if (contentType === ContentType.JSON || contentType === ContentType.PDF) {
headers.set("Content-Type", contentType);
}
headers.set("Authorization", `Bearer ${token}`);
@ -37,13 +39,13 @@ export default abstract class BaseApiService {
return JSON.stringify(body);
}
protected async getRequest<T>(url: URL, token?: string) {
protected async getRequest<T>(url: URL, token?: string, contentType?: ContentType, filename?: string) {
const request = async () =>
await fetch(url, {
method: "GET",
headers: this.buildHeaders(ContentType.JSON),
headers: this.buildHeaders(contentType ?? ContentType.JSON),
});
return this.sendRequest<T>(request);
return this.sendRequest<T>(request, filename);
}
protected async postRequest<T>(url: URL, body: { [key: string]: unknown } = {}, token?: string) {
@ -112,25 +114,39 @@ export default abstract class BaseApiService {
return this.sendRequest<T>(request);
}
private async sendRequest<T>(request: () => Promise<Response>): Promise<T> {
private async sendRequest<T>(request: () => Promise<Response>, filename?: string): Promise<T> {
const response = await request();
return this.processResponse<T>(response, request);
return this.processResponse<T>(response, request, filename);
}
protected async processResponse<T>(response: Response, request: () => Promise<Response>): Promise<T> {
let responseJson: any | null;
protected async processResponse<T>(response: Response, request: () => Promise<Response>, filename?: string): Promise<T> {
let responseContent: T;
if (response.ok) {
// Check the Content-Type header to determine the response type
const contentType = response.headers.get("Content-Type");
if (contentType && !contentType.includes("application/json")) {
responseContent = (await response.blob()) as T;
} else {
// Handle JSON response
try {
responseJson = await response.json();
} catch (err: unknown) {
responseJson = null;
responseContent = await response.json();
} catch (err) {
return Promise.reject(err);
}
if (!response.ok) {
}
} else {
// Handle error response
try {
const responseJson = await response.json();
return Promise.reject(responseJson);
} catch (err) {
return Promise.reject(err);
}
}
return responseJson as T;
return responseContent;
}
protected onError(error: unknown) {

View File

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

View File

@ -0,0 +1,45 @@
import BaseId360 from "../BaseId360";
export interface IConnectionUrlResponse {
enrollment: {
franceConnectUrl: string;
processId: string;
}
}
export default class Customers extends BaseId360 {
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 login(): Promise<IConnectionUrlResponse> {
const url = new URL(this.baseURl.concat(`/login`));
try {
return await this.postRequest<IConnectionUrlResponse>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async loginCallback(callbackToken: string | string[]): Promise<any> {
const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`));
try {
return await this.postRequest<any>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,103 @@
import { File } from "le-coffre-resources/dist/SuperAdmin";
import BaseNotary from "../BaseNotary";
// 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 BaseNotary {
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);
}
}
public async download(uid: string): Promise<any> {
const url = new URL(this.baseURl.concat(`/download/${uid}`));
try {
return await this.getRequest<any>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,54 @@
import BaseNotary from "../BaseNotary";
import { UserNotification } from "le-coffre-resources/dist/Notary";
// TODO Type get query params -> Where + inclue + orderby
export interface IGetNotificationsParams {
where?: {};
include?: {};
select?: {};
}
export type IPutNotificationsParams = {
read?: boolean;
};
export default class Notifications extends BaseNotary {
private static instance: Notifications;
private baseUrl = this.namespaceUrl.concat("/notifications");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q?: IGetNotificationsParams): Promise<UserNotification[]> {
const url = new URL(this.baseUrl);
if (q) {
const query = { q };
Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
}
try {
return await this.getRequest<UserNotification[]>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutNotificationsParams): Promise<UserNotification> {
const url = new URL(this.baseUrl.concat(`/${uid}`));
try {
return await this.putRequest<UserNotification>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,49 @@
import { ContentType } from "@Front/Api/BaseApiService";
import BaseNotary from "../BaseNotary";
export default class OfficeFolderAnchors extends BaseNotary {
private static instance: OfficeFolderAnchors;
private readonly baseURl = this.namespaceUrl.concat("/anchors");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(uid: string): Promise<any> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.getRequest<any>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async post(uid: string): Promise<any> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.postRequest<any>(url, {});
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async download(uid: string): Promise<any> {
const url = new URL(this.baseURl.concat(`/download/${uid}`));
try {
return await this.getRequest<any>(url, undefined, ContentType.PDF, `${uid}.pdf`);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.2251 3.36088C13.4893 3.59571 13.5131 4.00024 13.2783 4.26442L6.4516 11.9444C6.33015 12.0811 6.15607 12.1592 5.97326 12.1592C5.79045 12.1592 5.61637 12.0811 5.49492 11.9444L2.08159 8.10442C1.84676 7.84024 1.87055 7.43571 2.13474 7.20088C2.39892 6.96606 2.80344 6.98985 3.03827 7.25403L5.97326 10.5559L12.3216 3.41403C12.5564 3.14985 12.9609 3.12606 13.2251 3.36088Z" fill="white"/>
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 6L8.375 16L4 11.4545" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 219 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.2251 3.36088C13.4893 3.59571 13.5131 4.00024 13.2783 4.26442L6.4516 11.9444C6.33015 12.0811 6.15607 12.1592 5.97326 12.1592C5.79045 12.1592 5.61637 12.0811 5.49492 11.9444L2.08159 8.10442C1.84676 7.84024 1.87055 7.43571 2.13474 7.20088C2.39892 6.96606 2.80344 6.98985 3.03827 7.25403L5.97326 10.5559L12.3216 3.41403C12.5564 3.14985 12.9609 3.12606 13.2251 3.36088Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 497 B

View File

@ -26,4 +26,10 @@
margin-left: 32px;
}
}
.right-side {
display: flex;
align-items: center;
gap: 16px;
}
}

View File

@ -3,11 +3,13 @@ import classes from "./classes.module.scss";
import Typography, { ITypo } from "../Typography";
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Image from "next/image";
import WarningBadge from "../WarningBadge";
export type IBlock = {
name: string;
id: string;
selected: boolean;
hasFlag?: boolean;
};
type IProps = {
@ -30,9 +32,12 @@ export default function BlockList({ blocks, onSelectedBlock }: IProps) {
<div className={classes["left-side"]}>
<Typography typo={ITypo.P_16}>{folder.name}</Typography>
</div>
<div className={classes["right-side"]}>
{folder.hasFlag && <WarningBadge />}
<Image alt="chevron" src={ChevronIcon} />
</div>
</div>
</div>
);
})}
</div>

View File

@ -18,7 +18,7 @@
}
input[type="checkbox"]::before {
content: url("../../../Assets/Icons/check.svg");
content: url("../../../Assets/Icons/check_white.svg");
place-content: flex-start;
display: grid;
width: 16px;

View File

@ -207,7 +207,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
</div>
{this.props.document.document_status === EDocumentStatus.REFUSED && (
<Typography typo={ITypo.CAPTION_14} className={classes["error-message"]}>
Ce document nest pas conforme. Veuillez le déposer à nouveau.
Ce document n'est pas conforme. Veuillez le déposer à nouveau.
</Typography>
)}
</div>

View File

@ -5,6 +5,7 @@
border: 1px dashed #e7e7e7;
height: fit-content;
margin-top: 16px;
&[data-drag-over="true"] {
border: 1px dashed var(--grey);

View File

@ -39,6 +39,7 @@ type IState = {
refusedReason?: string;
isShowRefusedReasonModalVisible: boolean;
isAddDocumentModalVisible: boolean;
isLoading: boolean;
};
export default class DepositOtherDocument extends React.Component<IProps, IState> {
@ -54,6 +55,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
isDragOver: false,
refusedReason: "",
isShowRefusedReasonModalVisible: false,
isLoading: false,
};
this.addDocument = this.addDocument.bind(this);
@ -65,45 +67,17 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
this.onAccept = this.onAccept.bind(this);
}
private async onAccept() {
const filesArray = this.state.currentFiles;
if (!filesArray) return;
const documentCreated = await Documents.getInstance().post({
folder: {
uid: this.props.folder_uid,
},
depositor: {
uid: this.props.customer_uid,
},
});
console.log(documentCreated);
filesArray.forEach(async (file) => {
const formData = new FormData();
formData.append("file", file.file, file.fileName);
const query = JSON.stringify({ document: { uid: documentCreated.uid } });
formData.append("q", query);
const newFile = await Files.getInstance().post(formData);
console.log(newFile);
});
this.props.onClose!();
}
public override render(): JSX.Element {
return (
<Confirm
isOpen={this.state.isAddDocumentModalVisible}
onClose={this.props.onClose!}
onClose={() => {}}
onAccept={this.onAccept}
closeBtn
header={"Ajouter un document"}
cancelText={"Annuler"}
confirmText={"Déposer le document"}>
confirmText={this.state.isLoading ? "Chargement..." : "Déposer le document"}
canConfirm={!this.state.isLoading}>
<div className={classes["modal-content"]}>
<div className={classes["container"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
@ -140,7 +114,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
</div>
</Typography>
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
Sélectionnez des TEST documents .jpg, .pdf ou .png
Sélectionnez des documents .jpg, .pdf ou .png
</Typography>
</div>
</div>
@ -187,6 +161,38 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
public override componentDidMount(): void {}
private async onAccept() {
this.setState({
isLoading: true,
});
const filesArray = this.state.currentFiles;
if (!filesArray) return;
const documentCreated = await Documents.getInstance().post({
folder: {
uid: this.props.folder_uid,
},
depositor: {
uid: this.props.customer_uid,
},
});
for (let i = 0; i < filesArray.length; i++) {
const formData = new FormData();
formData.append("file", filesArray[i]!.file, filesArray[i]!.fileName);
const query = JSON.stringify({ document: { uid: documentCreated.uid } });
formData.append("q", query);
await Files.getInstance().post(formData);
}
this.setState({
isLoading: false,
});
this.props.onClose!();
}
private shortName(name: string): string {
const maxLength = 20;
if (name.length > maxLength) {

View File

@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import React from "react";
import Typography, { ITypo, ITypoColor } from "../Typography";
@ -20,6 +21,8 @@ export default class FilePreview extends React.Component<IProps, IState> {
<Loader />
</div>
{this.props.href && (
<>
{!type && (
<Typography typo={ITypo.H1} color={ITypoColor.BLACK}>
Erreur lors du chargement du fichier
@ -31,6 +34,8 @@ export default class FilePreview extends React.Component<IProps, IState> {
)}
{type?.toLowerCase() !== "pdf" && <img src={this.props.href} alt="File preview" className={classes["image"]} />}
</div>
</>
)}
</div>
);
}

View File

@ -75,6 +75,11 @@ class FolderListContainerClass extends React.Component<IPropsClass, IState> {
);
}
public override componentDidUpdate(prevProps: Readonly<IPropsClass>, prevState: Readonly<IState>, snapshot?: any): void {
if (prevProps.selectedFolder !== this.props.selectedFolder) {
this.setState({ filteredFolders: this.props.folders, blocks: this.getBlocks(this.props.folders) });
}
}
private getBlocks(folders: OfficeFolder[]): IBlock[] {
const pendingFolders = folders
.filter((folder) => {
@ -103,6 +108,7 @@ class FolderListContainerClass extends React.Component<IPropsClass, IState> {
id: folder.uid!,
name: folder.folder_number! + " - " + folder.name!,
selected: this.props.selectedFolder === folder.uid,
hasFlag: folder.documents?.some((document) => document.document_status === EDocumentStatus.DEPOSITED),
};
});
}

View File

@ -0,0 +1,102 @@
@import "@Themes/constants.scss";
.root {
position: relative;
.input {
z-index: 1;
display: flex;
flex-direction: row;
align-items: center;
padding: 24px;
gap: 10px;
width: 100%;
height: 70px;
border: 1px solid var(--grey-medium);
&:disabled {
cursor: not-allowed;
}
&:focus {
~ .fake-placeholder {
transform: translateY(-35px);
}
}
&:not([data-value=""]) {
~ .fake-placeholder {
transform: translateY(-35px);
}
}
&[type="number"] {
&:focus {
~ .fake-placeholder {
transform: translateY(-35px);
}
}
&:not([data-value=""]) {
~ .fake-placeholder {
transform: translateY(-35px);
}
}
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* For Chrome, Safari, and Opera */
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* For IE 10+ */
&::-ms-inner-spin-button,
&::-ms-outer-spin-button {
display: none;
}
}
&:not([data-value=""]) {
~ .fake-placeholder {
transform: translateY(-35px);
}
}
~ .fake-placeholder {
z-index: 2;
top: 35%;
margin-left: 8px;
padding: 0 16px;
pointer-events: none;
position: absolute;
background: $white;
transition: transform 0.3s ease-in-out;
}
}
&[data-is-errored="true"] {
.input {
border: 1px solid var(--red-flash);
~ .fake-placeholder {
color: var(--red-flash);
}
}
}
.copy-icon {
cursor: pointer;
height: 24px;
width: 24px;
position: absolute;
top: 50%;
right: 24px;
transform: translate(0, -50%);
}
}

View File

@ -0,0 +1,56 @@
import React from "react";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import { ReactNode } from "react";
import CopyIcon from "@Assets/Icons/copy.svg";
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
import classes from "./classes.module.scss";
import classnames from "classnames";
import Image from "next/image";
export type IProps = IBaseFieldProps & {
canCopy?: boolean;
};
export default class DateField extends BaseField<IProps> {
constructor(props: IProps) {
super(props);
this.state = this.getDefaultState();
}
public override render(): ReactNode {
const value = this.state.value ?? "";
return (
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["root"]} data-is-errored={this.hasError().toString()}>
<input
onChange={this.onChange}
data-value={value}
data-has-validation-errors={(this.state.validationError === null).toString()}
className={classnames(classes["input"], this.props.className)}
value={value}
onFocus={this.onFocus}
onBlur={this.onBlur}
name={this.props.name}
disabled={this.props.disabled}
type={"date"}
/>
<div className={classes["fake-placeholder"]}>
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
</div>
{this.props.canCopy && (
<div className={classes["copy-icon"]} onClick={this.onCopyClick}>
<Image src={CopyIcon} alt="Copy icon" />
</div>
)}
</div>
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
</Typography>
);
}
private onCopyClick = (): void => {
if (this.props.canCopy) {
navigator.clipboard.writeText(this.state.value ?? "");
}
};
}

View File

@ -7,6 +7,7 @@ import React, { FormEvent, ReactNode } from "react";
import Typography, { ITypo, ITypoColor } from "../../Typography";
import classes from "./classes.module.scss";
import { NextRouter, useRouter } from "next/router";
type IProps = {
selectedOption?: IOption;
@ -16,7 +17,7 @@ type IProps = {
placeholder?: string;
className?: string;
name: string;
disabled: boolean;
disabled?: boolean;
errors?: ValidationError;
};
@ -35,7 +36,11 @@ type IState = {
errors: ValidationError | null;
};
export default class SelectField extends React.Component<IProps, IState> {
type IPropsClass = IProps & {
router: NextRouter;
};
class SelectFieldClass extends React.Component<IPropsClass, IState> {
private contentRef = React.createRef<HTMLUListElement>();
private rootRef = React.createRef<HTMLDivElement>();
private removeOnresize = () => {};
@ -44,7 +49,7 @@ export default class SelectField extends React.Component<IProps, IState> {
disabled: false,
};
constructor(props: IProps) {
constructor(props: IPropsClass) {
super(props);
this.state = {
isOpen: false,
@ -64,7 +69,7 @@ export default class SelectField extends React.Component<IProps, IState> {
<div
className={classNames(classes["root"], this.props.className)}
ref={this.rootRef}
data-disabled={this.props.disabled.toString()}
data-disabled={this.props.disabled?.toString()}
data-errored={(this.state.errors !== null).toString()}>
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
<label
@ -116,6 +121,23 @@ export default class SelectField extends React.Component<IProps, IState> {
</div>
);
}
public override componentDidMount(): void {
this.onResize();
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
this.props.router.events.on("routeChangeStart", () => {
this.setState({
isOpen: false,
selectedOption: null,
listHeight: 0,
listWidth: 0,
});
});
}
public override componentWillUnmount() {
this.removeOnresize();
}
public override componentDidUpdate(prevProps: IProps) {
if (this.props.errors !== prevProps.errors) {
@ -140,15 +162,6 @@ export default class SelectField extends React.Component<IProps, IState> {
return null;
}
public override componentDidMount(): void {
this.onResize();
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
}
public override componentWillUnmount() {
this.removeOnresize();
}
private onResize() {
let listHeight = 0;
let listWidth = 0;
@ -192,3 +205,8 @@ export default class SelectField extends React.Component<IProps, IState> {
);
}
}
export default function SelectField(props: IProps) {
const router = useRouter();
return <SelectFieldClass {...props} router={router} />;
}

View File

@ -92,8 +92,8 @@ export default class BurgerModal extends React.Component<IProps, IState> {
mode={RulesMode.NECESSARY}
rules={[
{
action: AppRuleActions.create,
name: AppRuleNames.officeRoles,
action: AppRuleActions.update,
name: AppRuleNames.offices,
},
]}>
<NavigationLink
@ -123,7 +123,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
/>
</Rules>
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
<NavigationLink text="CGU" />
<NavigationLink target="_blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
<div className={classes["separator"]} />
<LogOutButton />
</div>

View File

@ -1,16 +1,35 @@
import Module from "@Front/Config/Module";
import React from "react";
import React, { useCallback, useEffect } from "react";
import HeaderLink from "../HeaderLink";
import classes from "./classes.module.scss";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
import { usePathname } from "next/navigation";
import Notifications from "@Front/Api/LeCoffreApi/Notary/Notifications/Notifications";
import Toasts from "@Front/Stores/Toasts";
export default function Navigation() {
const pathname = usePathname();
type IProps = {};
type IState = {};
const getNotifications = useCallback(async () => {
const notifications = await Notifications.getInstance().get({
where: {
read: false,
},
});
notifications.forEach((notification) => {
Toasts.getInstance().open({
title: notification.notification.message,
uid: notification.uid,
redirectUrl: notification.notification.redirection_url,
});
});
}, []);
useEffect(() => {
getNotifications();
}, [pathname, getNotifications]);
export default class Navigation extends React.Component<IProps, IState> {
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<HeaderLink
@ -42,5 +61,4 @@ export default class Navigation extends React.Component<IProps, IState> {
</Rules>
</div>
);
}
}

View File

@ -12,6 +12,7 @@ type IProps = {
isEnabled?: boolean;
isActive?: boolean;
routesActive?: string[];
target?: "blank" | "self" | "_blank";
};
type IPropsClass = IProps;
@ -25,7 +26,8 @@ class NavigationLinkClass extends React.Component<IPropsClass, IStateClass> {
<Link
href={this.props.path ?? ""}
className={classNames(classes["root"], this.props.isActive && [classes["active"]])}
onClick={this.props.onClick}>
onClick={this.props.onClick}
target={this.props.target}>
<div className={classes["content"]}>
<Typography typo={this.props.isActive ? ITypo.P_SB_18 : ITypo.NAV_HEADER_18}>{this.props.text}</Typography>
</div>
@ -38,7 +40,7 @@ export default function NavigationLink(props: IProps) {
const router = useRouter();
const { pathname } = router;
let isActive = props.path === pathname;
if(props.routesActive){
if (props.routesActive) {
for (const routeActive of props.routesActive) {
if (isActive) break;
isActive = pathname.includes(routeActive);

View File

@ -66,8 +66,8 @@ export default class ProfileModal extends React.Component<IProps, IState> {
mode={RulesMode.NECESSARY}
rules={[
{
action: AppRuleActions.create,
name: AppRuleNames.officeRoles,
action: AppRuleActions.update,
name: AppRuleNames.offices,
},
]}>
<NavigationLink
@ -96,7 +96,7 @@ export default class ProfileModal extends React.Component<IProps, IState> {
]}
/>
</Rules>
<NavigationLink text="CGU" />
<NavigationLink target="_blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
<div className={classes["separator"]} />
<LogOutButton />
</div>

View File

@ -21,6 +21,9 @@
}
.logo-container {
> a {
cursor: default !important;
}
.logo {
width: 174px;
height: 39px;

View File

@ -61,7 +61,7 @@ class HeaderClass extends React.Component<IPropsClass, IState> {
<link rel="shortcut icon" href={"/favicon.svg"} />
</Head>
<div className={classes["logo-container"]}>
<Link href={Module.getInstance().get().modules.pages.Home.props.path}>
<Link href={"#"}>
<Image src={LogoIcon} alt="logo" className={classes["logo"]} />
</Link>
</div>

View File

@ -23,8 +23,8 @@
input[type="radio"]::before {
content: "";
width: 10px;
height: 10px;
width: 11px;
height: 11px;
background-color: $turquoise-flash;
border-radius: 100px;
transform: scale(0);

View File

@ -26,7 +26,7 @@
pointer-events: all;
position: relative;
padding: 24px;
background: $orange-soft;
background: var(--orange-soft);
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.11);
border-radius: 5px;
@ -42,6 +42,13 @@
animation-fill-mode: forwards;
}
&[data-clickable="true"] {
cursor: pointer;
&:hover {
background: var(--orange-soft-hover);
}
}
.loadbar {
position: absolute;
top: 0;

View File

@ -8,18 +8,25 @@ import React from "react";
import classes from "./classes.module.scss";
import Toasts, { IToast } from "@Front/Stores/Toasts";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import CheckIcon from "@Assets/Icons/check.svg";
import Image from "next/image";
import { NextRouter, useRouter } from "next/router";
type IProps = {
toast: IToast;
};
type IPropsClass = IProps & {
router: NextRouter;
};
type IState = {
willClose: boolean;
};
export default class ToastElement extends React.Component<IProps, IState> {
class ToastElementClass extends React.Component<IPropsClass, IState> {
private closeTimeout = 0;
constructor(props: IProps) {
constructor(props: IPropsClass) {
super(props);
this.state = {
@ -27,6 +34,7 @@ export default class ToastElement extends React.Component<IProps, IState> {
};
this.onClose = this.onClose.bind(this);
this.handleClick = this.handleClick.bind(this);
}
public override render(): JSX.Element {
@ -35,7 +43,11 @@ export default class ToastElement extends React.Component<IProps, IState> {
"--data-duration": `${toast.time}ms`,
} as React.CSSProperties;
return (
<div className={classes["root"]} data-will-close={this.state.willClose}>
<div
className={classes["root"]}
data-will-close={this.state.willClose}
data-clickable={toast.redirectUrl ? true : false}
onClick={this.handleClick}>
{toast.time !== 0 && <div className={classes["loadbar"]} style={style} />}
<div className={classes["header"]}>
<div className={classes["text-icon_row"]}>
@ -45,7 +57,7 @@ export default class ToastElement extends React.Component<IProps, IState> {
{this.getToastText(toast.text)}
</div>
</div>
{/* {toast.closable && <Cross className={classes["cross"]} onClick={this.onClose} />} */}
{toast.closable && <Image src={CheckIcon} alt="Document check" className={classes["cross"]} onClick={this.onClose} />}
</div>
{toast.button}
</div>
@ -95,4 +107,16 @@ export default class ToastElement extends React.Component<IProps, IState> {
Toasts.getInstance().close(this.props.toast);
}, 200);
}
private handleClick(e: React.MouseEvent) {
if (this.props.toast.redirectUrl) {
this.props.router.push(this.props.toast.redirectUrl);
this.onClose(e);
}
}
}
export default function ToastElement(props: IProps) {
const router = useRouter();
return <ToastElementClass {...props} router={router} />;
}

View File

@ -1,6 +1,6 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import PlusIcon from "@Assets/Icons/plus.svg";
import Documents from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents";
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
import Module from "@Front/Config/Module";
import classNames from "classnames";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
@ -142,7 +142,9 @@ export default class UserFolder extends React.Component<IProps, IState> {
const documents = this.props.customer.documents.filter((document) => document.folder?.uid === this.props.folder.uid);
const totalDocuments: number = documents.length;
const numberDocumentsRefused: number = this.getDocumentsByStatus(EDocumentStatus.REFUSED)?.length || 0;
const numberDocumentsAsked: number = this.getDocumentsByStatus(EDocumentStatus.ASKED)?.length || 0;
const numberDocumentsAsked: number =
(this.getDocumentsByStatus(EDocumentStatus.ASKED)?.length || 0) +
(this.getDocumentsByStatus(EDocumentStatus.DEPOSITED)?.length || 0);
const depositedDocuments: number = totalDocuments - numberDocumentsAsked - numberDocumentsRefused;
const percentage = (depositedDocuments / totalDocuments) * 100;

View File

@ -1,8 +1,8 @@
import React, { useCallback, useEffect } from "react";
import { useRouter } from "next/router";
import Module from "@Front/Config/Module";
import JwtService from "@Front/Services/JwtService/JwtService";
import { IAppRule } from "@Front/Api/Entities/rule";
import { useRouter } from "next/router";
import Module from "@Front/Config/Module";
export enum RulesMode {
OPTIONAL = "optional",
@ -10,16 +10,18 @@ export enum RulesMode {
}
type IProps = {
isPage?: boolean;
mode: RulesMode;
rules: IAppRule[];
no?: boolean;
children: JSX.Element;
isPage?: boolean;
};
export default function Rules(props: IProps) {
const router = useRouter();
const [isShowing, setIsShowing] = React.useState(false);
const [hasJwt, setHasJwt] = React.useState(false);
const getShowValue = useCallback(() => {
if (props.mode === RulesMode.NECESSARY) {
return props.rules.every((rule) => JwtService.getInstance().hasRule(rule.name, rule.action));
@ -27,19 +29,18 @@ export default function Rules(props: IProps) {
return !!props.rules.find((rule) => JwtService.getInstance().hasRule(rule.name, rule.action));
}, [props.mode, props.rules]);
const show = getShowValue();
const [isShowing, setIsShowing] = React.useState(props.no ? !show : show);
useEffect(() => {
setIsShowing(props.no ? !show : show);
}, [props.no, show]);
if (!JwtService.getInstance().decodeJwt()) return;
setHasJwt(true);
setIsShowing(getShowValue());
}, [getShowValue, isShowing]);
if (!isShowing && props.isPage) {
router.push(Module.getInstance().get().modules.pages.Home.props.path);
}
if (!JwtService.getInstance().decodeJwt() || !isShowing) {
if (props.isPage && !isShowing) {
router.push(Module.getInstance().get().modules.pages[404].props.path);
return null;
}
if (!hasJwt || !isShowing) return null;
return props.children;
}

View File

@ -1,5 +1,4 @@
"use client";
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";
@ -10,7 +9,7 @@ import React, { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
import { useRouter } from "next/router";
import JwtService from "@Front/Services/JwtService/JwtService";
import JwtService, { ICustomerJwtPayload } from "@Front/Services/JwtService/JwtService";
import DepositOtherDocument from "@Front/Components/DesignSystem/DepositOtherDocument";
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
@ -24,27 +23,16 @@ export default function ClientDashboard(props: IProps) {
const [folder, setFolder] = useState<OfficeFolder | null>(null);
const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false);
const onCloseModalAddDocument = useCallback(() => {
console.log("Closing");
setIsAddDocumentModalVisible(false);
}, []);
const onOpenModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(true);
}, []);
useEffect(() => {
async function getDocuments() {
let jwt;
const getDocuments = useCallback(async () => {
let jwt: ICustomerJwtPayload | undefined;
if (typeof document !== "undefined") {
jwt = JwtService.getInstance().decodeJwt();
jwt = JwtService.getInstance().decodeCustomerJwt();
}
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 folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } });
console.log(folder);
const actualCustomer = folder?.customers?.find((customer) => customer.uid === jwt?.customerId);
if (!actualCustomer) throw new Error("Customer not found");
const query: IGetDocumentsparams = {
where: { depositor: { uid: actualCustomer.uid }, folder_uid: folderUid as string },
@ -58,17 +46,27 @@ export default function ClientDashboard(props: IProps) {
const documentList = await Documents.getInstance().get(query);
const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true } });
//const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } });
setFolder(folder);
setDocuments(documentList);
setCustomer(actualCustomer);
}
getDocuments();
}, [folderUid]);
const onCloseModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(false);
getDocuments();
}, [getDocuments]);
const onOpenModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(true);
}, []);
useEffect(() => {
getDocuments();
}, [folderUid, getDocuments]);
const renderHeader = useCallback(() => {
console.log("Dossier : ", customer);
return (
<div className={classes["header"]}>
<div className={classes["text-container"]}>
@ -93,19 +91,14 @@ export default function ClientDashboard(props: IProps) {
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.
l'aide 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>
);
}, [customer]);
}, [customer, folder?.folder_number, folder?.name, folder?.office?.name]);
const renderBox = useCallback(() => {
console.log(isAddDocumentModalVisible);
return (
<DepositOtherDocument
folder_uid={folderUid!}
@ -114,7 +107,7 @@ export default function ClientDashboard(props: IProps) {
onClose={onCloseModalAddDocument}
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
name: "Autres documents",
}),
})}
/>

View File

@ -1,5 +1,4 @@
import Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
//import Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
@ -11,7 +10,6 @@ import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Custo
import React from "react";
import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
type IProps = {};
type IState = {
@ -71,7 +69,7 @@ export default class ClientDashboard extends Base<IProps, IState> {
<DepositDocument
document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({
name: "Document annexe",
name: "Autres documents",
}),
})}
/>
@ -99,37 +97,34 @@ export default class ClientDashboard extends Base<IProps, IState> {
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.
l'aide 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
// 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 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 });
}
// 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 });

View File

@ -45,9 +45,9 @@ export default function CollaboratorInformations(props: IProps) {
setRoleModalOpened(false);
setSelectedOption({
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 : "Utilisateur restreint",
});
}, [userSelected?.office_role, userSelected?.role?.name, userSelected?.role?.uid]);
}, [userSelected?.office_role, userSelected?.role?.uid]);
const changeRole = useCallback(async () => {
await Users.getInstance().put(
@ -133,7 +133,7 @@ export default function CollaboratorInformations(props: IProps) {
setUserSelected(user);
setSelectedOption({
value: user?.office_role ? user?.office_role?.uid : user?.role?.uid,
label: user?.office_role ? user?.office_role?.name : user?.role?.name!,
label: user?.office_role ? user?.office_role?.name : "Utilisateur restreint",
});
}
@ -197,7 +197,6 @@ export default function CollaboratorInformations(props: IProps) {
})}
selectedOption={selectedOption!}
onChange={handleRoleChange}
disabled={userSelected?.role?.name === "super-admin"}
/>
</div>
{userSelected?.role?.name !== "super-admin" && (

View File

@ -72,6 +72,9 @@
border: 1px solid var(--grey);
.container-title {
display: flex;
gap: 8px;
align-items: center;
}
.documents {

View File

@ -19,6 +19,7 @@ import { useCallback, useEffect, useState } from "react";
import { MultiValue } from "react-select";
import classes from "./classes.module.scss";
import Tooltip from "@Front/Components/DesignSystem/ToolTip";
type IProps = {};
export default function DeedTypesInformations(props: IProps) {
@ -30,6 +31,7 @@ export default function DeedTypesInformations(props: IProps) {
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
const [isDeleteModalOpened, setIsDeleteModalOpened] = useState<boolean>(false);
const [isSaveModalOpened, setIsSaveModalOpened] = useState<boolean>(false);
const openDeleteModal = useCallback(() => {
setIsDeleteModalOpened(true);
@ -39,6 +41,14 @@ export default function DeedTypesInformations(props: IProps) {
setIsDeleteModalOpened(false);
}, []);
const openSaveModal = useCallback(() => {
setIsSaveModalOpened(true);
}, []);
const closeSaveModal = useCallback(() => {
setIsSaveModalOpened(false);
}, []);
const deleteDeedType = useCallback(async () => {
await DeedTypes.getInstance().put(
deedTypeUid as string,
@ -81,13 +91,18 @@ export default function DeedTypesInformations(props: IProps) {
const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
openSaveModal();
},
[openSaveModal],
);
const saveDocumentTypes = useCallback(async () => {
await DeedTypes.getInstance().put(deedTypeUid as string, {
uid: deedTypeUid as string,
document_types: selectedDocuments.map((document) => DocumentType.hydrate<DocumentType>({ uid: document.value as string })),
});
},
[deedTypeUid, selectedDocuments],
);
closeSaveModal();
}, [closeSaveModal, deedTypeUid, selectedDocuments]);
const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
setSelectedDocuments(values as IOption[]);
@ -137,7 +152,8 @@ export default function DeedTypesInformations(props: IProps) {
<div className={classes["documents-container"]}>
<Form onSubmit={onSubmitHandler}>
<div className={classes["container-title"]}>
<Typography typo={ITypo.P_SB_18}>Documents paramétrés</Typography>
<Typography typo={ITypo.P_SB_18}>Sélectionner les documents associés à ce type d'acte</Typography>
<Tooltip text="Si vous ne trouvez pas le document que vous souhaitez dans la liste, cliquez sur « Modifier la liste des documents » pour créer ce type de document à la liste" />
</div>
<div className={classes["documents"]}>
<MultiSelect
@ -171,6 +187,20 @@ export default function DeedTypesInformations(props: IProps) {
</Typography>
</div>
</Confirm>
<Confirm
isOpen={isSaveModalOpened}
onClose={closeSaveModal}
onAccept={saveDocumentTypes}
closeBtn
header={"Enregistrer les modifications ?"}
confirmText={"Enregistrer"}
cancelText={"Annuler"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Les documents seront associés à ce type d'acte.
</Typography>
</div>
</Confirm>
</div>
</DefaultDeedTypesDashboard>
);

View File

@ -29,13 +29,15 @@ export default function DocumentTypesCreate(props: IProps) {
});
const documentToCreate = DocumentType.hydrate<DocumentType>({
...values,
office: office
office: office,
});
await validateOrReject(documentToCreate, { groups: ["createDocumentType"] });
const documentTypeCreated = await DocumentTypes.getInstance().post(documentToCreate);
router.push(
Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentTypeCreated.uid!),
Module.getInstance()
.get()
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace("[uid]", documentTypeCreated.uid!),
);
} catch (e) {
if (e instanceof Array) {

View File

@ -70,7 +70,7 @@ export default function DocumentTypesEdit() {
/>
<TextAreaField
name="public_description"
placeholder="Description visible par les clients de loffice"
placeholder="Description visible par les clients de l'office"
defaultValue={documentTypeSelected?.public_description}
/>
<div className={classes["buttons-container"]}>

View File

@ -1,6 +1,18 @@
@import "@Themes/constants.scss";
.root {
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
@media (max-width: $screen-l) {
flex-direction: column;
align-items: flex-start;
gap: 24px;
}
}
.document-infos {
display: flex;
align-items: flex-start;

View File

@ -1,3 +1,4 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import PenICon from "@Assets/Icons/pen.svg";
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
@ -10,6 +11,7 @@ import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import classes from "./classes.module.scss";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
export default function DocumentTypesInformations() {
const router = useRouter();
@ -33,8 +35,14 @@ export default function DocumentTypesInformations() {
return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des collaborateurs"}>
<div className={classes["root"]}>
<div className={classes["folder-header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography>
<Link href={Module.getInstance().get().modules.pages.DeedTypes.props.path}>
<Button variant={EButtonVariant.LINE}>
Retour au paramétrage des types d'actes
<Image src={ChevronIcon} alt="Chevron" />
</Button>
</Link>
</div>
<div className={classes["document-infos"]}>
<div className={classes["left"]}>

View File

@ -9,7 +9,6 @@ import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Module from "@Front/Config/Module";
import { TextField } from "@mui/material";
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
import { Customer, OfficeFolder } from "le-coffre-resources/dist/Notary";
import Link from "next/link";
@ -17,6 +16,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
enum ESelectedOption {
EXISTING_CUSTOMER = "existing_customer",
@ -211,7 +211,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
contact: values,
});
if (!customer.uid) return;
customersToLink?.push({ customer: { uid: customer.uid } } as Partial<Customer>);
customersToLink?.push({ uid: customer.uid } as Partial<Customer>);
}
if (customersToLink) {
@ -220,7 +220,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
return Customer.hydrate<Customer>(customer);
}),
});
console.log(body);
await Folders.getInstance().put(this.props.selectedFolderUid, body);
this.props.router.push(`/folders/${this.props.selectedFolderUid}`);
}

View File

@ -157,23 +157,25 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
}
private async getAvailableDocuments(folder: OfficeFolder): Promise<IOption[]> {
// Getting already asked documents UIDs in an array
const userDocumentTypesUids = folder
.documents!.filter((document) => document.depositor!.uid! === this.props.customerUid!)
.map((document) => {
return document.document_type!.uid!;
});
// If those UIDs are already asked, filter them to not show them in the list and only
// show the documents that are not asked yet
const documentTypes = folder.deed!.document_types!.filter((documentType) => {
if (userDocumentTypesUids.includes(documentType!.uid!)) return false;
return true;
});
// If there is none document type available, return an empty array
if (!documentTypes) return [];
const documentTypesOptions: IOption[] = documentTypes
.filter((documentType) => {
return true;
})
.map((documentType) => {
// Else, return an array document types formatted as IOPtions
const documentTypesOptions: IOption[] = documentTypes.map((documentType) => {
return {
label: documentType!.name!,
value: documentType!.uid!,

View File

@ -67,7 +67,8 @@
}
@media (max-width: $screen-m) {
display: block;
display: flex;
flex-direction: column;
.delete-folder {
margin-left: 0;

View File

@ -1,5 +1,6 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
@ -34,6 +35,8 @@ type IState = {
inputArchivedDescripton: string;
isValidateModalVisible: boolean;
hasValidateAnchoring: boolean;
isVerifDeleteModalVisible: boolean;
isAnchored: boolean | null;
};
class FolderInformationClass extends BasePage<IPropsClass, IState> {
public constructor(props: IPropsClass) {
@ -44,6 +47,8 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
inputArchivedDescripton: "",
isValidateModalVisible: false,
hasValidateAnchoring: false,
isVerifDeleteModalVisible: false,
isAnchored: null,
};
this.onSelectedFolder = this.onSelectedFolder.bind(this);
this.openArchivedModal = this.openArchivedModal.bind(this);
@ -55,6 +60,9 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
this.closeModal = this.closeModal.bind(this);
this.validateAnchoring = this.validateAnchoring.bind(this);
this.openValidateModal = this.openValidateModal.bind(this);
this.openVerifDeleteFolder = this.openVerifDeleteFolder.bind(this);
this.closeVerifDeleteFolder = this.closeVerifDeleteFolder.bind(this);
this.verifyAnchorStatus = this.verifyAnchorStatus.bind(this);
}
// TODO: Message if the user has not created any folder yet
@ -104,12 +112,23 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
Archiver le dossier
</Button>
{this.everyDocumentValidated() && (
<>
{this.state.isAnchored === null && (
<Button variant={EButtonVariant.PRIMARY} onClick={this.openValidateModal}>
Ancrer le dossier
</Button>
)}
{this.state.isAnchored === true && (
<Button
variant={EButtonVariant.PRIMARY}
onClick={() => this.downloadAnchoringProof(this.state.selectedFolder?.uid)}>
Télécharger la preuve d'ancrage
</Button>
)}
</>
)}
{!this.doesFolderHaveCustomer() && (
<span className={classes["delete-folder"]} onClick={this.deleteFolder}>
<span className={classes["delete-folder"]} onClick={this.openVerifDeleteFolder}>
<Button variant={EButtonVariant.SECONDARY}>Supprimer le dossier</Button>
</span>
)}
@ -131,6 +150,18 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
onChange={this.onArchivedDescriptionInputChange}
/>
</Confirm>
<Confirm
isOpen={this.state.isVerifDeleteModalVisible}
onAccept={this.deleteFolder}
onClose={this.closeVerifDeleteFolder}
closeBtn
header={"Êtes-vous sûr de vouloir supprimer ce dossier ?"}
cancelText={"Annuler"}
confirmText={"Confirmer"}>
<div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>Cette action sera irréversible.</Typography>
</div>
</Confirm>
</div>
) : (
<div className={classes["no-folder-selected"]}>
@ -152,7 +183,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
header={
this.state.hasValidateAnchoring
? "Dossier en cours de certification"
: "Êtes-vous sûr de vouloir ancrer et certifier ?"
: "Êtes-vous sûr de vouloir ancrer et certifier ce dossier ?"
}
cancelText={"Annuler"}
confirmText={"Confirmer"}
@ -160,8 +191,9 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
<div className={classes["validate-document-container"]}>
{!this.state.hasValidateAnchoring && (
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}>
Afin de certifier les documents associés au dossier, cliquez sur Ancrez et certifier et les documents
seront certifiés sur la blockchain.
Les documents du dossier seront certifiés sur la blockchain. Pensez à bien télécharger l'ensemble des
documents du dossier ainsi que le fichier de preuve d'ancrage pour les mettre dans la GED de votre logiciel
de rédaction d'actes.
</Typography>
)}
{this.state.hasValidateAnchoring && (
@ -182,8 +214,39 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
);
}
public override async componentDidMount() {
const selectedFolder = await this.getFolder();
this.setState(
{
selectedFolder,
},
() => {
this.verifyAnchorStatus();
},
);
}
private async verifyAnchorStatus() {
if (!this.state.selectedFolder || !this.state.selectedFolder.uid) return;
try {
const anchorStatus = await OfficeFolderAnchors.getInstance().get(this.state.selectedFolder.uid!);
this.setState({
selectedFolder: (await this.getFolder()) as OfficeFolder,
isAnchored: anchorStatus.status === "VERIFIED_ON_CHAIN",
});
} catch (e) {
this.setState({
isAnchored: null,
});
}
}
public openVerifDeleteFolder() {
this.setState({
isVerifDeleteModalVisible: true,
});
}
public closeVerifDeleteFolder() {
this.setState({
isVerifDeleteModalVisible: false,
});
}
@ -206,6 +269,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
try {
const timeoutDelay = 9800;
await this.anchorFolder();
setTimeout(() => {
this.setState({
isValidateModalVisible: false,
@ -217,6 +281,34 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
hasValidateAnchoring: false,
});
}, timeoutDelay + 1000);
} catch (e) {
this.setState({
isValidateModalVisible: false,
hasValidateAnchoring: false,
});
console.error(e);
}
}
private async anchorFolder() {
if (!this.state.selectedFolder?.uid) return;
return await OfficeFolderAnchors.getInstance().post(this.state.selectedFolder.uid);
}
private async downloadAnchoringProof(uid?: string) {
if (!uid) return;
try {
const file: Blob = await OfficeFolderAnchors.getInstance().download(uid);
const url = window.URL.createObjectURL(file);
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
// the filename you want
a.download = `anchoring_proof_${this.state.selectedFolder?.folder_number}_${this.state.selectedFolder?.name}.pdf`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
} catch (e) {
console.error(e);
}
@ -241,7 +333,10 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
if (!documents) return 0;
const totalDocuments = documents.length;
const refusedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.REFUSED).length ?? 0;
const askedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.ASKED).length ?? 0;
const askedDocuments =
documents.filter(
(document) => document.document_status === EDocumentStatus.ASKED || document.document_status === EDocumentStatus.DEPOSITED,
).length ?? 0;
const depositedDocuments = totalDocuments - askedDocuments - refusedDocuments;
const percentage = (depositedDocuments / totalDocuments) * 100;
return isNaN(percentage) ? 0 : percentage;
@ -303,6 +398,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
},
},
},
folder_anchor: true,
},
};

View File

@ -13,6 +13,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import DateField from "@Front/Components/DesignSystem/Form/DateField";
type IProps = {};
@ -43,6 +44,8 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
value: this.state.selectedFolder?.deed?.deed_type?.uid,
} as IOption;
const openingDate = new Date(this.state.selectedFolder?.created_at ?? "");
if (!this.state.selectedFolder?.created_at) return <></>;
const defaultValue = openingDate.toISOString().split("T")[0];
return (
<DefaultNotaryDashboard title={"Ajouter client(s)"} onSelectedFolder={this.onSelectedFolder}>
<div className={classes["root"]}>
@ -60,7 +63,7 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
defaultValue={this.state.selectedFolder?.folder_number}
/>
<Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled />
<TextField placeholder="Ouverture du dossier" defaultValue={openingDate.toLocaleDateString("fr-FR")} disabled />
<DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled />
</div>
<div className={classes["button-container"]}>

View File

@ -15,8 +15,7 @@ import React from "react";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import OcrResult from "./OcrResult";
import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files";
import Files from "@Front/Api/LeCoffreApi/Notary/Files/Files";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {};
@ -34,6 +33,7 @@ type IState = {
selectedFile: File | null;
validatedPercentage: number;
document: Document | null;
fileBlob: Blob | null;
};
class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
@ -48,6 +48,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
selectedFile: null,
validatedPercentage: this.getRandomPercentageForOcr(),
document: null,
fileBlob: null,
};
this.closeModals = this.closeModals.bind(this);
@ -86,7 +87,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
)}
<div className={classes["file-container"]}>
<FilePreview
href={Files.getInstance().getUploadLink(this.state.selectedFile?.uid as string)}
href={this.state.fileBlob ? URL.createObjectURL(this.state.fileBlob) : ""}
fileName={this.state.selectedFile.file_name}
key={this.state.selectedFile.uid}
/>
@ -101,11 +102,11 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
)}
</div>
<div className={classes["footer"]}>
{this.state.document?.document_type?.name === "Document d'identité" && (
{/* {this.state.document?.document_type?.name === "Document d'identité" && (
<div className={classes["ocr-container"]}>
<OcrResult percentage={this.state.validatedPercentage} />
</div>
)}
)} */}
<div className={classes["buttons-container"]}>
{this.state.document?.document_status === EDocumentStatus.DEPOSITED && (
@ -117,7 +118,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
<Button disabled>Télécharger</Button>
</>
)}
{this.state.document?.document_status === "VALIDATED" && (
{this.state.document?.document_status === "VALIDATED" && this.state.fileBlob && (
<Button onClick={this.downloadFile}>Télécharger</Button>
)}
</div>
@ -175,38 +176,43 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
document_type: true,
folder: true,
});
this.setState({
this.setState(
{
document,
selectedFileIndex: 0,
selectedFile: document.files![0]!,
});
},
() => {
this.getFilePreview();
},
);
} catch (e) {
console.error(e);
}
}
private async getFilePreview(): Promise<void> {
try {
const fileBlob: Blob = await Files.getInstance().download(this.state.selectedFile?.uid as string);
this.setState({
fileBlob,
});
} catch (e) {
console.log(e);
}
}
private downloadFile() {
const fileName = this.state.selectedFile?.file_path?.split("/").pop();
fetch(Files.getInstance().getUploadLink(this.state.selectedFile?.uid as string))
.then((resp) => resp.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob);
if (!this.state.fileBlob) return;
const url = window.URL.createObjectURL(this.state.fileBlob);
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
// the filename you want
a.download = fileName as string;
a.download = this.state.selectedFile?.file_name as string;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch((e) => {
const a = document.createElement("a");
a.href = this.state.selectedFile?.file_path as string;
a.target = "_blank";
a.click();
console.error(e);
});
}
private getRandomPercentageForOcr() {
@ -228,20 +234,32 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
private goToPrevious() {
const index = this.state.selectedFileIndex - 1;
if (this.hasPrevious()) {
this.setState({
this.setState(
{
selectedFile: this.state.document!.files![index]!,
selectedFileIndex: index,
});
fileBlob: null,
},
() => {
this.getFilePreview();
},
);
}
}
private goToNext() {
if (this.hasNext()) {
const index = this.state.selectedFileIndex + 1;
this.setState({
this.setState(
{
selectedFile: this.state.document!.files![index]!,
selectedFileIndex: index,
});
fileBlob: null,
},
() => {
this.getFilePreview();
},
);
}
}

View File

@ -49,6 +49,7 @@ export default class ClientSection extends React.Component<IProps, IState> {
key={customer.uid}
isOpened={this.state.openedCustomer === customer.uid}
onChange={this.changeUserFolder}
isArchived
/>
);
});

View File

@ -3,9 +3,6 @@ import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import Module from "@Front/Config/Module";
import JwtService from "@Front/Services/JwtService/JwtService";
import UserStore from "@Front/Stores/UserStore";
import Image from "next/image";
import { useRouter } from "next/router";
import { useCallback } from "react";
@ -13,6 +10,7 @@ import { useCallback } from "react";
import classes from "./classes.module.scss";
import LandingImage from "./landing-connect.jpeg";
import { FrontendVariables } from "@Front/Config/VariablesFront";
import Link from "next/link";
export default function Login() {
const router = useRouter();
@ -26,20 +24,6 @@ export default function Login() {
);
}, [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 (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}>
@ -48,15 +32,14 @@ export default function Login() {
<div className={classes["title"]}>Connexion espace professionnel</div>
</Typography>
<Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier avec ID.not !
</Button>
<Button onClick={redirectCustomerOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier en tant que customer
S'identifier avec ID.not
</Button>
<Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography>
<Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
</DefaultDoubleSidePage>
);

View File

@ -11,6 +11,7 @@ import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Loader from "@Front/Components/DesignSystem/Loader";
import UserStore from "@Front/Stores/UserStore";
import Link from "next/link";
export default function LoginCallBack() {
const router = useRouter();
@ -45,7 +46,9 @@ export default function LoginCallBack() {
<Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography>
<Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
</DefaultDoubleSidePage>
);

View File

@ -0,0 +1,29 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100%;
max-width: 530px;
margin: auto;
.title {
margin: 32px 0;
text-align: center;
@media (max-width: $screen-s) {
font-family: 48px;
}
}
.loader {
width: 32px;
height: 32px;
}
.forget-password {
margin-top: 32px;
margin-bottom: 8px;
}
}

View File

@ -0,0 +1,58 @@
import LandingImage from "../Login/landing-connect.jpeg";
import Image from "next/image";
import CoffreIcon from "@Assets/Icons/coffre.svg";
import { useRouter } from "next/router";
import React, { useEffect } from "react";
import classes from "./classes.module.scss";
//import Module from "@Front/Config/Module";
//import Auth from "@Front/Api/Auth/IdNot";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Loader from "@Front/Components/DesignSystem/Loader";
// import { FrontendVariables } from "@Front/Config/VariablesFront";
// import CustomerStore from "@Front/Stores/CustomerStore";
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers";
import CustomerStore from "@Front/Stores/CustomerStore";
import Module from "@Front/Config/Module";
import Link from "next/link";
export default function LoginCallBack() {
const router = useRouter();
useEffect(() => {
const getReport = async () => {
const tokenid360 = router.query["token"];
if (!tokenid360) return;
// const variables = FrontendVariables.getInstance();
// console.log(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}/`)
// const reportRes = await fetch(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}`, { method: "GET"});
// const report = await reportRes.json() as id360ProcessResponse;
const token = await Customers.getInstance().loginCallback(tokenid360);
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
};
getReport();
}),
[router];
return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}>
<Image alt="coffre" src={CoffreIcon} />
<Typography typo={ITypo.H1}>
<div className={classes["title"]}>Connexion espace client</div>
</Typography>
<div className={classes["loader"]}>
<Loader />
</div>
<Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography>
<Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
</DefaultDoubleSidePage>
);
}

View File

@ -1,35 +1,29 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100%;
max-width: 530px;
margin: auto;
max-width: 80%;
.text {
.title {
margin: 32px 0;
text-align: center;
@media (max-width: $screen-s) {
font-family: 48px;
}
}
.france-connect-button {
.forget-password {
margin-top: 32px;
margin-bottom: 8px;
}
.logo {
cursor: pointer;
}
.what-is-france-connect {
font-family: "Inter", sans-serif;
margin-top: 12px;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 24px;
border-bottom: 0.88px solid black;
width: fit-content;
display: flex;
align-items: center;
cursor: pointer;
div {
margin-right: 8px;
}
}
}

View File

@ -1,50 +1,46 @@
import CoffreIcon from "@Assets/Icons/coffre.svg";
import franceConnectLogo from "./france-connect.svg";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BasePage from "../Base";
import classes from "./classes.module.scss";
import LandingImage from "../Login/landing-connect.jpeg";
import Image from "next/image";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import FranceConnectIcon from "./france-connect.svg";
import ExportIcon from "@Assets/Icons/export.svg";
import { FrontendVariables } from "@Front/Config/VariablesFront";
import cryptoRandomString from "crypto-random-string";
import Image from "next/image";
import { useRouter } from "next/router";
import { useCallback } from "react";
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers";
import classes from "./classes.module.scss";
import LandingImage from "./landing-connect.jpeg";
import Link from "next/link";
export default function Login() {
const router = useRouter();
const redirectCustomerOnConnection = useCallback(() => {
async function getCustomer() {
try {
const loginRes = await Customers.getInstance().login();
router.push(loginRes.enrollment.franceConnectUrl);
} catch (e) {
console.error(e);
}
}
getCustomer();
}, [router]);
export default class LoginCustomer extends BasePage {
public override render(): JSX.Element {
return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}>
<Image alt="coffre" src={CoffreIcon} />
<Typography typo={ITypo.H1}>
<div className={classes["title"]}>Identifiez-vous</div>
<div className={classes["title"]}>Connexion espace client</div>
</Typography>
<Typography typo={ITypo.P_16}>
<div className={classes["text"]}>
Pour accéder à votre espace de dépôt des documents, veuillez vous identifier avec FranceConnect.
</div>
<Image alt="france-connect" src={franceConnectLogo} onClick={redirectCustomerOnConnection} className={classes["logo"]} />
<Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography>
<Image
alt="france connect"
src={FranceConnectIcon}
onClick={this.redirectUserOnConnection}
className={classes["france-connect-button"]}></Image>
<div className={classes["what-is-france-connect"]}>
<div>Quest-ce que FranceConnect ?</div>
<Image alt="export" src={ExportIcon}></Image>
</div>
<Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
</DefaultDoubleSidePage>
);
}
private redirectUserOnConnection() {
const variables = FrontendVariables.getInstance();
// const baseFronturl =
// variables.BACK_API_PROTOCOL + variables.FRONT_APP_HOST + (variables.FRONT_APP_PORT ? ":" + variables.FRONT_APP_PORT : "");
const authorizeEndPoint = variables.FC_AUTHORIZE_ENDPOINT;
const clientId = variables.FC_CLIENT_ID;
const url = `${authorizeEndPoint}?client_id=${clientId}&redirect_uri=http://localhost:8080/login-callback&scope=openid&response_type=code&state=${cryptoRandomString(
{ length: 64 },
)}&nonce=${cryptoRandomString({ length: 64 })}&acr_values=eidas1`;
window.location.assign(url);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -90,7 +90,7 @@ export default class MyAccount extends Base<IProps, IState> {
/>
<TextField
name="cp_address"
placeholder="Adresse CP"
placeholder="Adresse"
defaultValue={this.state.user?.office_membership?.address?.address}
disabled
canCopy
@ -116,10 +116,9 @@ export default class MyAccount extends Base<IProps, IState> {
}
public override async componentDidMount() {
const jwtUncoded = JwtService.getInstance().decodeCustomerJwt();
console.log(jwtUncoded);
if (!jwtUncoded) return;
const user = await Users.getInstance().getByUid(jwtUncoded.userId, {
const jwtDecoded = JwtService.getInstance().decodeJwt();
if (!jwtDecoded) return;
const user = await Users.getInstance().getByUid(jwtDecoded.userId, {
q: {
office_membership: {
include: {

View File

@ -5,18 +5,18 @@
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
@media (max-width: $screen-s) {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
width: 150px;
.round {
width: 16px;
height: 16px;

View File

@ -3,7 +3,7 @@ import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Ty
import DefaultOfficeDashboard from "@Front/Components/LayoutTemplates/DefaultOfficeDashboard";
import User, { Office } from "le-coffre-resources/dist/SuperAdmin";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
@ -13,6 +13,8 @@ export default function OfficeInformations(props: IProps) {
let { officeUid } = router.query;
const [officeSelected, setOfficeSelected] = useState<Office | null>(null);
const [adminUsers, setAdminUsers] = useState<User[]>([]);
const [collaboratorUsers, setCollaboratorUsers] = useState<User[]>([]);
useEffect(() => {
async function getOffice() {
@ -31,19 +33,49 @@ export default function OfficeInformations(props: IProps) {
});
if (!office) return;
setOfficeSelected(office);
const adminUsers = office.users?.filter((user) => {
if (user.office_role && user.office_role.name === "admin") {
return true;
}
if (!user.office_role && user.role?.name === "admin") {
return true;
}
if (!user.office_role && user.role?.name === "super-admin") {
return true;
}
return false;
});
const collaboratorUsers = office.users?.filter((user) => {
if (user.office_role && user.office_role.name === "admin") {
return false;
}
if (!user.office_role && user.role?.name === "admin") {
return false;
}
if (!user.office_role && user.role?.name === "super-admin") {
return false;
}
return true;
});
setAdminUsers(adminUsers!);
setCollaboratorUsers(collaboratorUsers!);
}
getOffice();
}, [officeUid]);
const renderUser = (user: User) => (
const renderUser = useCallback(
(user: User) => (
<>
<div key={user.uid} className={classes["user-line-desktop"]}>
<div key={user.uid + "-" + officeUid} className={classes["user-line-desktop"]}>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : "Utilisateur restreint"}</Typography>
</div>
<div key={user.uid} className={classes["user-line-mobile"]}>
<div className={classes["line"]}>
@ -78,7 +110,10 @@ export default function OfficeInformations(props: IProps) {
</div>
</div>
</>
),
[officeUid],
);
return (
<DefaultOfficeDashboard mobileBackText={"Liste des utilisateurs"}>
<div className={classes["root"]}>
@ -134,20 +169,7 @@ export default function OfficeInformations(props: IProps) {
Rôle
</Typography>
</div>
{officeSelected?.users
?.filter((user) => {
if (user.office_role && user.office_role.name === "admin") {
return true;
}
if (!user.office_role && user.role?.name === "admin") {
return true;
}
if (!user.office_role && user.role?.name === "super-admin") {
return true;
}
return false;
})
.map((user) => {
{adminUsers.map((user) => {
return renderUser(user);
})}
</div>
@ -174,20 +196,7 @@ export default function OfficeInformations(props: IProps) {
Rôle
</Typography>
</div>
{officeSelected?.users
?.filter((user) => {
if (user.office_role && user.office_role.name === "admin") {
return false;
}
if (!user.office_role && user.role?.name === "admin") {
return false;
}
if (!user.office_role && user.role?.name === "super-admin") {
return false;
}
return true;
})
.map((user) => {
{collaboratorUsers.map((user) => {
return renderUser(user);
})}
</div>

View File

@ -19,7 +19,7 @@ export default class PageNotFound extends BasePage {
La page que vous recherchez semble introuvable.
</Typography>
<Link href={Module.getInstance().get().modules.pages.Home.props.path}>
<Button>Retour à laccueil</Button>
<Button>Retour à l'accueil</Button>
</Link>
</div>
</div>

View File

@ -12,6 +12,8 @@ import { useCallback, useState } from "react";
import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
type IProps = {};
export default function RolesCreate(props: IProps) {
@ -61,6 +63,14 @@ export default function RolesCreate(props: IProps) {
}, [hasChanged, redirect]);
return (
<Rules
mode={RulesMode.NECESSARY}
rules={[
{
action: AppRuleActions.create,
name: AppRuleNames.officeRoles,
},
]}>
<DefaultRolesDashboard mobileBackText={"Liste des rôles"} hasBackArrow title="Créer un rôle">
<div className={classes["root"]}>
<div className={classes["header"]}>
@ -91,5 +101,6 @@ export default function RolesCreate(props: IProps) {
</Confirm>
</div>
</DefaultRolesDashboard>
</Rules>
);
}

View File

@ -45,7 +45,11 @@ export default function RolesInformations(props: IProps) {
},
});
const rules = await Rules.getInstance().get({});
const rules = await Rules.getInstance().get({
where: {
namespace: "notary",
},
});
if (!role) return;
setRoleSelected(role);
if (!role.rules) return;
@ -124,7 +128,6 @@ export default function RolesInformations(props: IProps) {
label: "Tout sélectionner",
value: "all",
}}
toolTip="Tout sélectionner"
onChange={handleSelectAllChange}
checked={selectAll}
/>

View File

@ -25,7 +25,7 @@ export default function SelectFolder() {
where: {
customers: {
some: {
uid: jwt.userId || (jwt as any).customerId,
uid: jwt.customerId || (jwt as any).customerId,
},
},
},

View File

@ -1,5 +1,5 @@
import WarningIcon from "@Assets/images/warning.png";
import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
//import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
import Roles from "@Front/Api/LeCoffreApi/Admin/Roles/Roles";
import LiveVotes from "@Front/Api/LeCoffreApi/SuperAdmin/LiveVotes/LiveVotes";
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
@ -69,9 +69,11 @@ export default function UserInformations(props: IProps) {
},
});
if (!user) return;
const roles = await OfficeRoles.getInstance().get();
if (!roles) return;
setAvailableRoles(roles.map((role) => ({ value: role.uid, label: role.name })));
const roles = await Roles.getInstance().get({
where: {NOT: {OR:[{name: "super-admin"}, {name: "admin"}]}},
});
if (!roles) return
setAvailableRoles(roles.map((role) => ({ value: role.uid, label: role.label })));
setUserSelected(user);
}, [userUid]);
@ -81,7 +83,11 @@ export default function UserInformations(props: IProps) {
useEffect(() => {
if (!userSelected) return;
setCurrentAppointment(userSelected?.appointment?.find((appointment) => appointment.status === EAppointmentStatus.OPEN && appointment.votes?.length != 0) ?? null);
setCurrentAppointment(
userSelected?.appointment?.find(
(appointment) => appointment.status === EAppointmentStatus.OPEN && appointment.votes?.length != 0,
) ?? null,
);
}, [userSelected]);
/** Functions for the admin modal */
@ -271,7 +277,7 @@ export default function UserInformations(props: IProps) {
onChange={handleRoleChange}
selectedOption={{
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.label!,
label: userSelected?.office_role ? userSelected?.office_role?.name : "Utilisateur restreint",
}}
/>
</div>
@ -281,8 +287,16 @@ export default function UserInformations(props: IProps) {
<Typography typo={ITypo.P_SB_18}>Attribuer un titre</Typography>
</div>
<div className={classes["second-line"]}>
{!isSuperAdminChecked && (
<Switch label="Admin de son office" checked={isAdminChecked} onChange={handleAdminChanged} />
<Switch label="Super-admin LeCoffre.io" checked={isSuperAdminChecked} disabled={userHasVoted()} onChange={handleSuperAdminChanged} />
)}
<Switch
label="Super-admin LeCoffre.io"
checked={isSuperAdminChecked}
disabled={userHasVoted()}
onChange={handleSuperAdminChanged}
/>
{currentAppointment && (
<div className={classes["votes-block"]}>
<div className={classes["left"]}>

Some files were not shown because too many files have changed in this diff Show More