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_AUTHORIZE_ENDPOINT: process.env.NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT,
NEXT_PUBLIC_IDNOT_CLIENT_ID: process.env.NEXT_PUBLIC_IDNOT_CLIENT_ID, 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_IDNOT_BASE_URL: process.env.NEXT_PUBLIC_IDNOT_BASE_URL,
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
}, },
// webpack: config => { // webpack: config => {
// config.node = { // 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", "eslint-config-next": "13.2.4",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.73", "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.85",
"next": "13.2.4", "next": "13.2.4",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"react": "18.2.0", "react": "18.2.0",
@ -32,9 +32,7 @@
"react-select": "^5.7.2", "react-select": "^5.7.2",
"sass": "^1.59.2", "sass": "^1.59.2",
"sharp": "^0.32.1", "sharp": "^0.32.1",
"typescript": "4.9.5" "typescript": "4.9.5",
}, "uuidv4": "^6.2.13"
"devDependencies": {} }
} }

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 { export enum ContentType {
JSON = "application/json", JSON = "application/json",
PDF = "application/pdf",
FORM_DATA = "multipart/form-data;", FORM_DATA = "multipart/form-data;",
PNG = "image/png",
} }
export default abstract class BaseApiService { export default abstract class BaseApiService {
private static baseUrl: string; private static baseUrl: string;
@ -26,7 +28,7 @@ export default abstract class BaseApiService {
const headers = new Headers(); const headers = new Headers();
if (contentType === ContentType.JSON) { if (contentType === ContentType.JSON || contentType === ContentType.PDF) {
headers.set("Content-Type", contentType); headers.set("Content-Type", contentType);
} }
headers.set("Authorization", `Bearer ${token}`); headers.set("Authorization", `Bearer ${token}`);
@ -37,13 +39,13 @@ export default abstract class BaseApiService {
return JSON.stringify(body); 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 () => const request = async () =>
await fetch(url, { await fetch(url, {
method: "GET", 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) { 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); 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(); 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> { protected async processResponse<T>(response: Response, request: () => Promise<Response>, filename?: string): Promise<T> {
let responseJson: any | null; let responseContent: T;
try {
responseJson = await response.json(); if (response.ok) {
} catch (err: unknown) { // Check the Content-Type header to determine the response type
responseJson = null; const contentType = response.headers.get("Content-Type");
return Promise.reject(err);
if (contentType && !contentType.includes("application/json")) {
responseContent = (await response.blob()) as T;
} else {
// Handle JSON response
try {
responseContent = await response.json();
} catch (err) {
return Promise.reject(err);
}
}
} else {
// Handle error response
try {
const responseJson = await response.json();
return Promise.reject(responseJson);
} catch (err) {
return Promise.reject(err);
}
} }
if (!response.ok) { return responseContent;
return Promise.reject(responseJson);
}
return responseJson as T;
} }
protected onError(error: unknown) { 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"> <svg width="22" height="22" viewBox="0 0 22 22" 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"/> <path d="M18 6L8.375 16L4 11.4545" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </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; 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 Typography, { ITypo } from "../Typography";
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Image from "next/image"; import Image from "next/image";
import WarningBadge from "../WarningBadge";
export type IBlock = { export type IBlock = {
name: string; name: string;
id: string; id: string;
selected: boolean; selected: boolean;
hasFlag?: boolean;
}; };
type IProps = { type IProps = {
@ -30,7 +32,10 @@ export default function BlockList({ blocks, onSelectedBlock }: IProps) {
<div className={classes["left-side"]}> <div className={classes["left-side"]}>
<Typography typo={ITypo.P_16}>{folder.name}</Typography> <Typography typo={ITypo.P_16}>{folder.name}</Typography>
</div> </div>
<Image alt="chevron" src={ChevronIcon} /> <div className={classes["right-side"]}>
{folder.hasFlag && <WarningBadge />}
<Image alt="chevron" src={ChevronIcon} />
</div>
</div> </div>
</div> </div>
); );

View File

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

View File

@ -207,7 +207,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
</div> </div>
{this.props.document.document_status === EDocumentStatus.REFUSED && ( {this.props.document.document_status === EDocumentStatus.REFUSED && (
<Typography typo={ITypo.CAPTION_14} className={classes["error-message"]}> <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> </Typography>
)} )}
</div> </div>

View File

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

View File

@ -39,6 +39,7 @@ type IState = {
refusedReason?: string; refusedReason?: string;
isShowRefusedReasonModalVisible: boolean; isShowRefusedReasonModalVisible: boolean;
isAddDocumentModalVisible: boolean; isAddDocumentModalVisible: boolean;
isLoading: boolean;
}; };
export default class DepositOtherDocument extends React.Component<IProps, IState> { export default class DepositOtherDocument extends React.Component<IProps, IState> {
@ -54,6 +55,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
isDragOver: false, isDragOver: false,
refusedReason: "", refusedReason: "",
isShowRefusedReasonModalVisible: false, isShowRefusedReasonModalVisible: false,
isLoading: false,
}; };
this.addDocument = this.addDocument.bind(this); 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); 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 { public override render(): JSX.Element {
return ( return (
<Confirm <Confirm
isOpen={this.state.isAddDocumentModalVisible} isOpen={this.state.isAddDocumentModalVisible}
onClose={this.props.onClose!} onClose={() => {}}
onAccept={this.onAccept} onAccept={this.onAccept}
closeBtn closeBtn
header={"Ajouter un document"} header={"Ajouter un document"}
cancelText={"Annuler"} 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["modal-content"]}>
<div className={classes["container"]}> <div className={classes["container"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}> <Typography typo={ITypo.P_16} className={classes["text"]}>
@ -140,7 +114,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
</div> </div>
</Typography> </Typography>
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}> <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> </Typography>
</div> </div>
</div> </div>
@ -187,6 +161,38 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
public override componentDidMount(): void {} 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 { private shortName(name: string): string {
const maxLength = 20; const maxLength = 20;
if (name.length > maxLength) { if (name.length > maxLength) {

View File

@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import React from "react"; import React from "react";
import Typography, { ITypo, ITypoColor } from "../Typography"; import Typography, { ITypo, ITypoColor } from "../Typography";
@ -20,17 +21,21 @@ export default class FilePreview extends React.Component<IProps, IState> {
<Loader /> <Loader />
</div> </div>
{!type && ( {this.props.href && (
<Typography typo={ITypo.H1} color={ITypoColor.BLACK}> <>
Erreur lors du chargement du fichier {!type && (
</Typography> <Typography typo={ITypo.H1} color={ITypoColor.BLACK}>
Erreur lors du chargement du fichier
</Typography>
)}
<div className={classes["file-container"]}>
{type?.toLowerCase() === "pdf" && (
<embed src={this.props.href} width="100%" height="100%" type="application/pdf" className={classes["pdf"]} />
)}
{type?.toLowerCase() !== "pdf" && <img src={this.props.href} alt="File preview" className={classes["image"]} />}
</div>
</>
)} )}
<div className={classes["file-container"]}>
{type?.toLowerCase() === "pdf" && (
<embed src={this.props.href} width="100%" height="100%" type="application/pdf" className={classes["pdf"]} />
)}
{type?.toLowerCase() !== "pdf" && <img src={this.props.href} alt="File preview" className={classes["image"]} />}
</div>
</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[] { private getBlocks(folders: OfficeFolder[]): IBlock[] {
const pendingFolders = folders const pendingFolders = folders
.filter((folder) => { .filter((folder) => {
@ -103,6 +108,7 @@ class FolderListContainerClass extends React.Component<IPropsClass, IState> {
id: folder.uid!, id: folder.uid!,
name: folder.folder_number! + " - " + folder.name!, name: folder.folder_number! + " - " + folder.name!,
selected: this.props.selectedFolder === folder.uid, 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 Typography, { ITypo, ITypoColor } from "../../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { NextRouter, useRouter } from "next/router";
type IProps = { type IProps = {
selectedOption?: IOption; selectedOption?: IOption;
@ -16,7 +17,7 @@ type IProps = {
placeholder?: string; placeholder?: string;
className?: string; className?: string;
name: string; name: string;
disabled: boolean; disabled?: boolean;
errors?: ValidationError; errors?: ValidationError;
}; };
@ -35,7 +36,11 @@ type IState = {
errors: ValidationError | null; 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 contentRef = React.createRef<HTMLUListElement>();
private rootRef = React.createRef<HTMLDivElement>(); private rootRef = React.createRef<HTMLDivElement>();
private removeOnresize = () => {}; private removeOnresize = () => {};
@ -44,7 +49,7 @@ export default class SelectField extends React.Component<IProps, IState> {
disabled: false, disabled: false,
}; };
constructor(props: IProps) { constructor(props: IPropsClass) {
super(props); super(props);
this.state = { this.state = {
isOpen: false, isOpen: false,
@ -64,7 +69,7 @@ export default class SelectField extends React.Component<IProps, IState> {
<div <div
className={classNames(classes["root"], this.props.className)} className={classNames(classes["root"], this.props.className)}
ref={this.rootRef} ref={this.rootRef}
data-disabled={this.props.disabled.toString()} data-disabled={this.props.disabled?.toString()}
data-errored={(this.state.errors !== null).toString()}> data-errored={(this.state.errors !== null).toString()}>
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />} {selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
<label <label
@ -116,6 +121,23 @@ export default class SelectField extends React.Component<IProps, IState> {
</div> </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) { public override componentDidUpdate(prevProps: IProps) {
if (this.props.errors !== prevProps.errors) { if (this.props.errors !== prevProps.errors) {
@ -140,15 +162,6 @@ export default class SelectField extends React.Component<IProps, IState> {
return null; return null;
} }
public override componentDidMount(): void {
this.onResize();
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
}
public override componentWillUnmount() {
this.removeOnresize();
}
private onResize() { private onResize() {
let listHeight = 0; let listHeight = 0;
let listWidth = 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} mode={RulesMode.NECESSARY}
rules={[ rules={[
{ {
action: AppRuleActions.create, action: AppRuleActions.update,
name: AppRuleNames.officeRoles, name: AppRuleNames.offices,
}, },
]}> ]}>
<NavigationLink <NavigationLink
@ -123,7 +123,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
/> />
</Rules> </Rules>
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" /> <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"]} /> <div className={classes["separator"]} />
<LogOutButton /> <LogOutButton />
</div> </div>

View File

@ -1,46 +1,64 @@
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import React from "react"; import React, { useCallback, useEffect } from "react";
import HeaderLink from "../HeaderLink"; import HeaderLink from "../HeaderLink";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules"; import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule"; 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 = {}; const getNotifications = useCallback(async () => {
type IState = {}; 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,
});
});
}, []);
export default class Navigation extends React.Component<IProps, IState> { useEffect(() => {
public override render(): JSX.Element { getNotifications();
return ( }, [pathname, getNotifications]);
<div className={classes["root"]}>
return (
<div className={classes["root"]}>
<HeaderLink
text={"Dossiers en cours"}
path={Module.getInstance().get().modules.pages.Folder.props.path}
routesActive={[
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path,
Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path,
]}
/>
<HeaderLink
text={"Dossiers archivés"}
path={Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path}
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
/>
<Rules
mode={RulesMode.NECESSARY}
rules={[
{
action: AppRuleActions.update,
name: AppRuleNames.officeRoles,
},
]}>
<HeaderLink <HeaderLink
text={"Dossiers en cours"} text={"Collaborateurs"}
path={Module.getInstance().get().modules.pages.Folder.props.path} path={Module.getInstance().get().modules.pages.Collaborators.props.path}
routesActive={[ routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path,
Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path,
]}
/> />
<HeaderLink </Rules>
text={"Dossiers archivés"} </div>
path={Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path} );
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
/>
<Rules
mode={RulesMode.NECESSARY}
rules={[
{
action: AppRuleActions.update,
name: AppRuleNames.officeRoles,
},
]}>
<HeaderLink
text={"Collaborateurs"}
path={Module.getInstance().get().modules.pages.Collaborators.props.path}
routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
/>
</Rules>
</div>
);
}
} }

View File

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

View File

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

View File

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

View File

@ -61,7 +61,7 @@ class HeaderClass extends React.Component<IPropsClass, IState> {
<link rel="shortcut icon" href={"/favicon.svg"} /> <link rel="shortcut icon" href={"/favicon.svg"} />
</Head> </Head>
<div className={classes["logo-container"]}> <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"]} /> <Image src={LogoIcon} alt="logo" className={classes["logo"]} />
</Link> </Link>
</div> </div>

View File

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

View File

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

View File

@ -8,18 +8,25 @@ import React from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Toasts, { IToast } from "@Front/Stores/Toasts"; import Toasts, { IToast } from "@Front/Stores/Toasts";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; 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 = { type IProps = {
toast: IToast; toast: IToast;
}; };
type IPropsClass = IProps & {
router: NextRouter;
};
type IState = { type IState = {
willClose: boolean; willClose: boolean;
}; };
export default class ToastElement extends React.Component<IProps, IState> { class ToastElementClass extends React.Component<IPropsClass, IState> {
private closeTimeout = 0; private closeTimeout = 0;
constructor(props: IProps) { constructor(props: IPropsClass) {
super(props); super(props);
this.state = { this.state = {
@ -27,6 +34,7 @@ export default class ToastElement extends React.Component<IProps, IState> {
}; };
this.onClose = this.onClose.bind(this); this.onClose = this.onClose.bind(this);
this.handleClick = this.handleClick.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
@ -35,7 +43,11 @@ export default class ToastElement extends React.Component<IProps, IState> {
"--data-duration": `${toast.time}ms`, "--data-duration": `${toast.time}ms`,
} as React.CSSProperties; } as React.CSSProperties;
return ( 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} />} {toast.time !== 0 && <div className={classes["loadbar"]} style={style} />}
<div className={classes["header"]}> <div className={classes["header"]}>
<div className={classes["text-icon_row"]}> <div className={classes["text-icon_row"]}>
@ -45,7 +57,7 @@ export default class ToastElement extends React.Component<IProps, IState> {
{this.getToastText(toast.text)} {this.getToastText(toast.text)}
</div> </div>
</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> </div>
{toast.button} {toast.button}
</div> </div>
@ -95,4 +107,16 @@ export default class ToastElement extends React.Component<IProps, IState> {
Toasts.getInstance().close(this.props.toast); Toasts.getInstance().close(this.props.toast);
}, 200); }, 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 ChevronIcon from "@Assets/Icons/chevron.svg";
import PlusIcon from "@Assets/Icons/plus.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 Module from "@Front/Config/Module";
import classNames from "classnames"; import classNames from "classnames";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; 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 documents = this.props.customer.documents.filter((document) => document.folder?.uid === this.props.folder.uid);
const totalDocuments: number = documents.length; const totalDocuments: number = documents.length;
const numberDocumentsRefused: number = this.getDocumentsByStatus(EDocumentStatus.REFUSED)?.length || 0; 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 depositedDocuments: number = totalDocuments - numberDocumentsAsked - numberDocumentsRefused;
const percentage = (depositedDocuments / totalDocuments) * 100; const percentage = (depositedDocuments / totalDocuments) * 100;

View File

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

View File

@ -1,5 +1,4 @@
"use client"; "use client";
import Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents"; import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument"; import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
@ -10,7 +9,7 @@ import React, { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { useRouter } from "next/router"; import { 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 DepositOtherDocument from "@Front/Components/DesignSystem/DepositOtherDocument";
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
@ -24,51 +23,50 @@ export default function ClientDashboard(props: IProps) {
const [folder, setFolder] = useState<OfficeFolder | null>(null); const [folder, setFolder] = useState<OfficeFolder | null>(null);
const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false); const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false);
const onCloseModalAddDocument = useCallback(() => { const getDocuments = useCallback(async () => {
console.log("Closing"); let jwt: ICustomerJwtPayload | undefined;
if (typeof document !== "undefined") {
jwt = JwtService.getInstance().decodeCustomerJwt();
}
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 },
include: {
files: true,
document_history: true,
document_type: true,
depositor: true,
},
};
const documentList = await Documents.getInstance().get(query);
//const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } });
setFolder(folder);
setDocuments(documentList);
setCustomer(actualCustomer);
}, [folderUid]);
const onCloseModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(false); setIsAddDocumentModalVisible(false);
}, []); getDocuments();
}, [getDocuments]);
const onOpenModalAddDocument = useCallback(() => { const onOpenModalAddDocument = useCallback(() => {
setIsAddDocumentModalVisible(true); setIsAddDocumentModalVisible(true);
}, []); }, []);
useEffect(() => { useEffect(() => {
async function getDocuments() {
let jwt;
if (typeof document !== "undefined") {
jwt = JwtService.getInstance().decodeJwt();
}
if (!jwt || !jwt.email) return;
const customers = await Customers.getInstance().get({
where: { contact: { email: jwt.email }, office_folders: { some: { uid: folderUid } } },
});
const actualCustomer: Customer = customers[0]!;
const query: IGetDocumentsparams = {
where: { depositor: { uid: actualCustomer.uid }, folder_uid: folderUid as string },
include: {
files: true,
document_history: true,
document_type: true,
depositor: true,
},
};
const documentList = await Documents.getInstance().get(query);
const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true } });
setFolder(folder);
setDocuments(documentList);
setCustomer(actualCustomer);
}
getDocuments(); getDocuments();
}, [folderUid]); }, [folderUid, getDocuments]);
const renderHeader = useCallback(() => { const renderHeader = useCallback(() => {
console.log("Dossier : ", customer);
return ( return (
<div className={classes["header"]}> <div className={classes["header"]}>
<div className={classes["text-container"]}> <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 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 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 à 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> </Typography>
</div> </div>
<a href="mailto:g.texier@notaires.fr" target="_blank">
<Button className={classes["button"]}>Contacter mon notaire</Button>
</a>
</div> </div>
); );
}, [customer]); }, [customer, folder?.folder_number, folder?.name, folder?.office?.name]);
const renderBox = useCallback(() => { const renderBox = useCallback(() => {
console.log(isAddDocumentModalVisible);
return ( return (
<DepositOtherDocument <DepositOtherDocument
folder_uid={folderUid!} folder_uid={folderUid!}
@ -114,7 +107,7 @@ export default function ClientDashboard(props: IProps) {
onClose={onCloseModalAddDocument} onClose={onCloseModalAddDocument}
document={Document.hydrate<Document>({ document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({ 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 Customers from "@Front/Api/LeCoffreApi/Customer/Customers/Customers";
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument"; import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
import TextField from "@Front/Components/DesignSystem/Form/TextField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
@ -11,7 +10,6 @@ import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Custo
import React from "react"; import React from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
type IProps = {}; type IProps = {};
type IState = { type IState = {
@ -71,7 +69,7 @@ export default class ClientDashboard extends Base<IProps, IState> {
<DepositDocument <DepositDocument
document={Document.hydrate<Document>({ document={Document.hydrate<Document>({
document_type: DocumentType.hydrate<DocumentType>({ 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 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 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 à 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> </Typography>
</div> </div>
<a href="mailto:g.texier@notaires.fr" target="_blank">
<Button className={classes["button"]}>Contacter mon notaire</Button>
</a>
</div> </div>
); );
} }
public override async componentDidMount() { // public override async componentDidMount() {
// TODO Get documents of the current customer according to userStore // // TODO Get documents of the current customer according to userStore
// REMOVE this mock // // REMOVE this mock
const jwt = JwtService.getInstance().decodeJwt(); // const jwt = JwtService.getInstance().decodeJwt();
const mockedCustomers = await Customers.getInstance().get({ // const mockedCustomers = await Customers.getInstance().get({
where: { contact: { email: jwt?.email } }, // where: { contact: { email: jwt?.email } },
}); // });
const mockedCustomer: Customer = mockedCustomers[0]!; // const mockedCustomer: Customer = mockedCustomers[0]!;
const query: IGetDocumentsparams = { // const query: IGetDocumentsparams = {
where: { depositor: { uid: mockedCustomer.uid } }, // where: { depositor: { uid: mockedCustomer.uid } },
include: { // include: {
files: true, // files: true,
document_history: true, // document_history: true,
document_type: true, // document_type: true,
}, // },
}; // };
const documents: Document[] = await Documents.getInstance().get(query); // const documents: Document[] = await Documents.getInstance().get(query);
this.setState({ documents, mockedCustomer }); // this.setState({ documents, mockedCustomer });
} // }
private onCloseModalAddDocument() { private onCloseModalAddDocument() {
this.setState({ isAddDocumentModalVisible: false }); this.setState({ isAddDocumentModalVisible: false });

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import { useCallback, useEffect, useState } from "react";
import { MultiValue } from "react-select"; import { MultiValue } from "react-select";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Tooltip from "@Front/Components/DesignSystem/ToolTip";
type IProps = {}; type IProps = {};
export default function DeedTypesInformations(props: IProps) { export default function DeedTypesInformations(props: IProps) {
@ -30,6 +31,7 @@ export default function DeedTypesInformations(props: IProps) {
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]); const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
const [isDeleteModalOpened, setIsDeleteModalOpened] = useState<boolean>(false); const [isDeleteModalOpened, setIsDeleteModalOpened] = useState<boolean>(false);
const [isSaveModalOpened, setIsSaveModalOpened] = useState<boolean>(false);
const openDeleteModal = useCallback(() => { const openDeleteModal = useCallback(() => {
setIsDeleteModalOpened(true); setIsDeleteModalOpened(true);
@ -39,6 +41,14 @@ export default function DeedTypesInformations(props: IProps) {
setIsDeleteModalOpened(false); setIsDeleteModalOpened(false);
}, []); }, []);
const openSaveModal = useCallback(() => {
setIsSaveModalOpened(true);
}, []);
const closeSaveModal = useCallback(() => {
setIsSaveModalOpened(false);
}, []);
const deleteDeedType = useCallback(async () => { const deleteDeedType = useCallback(async () => {
await DeedTypes.getInstance().put( await DeedTypes.getInstance().put(
deedTypeUid as string, deedTypeUid as string,
@ -81,14 +91,19 @@ export default function DeedTypesInformations(props: IProps) {
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
await DeedTypes.getInstance().put(deedTypeUid as string, { openSaveModal();
uid: deedTypeUid as string,
document_types: selectedDocuments.map((document) => DocumentType.hydrate<DocumentType>({ uid: document.value as string })),
});
}, },
[deedTypeUid, selectedDocuments], [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 })),
});
closeSaveModal();
}, [closeSaveModal, deedTypeUid, selectedDocuments]);
const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => { const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
setSelectedDocuments(values as IOption[]); setSelectedDocuments(values as IOption[]);
}, []); }, []);
@ -137,7 +152,8 @@ export default function DeedTypesInformations(props: IProps) {
<div className={classes["documents-container"]}> <div className={classes["documents-container"]}>
<Form onSubmit={onSubmitHandler}> <Form onSubmit={onSubmitHandler}>
<div className={classes["container-title"]}> <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>
<div className={classes["documents"]}> <div className={classes["documents"]}>
<MultiSelect <MultiSelect
@ -171,6 +187,20 @@ export default function DeedTypesInformations(props: IProps) {
</Typography> </Typography>
</div> </div>
</Confirm> </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> </div>
</DefaultDeedTypesDashboard> </DefaultDeedTypesDashboard>
); );

View File

@ -29,13 +29,15 @@ export default function DocumentTypesCreate(props: IProps) {
}); });
const documentToCreate = DocumentType.hydrate<DocumentType>({ const documentToCreate = DocumentType.hydrate<DocumentType>({
...values, ...values,
office: office office: office,
}); });
await validateOrReject(documentToCreate, { groups: ["createDocumentType"] }); await validateOrReject(documentToCreate, { groups: ["createDocumentType"] });
const documentTypeCreated = await DocumentTypes.getInstance().post(documentToCreate); const documentTypeCreated = await DocumentTypes.getInstance().post(documentToCreate);
router.push( 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) { } catch (e) {
if (e instanceof Array) { if (e instanceof Array) {

View File

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

View File

@ -1,6 +1,18 @@
@import "@Themes/constants.scss"; @import "@Themes/constants.scss";
.root { .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 { .document-infos {
display: flex; display: flex;
align-items: flex-start; 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 PenICon from "@Assets/Icons/pen.svg";
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes"; import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
@ -10,6 +11,7 @@ import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
export default function DocumentTypesInformations() { export default function DocumentTypesInformations() {
const router = useRouter(); const router = useRouter();
@ -33,8 +35,14 @@ export default function DocumentTypesInformations() {
return ( return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des collaborateurs"}> <DefaultDocumentTypesDashboard mobileBackText={"Liste des collaborateurs"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["folder-header"]}> <div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography> <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>
<div className={classes["document-infos"]}> <div className={classes["document-infos"]}>
<div className={classes["left"]}> <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 BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { TextField } from "@mui/material";
import { ECivility } from "le-coffre-resources/dist/Customer/Contact"; import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
import { Customer, OfficeFolder } from "le-coffre-resources/dist/Notary"; import { Customer, OfficeFolder } from "le-coffre-resources/dist/Notary";
import Link from "next/link"; import Link from "next/link";
@ -17,6 +16,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
enum ESelectedOption { enum ESelectedOption {
EXISTING_CUSTOMER = "existing_customer", EXISTING_CUSTOMER = "existing_customer",
@ -211,7 +211,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
contact: values, contact: values,
}); });
if (!customer.uid) return; if (!customer.uid) return;
customersToLink?.push({ customer: { uid: customer.uid } } as Partial<Customer>); customersToLink?.push({ uid: customer.uid } as Partial<Customer>);
} }
if (customersToLink) { if (customersToLink) {
@ -220,7 +220,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
return Customer.hydrate<Customer>(customer); return Customer.hydrate<Customer>(customer);
}), }),
}); });
console.log(body);
await Folders.getInstance().put(this.props.selectedFolderUid, body); await Folders.getInstance().put(this.props.selectedFolderUid, body);
this.props.router.push(`/folders/${this.props.selectedFolderUid}`); this.props.router.push(`/folders/${this.props.selectedFolderUid}`);
} }

View File

@ -157,29 +157,31 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
} }
private async getAvailableDocuments(folder: OfficeFolder): Promise<IOption[]> { private async getAvailableDocuments(folder: OfficeFolder): Promise<IOption[]> {
// Getting already asked documents UIDs in an array
const userDocumentTypesUids = folder const userDocumentTypesUids = folder
.documents!.filter((document) => document.depositor!.uid! === this.props.customerUid!) .documents!.filter((document) => document.depositor!.uid! === this.props.customerUid!)
.map((document) => { .map((document) => {
return document.document_type!.uid!; 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) => { const documentTypes = folder.deed!.document_types!.filter((documentType) => {
if (userDocumentTypesUids.includes(documentType!.uid!)) return false; if (userDocumentTypesUids.includes(documentType!.uid!)) return false;
return true; return true;
}); });
// If there is none document type available, return an empty array
if (!documentTypes) return []; if (!documentTypes) return [];
const documentTypesOptions: IOption[] = documentTypes // Else, return an array document types formatted as IOPtions
.filter((documentType) => { const documentTypesOptions: IOption[] = documentTypes.map((documentType) => {
return true; return {
}) label: documentType!.name!,
.map((documentType) => { value: documentType!.uid!,
return { description: documentType!.private_description!,
label: documentType!.name!, };
value: documentType!.uid!, });
description: documentType!.private_description!,
};
});
return documentTypesOptions; return documentTypesOptions;
} }

View File

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

View File

@ -1,5 +1,6 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders"; 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 Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation"; import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField"; import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
@ -34,6 +35,8 @@ type IState = {
inputArchivedDescripton: string; inputArchivedDescripton: string;
isValidateModalVisible: boolean; isValidateModalVisible: boolean;
hasValidateAnchoring: boolean; hasValidateAnchoring: boolean;
isVerifDeleteModalVisible: boolean;
isAnchored: boolean | null;
}; };
class FolderInformationClass extends BasePage<IPropsClass, IState> { class FolderInformationClass extends BasePage<IPropsClass, IState> {
public constructor(props: IPropsClass) { public constructor(props: IPropsClass) {
@ -44,6 +47,8 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
inputArchivedDescripton: "", inputArchivedDescripton: "",
isValidateModalVisible: false, isValidateModalVisible: false,
hasValidateAnchoring: false, hasValidateAnchoring: false,
isVerifDeleteModalVisible: false,
isAnchored: null,
}; };
this.onSelectedFolder = this.onSelectedFolder.bind(this); this.onSelectedFolder = this.onSelectedFolder.bind(this);
this.openArchivedModal = this.openArchivedModal.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.closeModal = this.closeModal.bind(this);
this.validateAnchoring = this.validateAnchoring.bind(this); this.validateAnchoring = this.validateAnchoring.bind(this);
this.openValidateModal = this.openValidateModal.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 // TODO: Message if the user has not created any folder yet
@ -104,12 +112,23 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
Archiver le dossier Archiver le dossier
</Button> </Button>
{this.everyDocumentValidated() && ( {this.everyDocumentValidated() && (
<Button variant={EButtonVariant.PRIMARY} onClick={this.openValidateModal}> <>
Ancrer le dossier {this.state.isAnchored === null && (
</Button> <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() && ( {!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> <Button variant={EButtonVariant.SECONDARY}>Supprimer le dossier</Button>
</span> </span>
)} )}
@ -131,6 +150,18 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
onChange={this.onArchivedDescriptionInputChange} onChange={this.onArchivedDescriptionInputChange}
/> />
</Confirm> </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>
) : ( ) : (
<div className={classes["no-folder-selected"]}> <div className={classes["no-folder-selected"]}>
@ -152,7 +183,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
header={ header={
this.state.hasValidateAnchoring this.state.hasValidateAnchoring
? "Dossier en cours de certification" ? "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"} cancelText={"Annuler"}
confirmText={"Confirmer"} confirmText={"Confirmer"}
@ -160,8 +191,9 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
<div className={classes["validate-document-container"]}> <div className={classes["validate-document-container"]}>
{!this.state.hasValidateAnchoring && ( {!this.state.hasValidateAnchoring && (
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}> <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 Les documents du dossier seront certifiés sur la blockchain. Pensez à bien télécharger l'ensemble des
seront certifiés sur la blockchain. 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> </Typography>
)} )}
{this.state.hasValidateAnchoring && ( {this.state.hasValidateAnchoring && (
@ -182,8 +214,39 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
); );
} }
public override async componentDidMount() { 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({
isAnchored: anchorStatus.status === "VERIFIED_ON_CHAIN",
});
} catch (e) {
this.setState({
isAnchored: null,
});
}
}
public openVerifDeleteFolder() {
this.setState({ this.setState({
selectedFolder: (await this.getFolder()) as OfficeFolder, isVerifDeleteModalVisible: true,
});
}
public closeVerifDeleteFolder() {
this.setState({
isVerifDeleteModalVisible: false,
}); });
} }
@ -206,6 +269,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
try { try {
const timeoutDelay = 9800; const timeoutDelay = 9800;
await this.anchorFolder();
setTimeout(() => { setTimeout(() => {
this.setState({ this.setState({
isValidateModalVisible: false, isValidateModalVisible: false,
@ -217,6 +281,34 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
hasValidateAnchoring: false, hasValidateAnchoring: false,
}); });
}, timeoutDelay + 1000); }, 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) { } catch (e) {
console.error(e); console.error(e);
} }
@ -241,7 +333,10 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
if (!documents) return 0; if (!documents) return 0;
const totalDocuments = documents.length; const totalDocuments = documents.length;
const refusedDocuments = documents.filter((document) => document.document_status === EDocumentStatus.REFUSED).length ?? 0; 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 depositedDocuments = totalDocuments - askedDocuments - refusedDocuments;
const percentage = (depositedDocuments / totalDocuments) * 100; const percentage = (depositedDocuments / totalDocuments) * 100;
return isNaN(percentage) ? 0 : percentage; 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 BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import DateField from "@Front/Components/DesignSystem/Form/DateField";
type IProps = {}; type IProps = {};
@ -43,6 +44,8 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
value: this.state.selectedFolder?.deed?.deed_type?.uid, value: this.state.selectedFolder?.deed?.deed_type?.uid,
} as IOption; } as IOption;
const openingDate = new Date(this.state.selectedFolder?.created_at ?? ""); const openingDate = new Date(this.state.selectedFolder?.created_at ?? "");
if (!this.state.selectedFolder?.created_at) return <></>;
const defaultValue = openingDate.toISOString().split("T")[0];
return ( return (
<DefaultNotaryDashboard title={"Ajouter client(s)"} onSelectedFolder={this.onSelectedFolder}> <DefaultNotaryDashboard title={"Ajouter client(s)"} onSelectedFolder={this.onSelectedFolder}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -60,7 +63,7 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
defaultValue={this.state.selectedFolder?.folder_number} defaultValue={this.state.selectedFolder?.folder_number}
/> />
<Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled /> <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>
<div className={classes["button-container"]}> <div className={classes["button-container"]}>

View File

@ -15,8 +15,7 @@ import React from "react";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import OcrResult from "./OcrResult"; import Files from "@Front/Api/LeCoffreApi/Notary/Files/Files";
import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField"; import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {}; type IProps = {};
@ -34,6 +33,7 @@ type IState = {
selectedFile: File | null; selectedFile: File | null;
validatedPercentage: number; validatedPercentage: number;
document: Document | null; document: Document | null;
fileBlob: Blob | null;
}; };
class ViewDocumentsClass extends BasePage<IPropsClass, IState> { class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
@ -48,6 +48,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
selectedFile: null, selectedFile: null,
validatedPercentage: this.getRandomPercentageForOcr(), validatedPercentage: this.getRandomPercentageForOcr(),
document: null, document: null,
fileBlob: null,
}; };
this.closeModals = this.closeModals.bind(this); this.closeModals = this.closeModals.bind(this);
@ -86,7 +87,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
)} )}
<div className={classes["file-container"]}> <div className={classes["file-container"]}>
<FilePreview <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} fileName={this.state.selectedFile.file_name}
key={this.state.selectedFile.uid} key={this.state.selectedFile.uid}
/> />
@ -101,11 +102,11 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
)} )}
</div> </div>
<div className={classes["footer"]}> <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"]}> <div className={classes["ocr-container"]}>
<OcrResult percentage={this.state.validatedPercentage} /> <OcrResult percentage={this.state.validatedPercentage} />
</div> </div>
)} )} */}
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
{this.state.document?.document_status === EDocumentStatus.DEPOSITED && ( {this.state.document?.document_status === EDocumentStatus.DEPOSITED && (
@ -117,7 +118,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
<Button disabled>Télécharger</Button> <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> <Button onClick={this.downloadFile}>Télécharger</Button>
)} )}
</div> </div>
@ -175,38 +176,43 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
document_type: true, document_type: true,
folder: true, folder: true,
}); });
this.setState({ this.setState(
document, {
selectedFileIndex: 0, document,
selectedFile: document.files![0]!, selectedFileIndex: 0,
}); selectedFile: document.files![0]!,
},
() => {
this.getFilePreview();
},
);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
private downloadFile() { private async getFilePreview(): Promise<void> {
const fileName = this.state.selectedFile?.file_path?.split("/").pop(); try {
fetch(Files.getInstance().getUploadLink(this.state.selectedFile?.uid as string)) const fileBlob: Blob = await Files.getInstance().download(this.state.selectedFile?.uid as string);
.then((resp) => resp.blob()) this.setState({
.then((blob) => { fileBlob,
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
// the filename you want
a.download = fileName 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);
}); });
} catch (e) {
console.log(e);
}
}
private downloadFile() {
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 = this.state.selectedFile?.file_name as string;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
} }
private getRandomPercentageForOcr() { private getRandomPercentageForOcr() {
@ -228,20 +234,32 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
private goToPrevious() { private goToPrevious() {
const index = this.state.selectedFileIndex - 1; const index = this.state.selectedFileIndex - 1;
if (this.hasPrevious()) { if (this.hasPrevious()) {
this.setState({ this.setState(
selectedFile: this.state.document!.files![index]!, {
selectedFileIndex: index, selectedFile: this.state.document!.files![index]!,
}); selectedFileIndex: index,
fileBlob: null,
},
() => {
this.getFilePreview();
},
);
} }
} }
private goToNext() { private goToNext() {
if (this.hasNext()) { if (this.hasNext()) {
const index = this.state.selectedFileIndex + 1; const index = this.state.selectedFileIndex + 1;
this.setState({ this.setState(
selectedFile: this.state.document!.files![index]!, {
selectedFileIndex: index, 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} key={customer.uid}
isOpened={this.state.openedCustomer === customer.uid} isOpened={this.state.openedCustomer === customer.uid}
onChange={this.changeUserFolder} 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 Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
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 Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback } from "react"; import { useCallback } from "react";
@ -13,6 +10,7 @@ import { useCallback } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import LandingImage from "./landing-connect.jpeg"; import LandingImage from "./landing-connect.jpeg";
import { FrontendVariables } from "@Front/Config/VariablesFront"; import { FrontendVariables } from "@Front/Config/VariablesFront";
import Link from "next/link";
export default function Login() { export default function Login() {
const router = useRouter(); const router = useRouter();
@ -26,20 +24,6 @@ export default function Login() {
); );
}, [router]); }, [router]);
const redirectCustomerOnConnection = useCallback(() => {
async function getCustomer() {
try {
await UserStore.instance.connectCustomer("antoine.bernard@outlook.com");
await JwtService.getInstance().checkJwt();
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
} catch (e) {
console.error(e);
}
}
getCustomer();
}, [router]);
return ( return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}> <DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -48,15 +32,14 @@ export default function Login() {
<div className={classes["title"]}>Connexion espace professionnel</div> <div className={classes["title"]}>Connexion espace professionnel</div>
</Typography> </Typography>
<Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}> <Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier avec ID.not ! S'identifier avec ID.not
</Button>
<Button onClick={redirectCustomerOnConnection} icon={idNoteLogo} iconposition={"left"}>
S'identifier en tant que customer
</Button> </Button>
<Typography typo={ITypo.P_18}> <Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div> <div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography> </Typography>
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button> <Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div> </div>
</DefaultDoubleSidePage> </DefaultDoubleSidePage>
); );

View File

@ -11,6 +11,7 @@ import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Loader from "@Front/Components/DesignSystem/Loader"; import Loader from "@Front/Components/DesignSystem/Loader";
import UserStore from "@Front/Stores/UserStore"; import UserStore from "@Front/Stores/UserStore";
import Link from "next/link";
export default function LoginCallBack() { export default function LoginCallBack() {
const router = useRouter(); const router = useRouter();
@ -45,7 +46,9 @@ export default function LoginCallBack() {
<Typography typo={ITypo.P_18}> <Typography typo={ITypo.P_18}>
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div> <div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
</Typography> </Typography>
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button> <Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div> </div>
</DefaultDoubleSidePage> </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 { .root {
display: flex; display: flex;
align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
max-width: 530px;
margin: auto; margin: auto;
max-width: 80%;
.text { .title {
margin: 32px 0; margin: 32px 0;
text-align: center;
} @media (max-width: $screen-s) {
font-family: 48px;
.france-connect-button {
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;
} }
} }
}
.forget-password {
margin-top: 32px;
margin-bottom: 8px;
}
.logo {
cursor: pointer;
}
}

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 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 DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import FranceConnectIcon from "./france-connect.svg"; import Image from "next/image";
import ExportIcon from "@Assets/Icons/export.svg"; import { useRouter } from "next/router";
import { FrontendVariables } from "@Front/Config/VariablesFront"; import { useCallback } from "react";
import cryptoRandomString from "crypto-random-string"; 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 class LoginCustomer extends BasePage { export default function Login() {
public override render(): JSX.Element { const router = useRouter();
return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}>
<Typography typo={ITypo.H1}>
<div className={classes["title"]}>Identifiez-vous</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>
</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>
</div>
</DefaultDoubleSidePage>
);
}
private redirectUserOnConnection() { const redirectCustomerOnConnection = useCallback(() => {
const variables = FrontendVariables.getInstance(); async function getCustomer() {
// const baseFronturl = try {
// variables.BACK_API_PROTOCOL + variables.FRONT_APP_HOST + (variables.FRONT_APP_PORT ? ":" + variables.FRONT_APP_PORT : ""); const loginRes = await Customers.getInstance().login();
const authorizeEndPoint = variables.FC_AUTHORIZE_ENDPOINT; router.push(loginRes.enrollment.franceConnectUrl);
const clientId = variables.FC_CLIENT_ID; } catch (e) {
const url = `${authorizeEndPoint}?client_id=${clientId}&redirect_uri=http://localhost:8080/login-callback&scope=openid&response_type=code&state=${cryptoRandomString( console.error(e);
{ length: 64 }, }
)}&nonce=${cryptoRandomString({ length: 64 })}&acr_values=eidas1`; }
window.location.assign(url); getCustomer();
} }, [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>
<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>
<Link href="mailto:g.texier@notaires.fr">
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
</DefaultDoubleSidePage>
);
} }

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

View File

@ -5,18 +5,18 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
gap: 16px;
@media (max-width: $screen-s) { @media (max-width: $screen-s) {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 16px;
} }
.header-right { .header-right {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
width: 150px;
.round { .round {
width: 16px; width: 16px;
height: 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 DefaultOfficeDashboard from "@Front/Components/LayoutTemplates/DefaultOfficeDashboard";
import User, { Office } from "le-coffre-resources/dist/SuperAdmin"; import User, { Office } from "le-coffre-resources/dist/SuperAdmin";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
@ -13,6 +13,8 @@ export default function OfficeInformations(props: IProps) {
let { officeUid } = router.query; let { officeUid } = router.query;
const [officeSelected, setOfficeSelected] = useState<Office | null>(null); const [officeSelected, setOfficeSelected] = useState<Office | null>(null);
const [adminUsers, setAdminUsers] = useState<User[]>([]);
const [collaboratorUsers, setCollaboratorUsers] = useState<User[]>([]);
useEffect(() => { useEffect(() => {
async function getOffice() { async function getOffice() {
@ -31,54 +33,87 @@ export default function OfficeInformations(props: IProps) {
}); });
if (!office) return; if (!office) return;
setOfficeSelected(office); 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(); getOffice();
}, [officeUid]); }, [officeUid]);
const renderUser = (user: User) => ( const renderUser = useCallback(
<> (user: User) => (
<div key={user.uid} className={classes["user-line-desktop"]}> <>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography> <div key={user.uid + "-" + officeUid} className={classes["user-line-desktop"]}>
<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>
</div>
<div key={user.uid} className={classes["user-line-mobile"]}>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Nom
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Prénom
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
E-mail
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Téléphone
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : "Utilisateur restreint"}</Typography>
</div> </div>
<div className={classes["line"]}> <div key={user.uid} className={classes["user-line-mobile"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}> <div className={classes["line"]}>
Rôle <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
</Typography> Nom
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Prénom
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
E-mail
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Téléphone
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Rôle
</Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography>
</div>
</div> </div>
</div> </>
</> ),
[officeUid],
); );
return ( return (
<DefaultOfficeDashboard mobileBackText={"Liste des utilisateurs"}> <DefaultOfficeDashboard mobileBackText={"Liste des utilisateurs"}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -134,22 +169,9 @@ export default function OfficeInformations(props: IProps) {
Rôle Rôle
</Typography> </Typography>
</div> </div>
{officeSelected?.users {adminUsers.map((user) => {
?.filter((user) => { return renderUser(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) => {
return renderUser(user);
})}
</div> </div>
</div> </div>
<div className={classes["users"]}> <div className={classes["users"]}>
@ -174,22 +196,9 @@ export default function OfficeInformations(props: IProps) {
Rôle Rôle
</Typography> </Typography>
</div> </div>
{officeSelected?.users {collaboratorUsers.map((user) => {
?.filter((user) => { return renderUser(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) => {
return renderUser(user);
})}
</div> </div>
</div> </div>
</div> </div>

View File

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

View File

@ -12,6 +12,8 @@ import { useCallback, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService"; 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 = {}; type IProps = {};
export default function RolesCreate(props: IProps) { export default function RolesCreate(props: IProps) {
@ -61,35 +63,44 @@ export default function RolesCreate(props: IProps) {
}, [hasChanged, redirect]); }, [hasChanged, redirect]);
return ( return (
<DefaultRolesDashboard mobileBackText={"Liste des rôles"} hasBackArrow title="Créer un rôle"> <Rules
<div className={classes["root"]}> mode={RulesMode.NECESSARY}
<div className={classes["header"]}> rules={[
<Typography typo={ITypo.H1Bis}>Créer un rôle</Typography> {
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"]}>
<Typography typo={ITypo.H1Bis}>Créer un rôle</Typography>
</div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom du rôle" />
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler
</Button>
<Button type="submit">Créer le rôle</Button>
</div>
</Form>
<Confirm
isOpen={isConfirmModalVisible}
onClose={closeConfirmModal}
onAccept={redirect}
closeBtn
header={"Êtes-vous sur de vouloir quitter sans enregistrer ?"}
cancelText={"Annuler"}
confirmText={"Quitter"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Si vous quittez, toutes les modifications que vous avez effectuées ne seront pas enregistrées.
</Typography>
</div>
</Confirm>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> </DefaultRolesDashboard>
<TextField name="name" placeholder="Nom du rôle" /> </Rules>
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler
</Button>
<Button type="submit">Créer le rôle</Button>
</div>
</Form>
<Confirm
isOpen={isConfirmModalVisible}
onClose={closeConfirmModal}
onAccept={redirect}
closeBtn
header={"Êtes-vous sur de vouloir quitter sans enregistrer ?"}
cancelText={"Annuler"}
confirmText={"Quitter"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Si vous quittez, toutes les modifications que vous avez effectuées ne seront pas enregistrées.
</Typography>
</div>
</Confirm>
</div>
</DefaultRolesDashboard>
); );
} }

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; if (!role) return;
setRoleSelected(role); setRoleSelected(role);
if (!role.rules) return; if (!role.rules) return;
@ -124,7 +128,6 @@ export default function RolesInformations(props: IProps) {
label: "Tout sélectionner", label: "Tout sélectionner",
value: "all", value: "all",
}} }}
toolTip="Tout sélectionner"
onChange={handleSelectAllChange} onChange={handleSelectAllChange}
checked={selectAll} checked={selectAll}
/> />

View File

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

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