Merge branch 'dev' into staging

This commit is contained in:
Vins 2023-07-25 09:51:33 +02:00
commit 1990c9d4c6
130 changed files with 6361 additions and 1305 deletions

View File

@ -6,4 +6,10 @@
],
"description": "media queries"
},
"Default div": {
"prefix": "<div",
"body": [
"<div className={classes[\"$1\"]}>$2</div>"
]
}
}

View File

@ -13,16 +13,16 @@ const nextConfig = {
NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT: process.env.NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT,
NEXT_PUBLIC_IDNOT_CLIENT_ID: process.env.NEXT_PUBLIC_IDNOT_CLIENT_ID,
},
webpack: config => {
config.node = {
fs: 'empty',
child_process: 'empty',
net: 'empty',
dns: 'empty',
tls: 'empty',
};
return config;
},
// webpack: config => {
// config.node = {
// fs: 'empty',
// child_process: 'empty',
// net: 'empty',
// dns: 'empty',
// tls: 'empty',
// };
// return config;
// },
};
module.exports = nextConfig;

1187
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,13 +23,15 @@
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
"form-data": "^4.0.0",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.52",
"jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.58",
"next": "13.2.4",
"prettier": "^2.8.7",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-select": "^5.7.2",
"sass": "^1.59.2",
"sharp": "^0.32.1",
"typescript": "4.9.5"
}
}

View File

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

View File

@ -1,4 +1,5 @@
import { FrontendVariables } from "@Front/Config/VariablesFront";
import CookieService from "@Front/Services/CookieService/CookieService";
export enum ContentType {
JSON = "application/json",
@ -21,11 +22,14 @@ export default abstract class BaseApiService {
}
protected buildHeaders(contentType: ContentType) {
const token = CookieService.getInstance().getCookie("leCoffreAccessToken");
const headers = new Headers();
if (contentType === ContentType.JSON) {
headers.set("Content-Type", contentType);
}
headers.set("Authorization", `Bearer ${token}`);
return headers;
}
@ -33,7 +37,7 @@ export default abstract class BaseApiService {
return JSON.stringify(body);
}
protected async getRequest<T>(url: URL) {
protected async getRequest<T>(url: URL, token?: string) {
const request = async () =>
await fetch(url, {
method: "GET",
@ -42,7 +46,7 @@ export default abstract class BaseApiService {
return this.sendRequest<T>(request);
}
protected async postRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
protected async postRequest<T>(url: URL, body: { [key: string]: unknown } = {}, token?: string) {
return this.sendRequest<T>(
async () =>
await fetch(url, {
@ -64,7 +68,7 @@ export default abstract class BaseApiService {
);
}
protected async putRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
protected async putRequest<T>(url: URL, body: { [key: string]: unknown } = {}, token?: string) {
const request = async () =>
await fetch(url, {
method: "PUT",
@ -86,7 +90,7 @@ export default abstract class BaseApiService {
return this.sendRequest<T>(request);
}
protected async deleteRequest<T>(url: URL, body: { [key: string]: unknown } = {}) {
protected async deleteRequest<T>(url: URL, body: { [key: string]: unknown } = {}, token?: string) {
const request = async () =>
await fetch(url, {
method: "DELETE",
@ -97,7 +101,7 @@ export default abstract class BaseApiService {
return this.sendRequest<T>(request);
}
protected async putFormDataRequest<T>(url: URL, body: FormData) {
protected async putFormDataRequest<T>(url: URL, body: FormData, token?: string) {
const request = async () =>
await fetch(url, {
method: "PUT",

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,49 @@
import { DeedType } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
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);
}
}
}

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/SuperAdmin";
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,65 @@
import { OfficeRole } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../BaseAdmin";
export type IGetRolesParams = {
where?: {};
include?: {};
select?: {};
};
export type IPutRoleParams = {
rules: OfficeRole["rules"];
};
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);
}
}
}

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

@ -15,7 +15,7 @@ export type IPutDeedTypesParams = {
deed?: DeedType["deed"];
office?: DeedType["office"];
archived_at?: DeedType["archived_at"];
deed_type_has_document_types?: DeedType["deed_type_has_document_types"];
document_types?: DeedType["document_types"];
};
export default class DeedTypes extends BaseSuperAdmin {
@ -37,9 +37,11 @@ export default class DeedTypes extends BaseSuperAdmin {
/**
* @description : Get all DeedTypes
*/
public async get(q: IGetDeedTypesParams): Promise<DeedType[]> {
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) {

View File

@ -15,7 +15,7 @@ export type IPutDeedsParams = {
description?: OfficeFolder["description"];
archived_description?: OfficeFolder["archived_description"];
status?: OfficeFolder["status"];
deed_has_document_types?: Deed["deed_has_document_types"];
document_types?: Deed["document_types"];
};
export default class Deeds extends BaseSuperAdmin {

View File

@ -37,10 +37,12 @@ export default class DocumentTypes extends BaseSuperAdmin {
}
}
public async get(q: IGetDocumentTypesparams): Promise<DocumentType[]> {
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) {

View File

@ -1,5 +1,4 @@
import { File } from "le-coffre-resources/dist/SuperAdmin";
import BaseSuperAdmin from "../BaseSuperAdmin";
// TODO Type get query params -> Where + inclue + orderby

View File

@ -88,7 +88,7 @@ export default class Folders extends BaseSuperAdmin {
public async delete(uid: string): Promise<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const targetedFolder = await this.getByUid(uid);
if (targetedFolder.office_folder_has_customers) return Promise.reject(`The folder ${uid} contains customers`);
if (targetedFolder.customers) return Promise.reject(`The folder ${uid} contains customers`);
try {
return await this.deleteRequest(url);
} catch (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

@ -15,7 +15,6 @@ export type IPutUsersParams = {
idNot?: User["idNot"];
contact?: User["contact"];
office_membership?: User["office_membership"];
office_folder_has_stakeholders?: User["office_folder_has_stakeholders"];
documents?: User["documents"];
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,29 @@
@import "@Themes/constants.scss";
.root {
display: inline-flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 24px;
border: 1px solid $grey-medium;
cursor: pointer;
&:hover {
background-color: $grey-medium;
}
&[data-selected="true"] {
background-color: $grey-medium;
}
.left-side {
display: inline-flex;
justify-content: space-between;
align-items: center;
.warning {
margin-left: 32px;
}
}
}

View File

@ -0,0 +1,40 @@
import React, { useCallback } from "react";
import classes from "./classes.module.scss";
import Typography, { ITypo } from "../Typography";
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Image from "next/image";
export type IBlock = {
name: string;
id: string;
selected: boolean;
};
type IProps = {
blocks: IBlock[];
onSelectedBlock: (block: IBlock) => void;
};
export default function BlockList({ blocks, onSelectedBlock }: IProps) {
const selectBlock = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
onSelectedBlock && onSelectedBlock(blocks.find((block) => block.id === e.currentTarget.id)!);
},
[blocks, onSelectedBlock],
);
return (
<div>
{blocks.map((block) => {
return (
<div onClick={selectBlock} key={block.id} id={block.id}>
<div className={classes["root"]} data-selected={block.selected.toString()}>
<div className={classes["left-side"]}>
<Typography typo={ITypo.P_16}>{block.name}</Typography>
</div>
<Image alt="chevron" src={ChevronIcon} />
</div>
</div>
);
})}
</div>
);
}

View File

@ -9,13 +9,29 @@ type IProps = {
name?: string;
option: IOption;
toolTip?: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
checked: boolean;
};
export default class CheckBox extends React.Component<IProps> {
type IState = {
checked: boolean;
};
export default class CheckBox extends React.Component<IProps, IState> {
static defaultProps = {
toolTip: "",
checked: false,
};
constructor(props: IProps) {
super(props);
this.state = {
checked: this.props.checked ?? false,
};
this.onChange = this.onChange.bind(this);
}
public override render(): JSX.Element {
return (
<Typography typo={ITypo.P_ERR_16} color={ITypoColor.BLACK}>
@ -24,6 +40,8 @@ export default class CheckBox extends React.Component<IProps> {
type="checkbox"
name={this.props.name ?? (this.props.option.value as string)}
value={this.props.option.value as string}
onChange={this.onChange}
checked={this.state.checked}
/>
{this.props.option.label}
{this.props.toolTip && <Tooltip className={classes["tooltip"]} text={this.props.toolTip} />}
@ -31,4 +49,20 @@ export default class CheckBox extends React.Component<IProps> {
</Typography>
);
}
public override componentDidUpdate(prevProps: Readonly<IProps>): void {
if (prevProps.checked !== this.props.checked) {
this.setState({
checked: this.props.checked,
});
}
}
private onChange(e: React.ChangeEvent<HTMLInputElement>) {
this.setState({
checked: !this.state.checked,
});
this.props.onChange && this.props.onChange(e);
}
}

View File

@ -4,9 +4,11 @@ import Link from "next/link";
import React from "react";
import Button from "../Button";
import FolderList from "../FolderList";
import SearchBar from "../SearchBar";
import classes from "./classes.module.scss";
import BlockList, { IBlock } from "../BlockList";
import { NextRouter, useRouter } from "next/router";
import { EDocumentStatus } from "le-coffre-resources/dist/SuperAdmin/Document";
type IProps = {
folders: IDashBoardFolder[];
@ -14,17 +16,26 @@ type IProps = {
onSelectedFolder?: (folder: IDashBoardFolder) => void;
onCloseLeftSide?: () => void;
};
type IPropsClass = IProps & {
router: NextRouter;
};
type IState = {
filteredFolders: IDashBoardFolder[];
};
export default class FolderListContainer extends React.Component<IProps, IState> {
public constructor(props: IProps) {
class FolderListContainerClass extends React.Component<IPropsClass, IState> {
private redirectPath: string = this.props.isArchived
? Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.pages.FolderInformation.props.path
: Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path;
public constructor(props: IPropsClass) {
super(props);
this.state = {
filteredFolders: this.props.folders,
};
this.filterFolders = this.filterFolders.bind(this);
this.onSelectedFolder = this.onSelectedFolder.bind(this);
}
public override render(): JSX.Element {
@ -33,15 +44,10 @@ export default class FolderListContainer extends React.Component<IProps, IState>
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar folders={this.props.folders} onChange={this.filterFolders} placeholder="Chercher un dossier" />
<SearchBar onChange={this.filterFolders} placeholder="Chercher un dossier" />
</div>
<div className={classes["folderlist-container"]}>
<FolderList
folders={this.state.filteredFolders}
onSelectedFolder={this.props.onSelectedFolder && this.props.onSelectedFolder}
onCloseLeftSide={this.props.onCloseLeftSide}
isArchived={this.props.isArchived}
/>
<BlockList blocks={this.getBlocks()} onSelectedBlock={this.onSelectedFolder} />
</div>
</div>
{!this.props.isArchived && (
@ -55,8 +61,62 @@ export default class FolderListContainer extends React.Component<IProps, IState>
);
}
private filterFolders(folders: IDashBoardFolder[]): IDashBoardFolder[] {
this.setState({ filteredFolders: folders });
return folders;
private getBlocks(): IBlock[] {
const pendingFolders = this.props.folders
.filter((folder) => {
const pendingDocuments = (folder.documents ?? []).filter(
(document) => document.document_status === EDocumentStatus.DEPOSITED,
);
return pendingDocuments.length >= 1;
})
.sort((folder1, folder2) => {
return folder1.created_at! > folder2.created_at! ? -1 : 1;
});
const otherFolders = this.props.folders
.filter((folder) => {
const pendingDocuments = (folder.documents ?? []).filter(
(document) => document.document_status === EDocumentStatus.DEPOSITED,
);
return pendingDocuments.length === 0;
})
.sort((folder1, folder2) => {
return folder1.created_at! > folder2.created_at! ? -1 : 1;
});
return [...pendingFolders, ...otherFolders].map((folder) => {
return { id: folder.uid!, name: folder.folder_number! + " - " + folder.name!, selected: false };
});
}
private onSelectedFolder(block: IBlock) {
const folder = this.props.folders.find((folder) => folder.uid === block.id);
if (!folder) return;
this.props.onSelectedFolder && this.props.onSelectedFolder(folder);
const path = this.redirectPath.replace("[folderUid]", folder.uid ?? "");
this.props.router.push(path);
}
private filterFolders(value: string): void {
const filteredFolders: IDashBoardFolder[] = this.props.folders.filter((folder) => {
const name = folder.name.toLowerCase();
const number = folder.folder_number.toLowerCase();
if (folder.customers) {
const customerNames = folder.customers
.map((customer) => {
return `${customer.contact?.first_name.toLowerCase()} ${customer.contact?.last_name.toLowerCase()}`;
})
.join(", ");
return name.includes(value) || number.includes(value) || customerNames.includes(value);
}
return name.includes(value) || number.includes(value);
});
this.setState({ filteredFolders });
}
}
export default function FolderListContainer(props: IProps) {
const router = useRouter();
return <FolderListContainerClass {...props} router={router} />;
}

View File

@ -55,6 +55,12 @@ export default abstract class BaseField<P extends IProps, S extends IState = ISt
});
}
if (this.props.defaultValue !== prevProps.defaultValue) {
this.setState({
value: this.props.defaultValue ?? "",
});
}
if (this.props.validationError !== prevProps.validationError) {
this.setState({
validationError: this.props.validationError ?? null,

View File

@ -123,6 +123,12 @@ export default class SelectField extends React.Component<IProps, IState> {
errors: this.props.errors ?? null,
});
}
if (this.props.selectedOption !== prevProps.selectedOption) {
this.setState({
selectedOption: this.props.selectedOption ?? null,
});
}
}
static getDerivedStateFromProps(props: IProps, state: IState) {

View File

@ -32,6 +32,11 @@ export default class BurgerModal extends React.Component<IProps, IState> {
text="Dossiers archivés"
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
/>
<NavigationLink
path={Module.getInstance().get().modules.pages.Collaborators.props.path}
text="Collaborateurs"
routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
/>
<div className={classes["separator"]} />
<LogOutButton />
</div>

View File

@ -24,6 +24,11 @@ export default class Navigation extends React.Component<IProps, IState> {
path={Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path}
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
/>
<HeaderLink
text={"Collaborateurs"}
path={Module.getInstance().get().modules.pages.Collaborators.props.path}
routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
/>
</div>
);
}

View File

@ -1,8 +1,9 @@
import React from "react";
import classes from "./classes.module.scss";
import NavigationLink from "../../NavigationLink";
import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
import Module from "@Front/Config/Module";
import React from "react";
import NavigationLink from "../../NavigationLink";
import classes from "./classes.module.scss";
type IProps = {
isOpen: boolean;
@ -19,6 +20,34 @@ export default class ProfileModal extends React.Component<IProps, IState> {
<div className={classes["background"]} onClick={this.props.closeModal} />
<div className={classes["root"]}>
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
<NavigationLink path={Module.getInstance().get().modules.pages.Roles.props.path} text="Gestion des rôles" />
<NavigationLink
path={Module.getInstance().get().modules.pages.DeedTypes.props.path}
text="Paramétrage des listes de pièces"
routesActive={[
Module.getInstance().get().modules.pages.DeedTypes.props.path,
Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path,
Module.getInstance().get().modules.pages.DeedTypes.pages.DeedTypesInformations.props.path,
Module.getInstance().get().modules.pages.DeedTypes.pages.Edit.props.path,
]}
/>
<NavigationLink
path={Module.getInstance().get().modules.pages.Users.props.path}
text="Gestion des utilisateurs"
routesActive={[
Module.getInstance().get().modules.pages.Users.props.path,
Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path,
]}
/>
<NavigationLink
path={Module.getInstance().get().modules.pages.Offices.props.path}
text="Gestion des offices"
routesActive={[
Module.getInstance().get().modules.pages.Offices.props.path,
Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path,
]}
/>
<NavigationLink text="Gestion des noms de domaine" />
<NavigationLink text="CGU" />
<div className={classes["separator"]} />
<LogOutButton />

View File

@ -1,5 +1,4 @@
import LoopIcon from "@Assets/Icons/loop.svg";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Image from "next/image";
import React from "react";
@ -7,10 +6,10 @@ import Typography, { ITypo } from "../Typography";
import classes from "./classes.module.scss";
type IProps = {
folders: IDashBoardFolder[];
onChange?: (folders: IDashBoardFolder[]) => IDashBoardFolder[];
onChange?: (input: string) => void;
placeholder?: string;
};
type IState = {
hasValue: boolean;
};
@ -46,30 +45,10 @@ export default class SearchBar extends React.Component<IProps, IState> {
const hasValue = event.target.value.length > 0;
this.doesInputHaveValue(hasValue);
if (!this.props.onChange) return;
this.props.onChange(this.filterFolders(event)!);
this.props.onChange(event.target.value);
}
private doesInputHaveValue(hasValue: boolean) {
this.setState({ hasValue });
}
private filterFolders(event: React.ChangeEvent<HTMLInputElement>) {
const filteredFolders: IDashBoardFolder[] = this.props.folders.filter((folder) => {
const name = folder.name.toLowerCase();
const number = folder.folder_number.toLowerCase();
const value = event.target.value.toLowerCase();
if (folder.office_folder_has_customers) {
const customerNames = folder.office_folder_has_customers
.map((customer) => {
return `${customer.customer.contact?.first_name.toLowerCase()} ${customer.customer.contact?.last_name.toLowerCase()}`;
})
.join(", ");
return name.includes(value) || number.includes(value) || customerNames.includes(value);
}
return name.includes(value) || number.includes(value);
});
return filteredFolders;
}
}

View File

@ -0,0 +1,21 @@
@import "@Themes/constants.scss";
.root {
width: calc(100vh - 83px);
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
height: 100%;
border-right: 1px solid var(--grey-medium);
}
}

View File

@ -0,0 +1,64 @@
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import User from "le-coffre-resources/dist/Notary";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import { useRouter } from "next/router";
import Module from "@Front/Config/Module";
type IProps = {
collaborators: User[];
onSelectedCollaborator?: (user: User) => void;
onCloseLeftSide?: () => void;
};
export default function CollaboratorListContainer(props: IProps) {
const [filteredUsers, setFilteredUsers] = useState<User[]>(props.collaborators);
const router = useRouter();
const { collaboratorUid } = router.query;
const filterUsers = useCallback(
(input: string) => {
const filteredUsers = props.collaborators.filter((user) => {
return (
user.contact?.first_name?.toLowerCase().includes(input.toLowerCase()) ||
user.contact?.last_name?.toLowerCase().includes(input.toLowerCase())
);
});
setFilteredUsers(filteredUsers);
},
[props.collaborators],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const redirectPath = Module.getInstance().get().modules.pages.Collaborators.pages.CollaboratorInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterUsers} placeholder="Chercher un collaborateur" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredUsers.map((user) => {
return {
name: user.contact?.first_name + " " + user.contact?.last_name,
id: user.uid!,
selected: user.uid === collaboratorUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,121 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/Admin/Users/Users";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import JwtService from "@Front/Services/JwtService/JwtService";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import User from "le-coffre-resources/dist/Notary";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import CollaboratorListContainer from "./CollaboratorListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedUser: (user: User) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
collaborators: User[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultCollaboratorDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
collaborators: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.collaborators && (
<CollaboratorListContainer collaborators={this.state.collaborators} onCloseLeftSide={this.onCloseLeftSide} />
)}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const jwt = JwtService.getInstance().decodeJwt();
if (!jwt) return;
const query: IGetUsersparams = {
where: { office_uid: jwt!.office_Id },
include: { contact: true },
};
const collaborators = await Users.getInstance().get(query);
this.setState({ collaborators });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -0,0 +1,22 @@
@import "@Themes/constants.scss";
.root {
width: calc(100vh - 83px);
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
max-height: calc(100vh - 290px);
overflow: auto;
border-right: 1px solid var(--grey-medium);
}
}

View File

@ -0,0 +1,69 @@
import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import Button from "@Front/Components/DesignSystem/Button";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module";
import { DeedType } from "le-coffre-resources/dist/Admin";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {
deedTypes: DeedType[];
onSelectedDeed?: (deed: DeedTypes) => void;
onCloseLeftSide?: () => void;
};
export default function DeedListContainer(props: IProps) {
const [filteredUsers, setFilteredUsers] = useState<DeedType[]>(props.deedTypes);
const router = useRouter();
const { deedTypeUid } = router.query;
const filterDeeds = useCallback(
(input: string) => {
const filteredUsers = props.deedTypes.filter((deedType) => {
return deedType.name?.toLowerCase().includes(input.toLowerCase());
});
setFilteredUsers(filteredUsers);
},
[props.deedTypes],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const redirectPath = Module.getInstance().get().modules.pages.DeedTypes.pages.DeedTypesInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterDeeds} placeholder="Chercher un type d'acte" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredUsers.map((deed) => {
return {
name: deed.name,
id: deed.uid!,
selected: deedTypeUid === deed.uid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
<div>
<Link href={Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path}>
<Button fullwidth={true}>Créer un type d'acte</Button>
</Link>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,116 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import DeedTypes, { IGetDeedTypesParams } from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import { Deed, DeedType } from "le-coffre-resources/dist/Admin";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import DeedListContainer from "./DeedTypeListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedDeed: (deed: Deed) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
deedTypes: DeedType[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultDeedTypesDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
deedTypes: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.deedTypes && (
<DeedListContainer deedTypes={this.state.deedTypes} onCloseLeftSide={this.onCloseLeftSide} />
)}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const query: IGetDeedTypesParams = {};
const deedTypes = await DeedTypes.getInstance().get(query);
this.setState({ deedTypes });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -0,0 +1,29 @@
@import "@Themes/constants.scss";
.root {
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
max-height: calc(100vh - 290px);
overflow: auto;
border-right: 1px solid var(--grey-medium);
}
.create-container {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}

View File

@ -0,0 +1,69 @@
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import Button from "@Front/Components/DesignSystem/Button";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module";
import { DocumentType } from "le-coffre-resources/dist/SuperAdmin";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {
documentTypes: DocumentType[];
onSelectedDocumentType?: (documentType: DocumentType) => void;
onCloseLeftSide?: () => void;
};
export default function DocumentTypeListContainer(props: IProps) {
const [filteredDocumentTypes, setFilteredDocumentTypes] = useState<DocumentType[]>(props.documentTypes);
const router = useRouter();
const { documentTypeUid } = router.query;
const filterDocumentTypes = useCallback(
(input: string) => {
const filteredDocumentTypes = props.documentTypes.filter((documentType) => {
return documentType.name.toLowerCase().includes(input.toLowerCase());
});
setFilteredDocumentTypes(filteredDocumentTypes);
},
[props.documentTypes],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
console.log("Block selected :", block);
const redirectPath = Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterDocumentTypes} placeholder="Chercher un document" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredDocumentTypes.map((documentType) => {
return {
name: documentType.name,
id: documentType.uid!,
selected: documentType.uid === documentTypeUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
<div className={classes["create-container"]}>
<Link href={Module.getInstance().get().modules.pages.DocumentTypes.pages.Create.props.path}>
<Button fullwidth={true}>Créer un type de document</Button>
</Link>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,121 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import DocumentTypes from "@Front/Api/LeCoffreApi/SuperAdmin/DocumentTypes/DocumentTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import JwtService from "@Front/Services/JwtService/JwtService";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import { DocumentType } from "le-coffre-resources/dist/Notary";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import DocumentTypeListContainer from "./DocumentTypeListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedDocumentType: (documentType: DocumentType) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
documentTypes: DocumentType[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultDocumentTypesDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
documentTypes: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.documentTypes && (
<DocumentTypeListContainer documentTypes={this.state.documentTypes} onCloseLeftSide={this.onCloseLeftSide} />
)}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const jwt = JwtService.getInstance().decodeJwt();
if (!jwt) return;
const documentTypes = await DocumentTypes.getInstance().get({
where: {
office_uid: jwt.office_Id,
},
});
this.setState({ documentTypes });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -1,4 +1,3 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders, { IGetFoldersParams } from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
@ -9,12 +8,11 @@ import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
type IProps = {
title: string;
@ -39,7 +37,7 @@ export type IDashBoardFolder = {
description: OfficeFolder["description"];
deed?: OfficeFolder["deed"];
created_at: OfficeFolder["created_at"];
office_folder_has_customers?: OfficeFolder["office_folder_has_customers"];
customers?: OfficeFolder["customers"];
archived_description: OfficeFolder["archived_description"];
status: OfficeFolder["status"];
};
@ -118,9 +116,7 @@ export default class DefaultNotaryDashboard extends React.Component<IProps, ISta
include: {
deed: { include: { deed_type: true } },
office: true,
office_folder_has_customers: {
include: {
customer: {
customers: {
include: {
contact: true,
documents: {
@ -132,8 +128,6 @@ export default class DefaultNotaryDashboard extends React.Component<IProps, ISta
},
},
},
},
},
documents: {
include: {
depositor: {

View File

@ -0,0 +1,21 @@
@import "@Themes/constants.scss";
.root {
width: calc(100vh - 83px);
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
height: 100%;
border-right: 1px solid var(--grey-medium);
}
}

View File

@ -0,0 +1,63 @@
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module";
import { Office } from "le-coffre-resources/dist/SuperAdmin";
import { useRouter } from "next/router";
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {
offices: Office[];
onSelectedOffice?: (office: Office) => void;
onCloseLeftSide?: () => void;
};
export default function OfficeListContainer(props: IProps) {
const [filteredOffices, setFilteredOffices] = useState<Office[]>(props.offices);
const router = useRouter();
const { officeUid } = router.query;
const filterOffices = useCallback(
(input: string) => {
const filteredOffices = props.offices.filter((office) => {
return (
office.name.toLowerCase().includes(input.toLowerCase()) || office.crpcen?.toLowerCase().includes(input.toLowerCase())
);
});
setFilteredOffices(filteredOffices);
},
[props.offices],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const redirectPath = Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterOffices} placeholder="Chercher un utilisateur" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredOffices.map((office) => {
return {
name: office.crpcen + " - " + office.name,
id: office.uid!,
selected: office.uid === officeUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,112 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import { Office } from "le-coffre-resources/dist/Notary";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import OfficeListContainer from "./OfficeListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedOffice: (office: Office) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
offices: Office[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultOfficeDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
offices: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.offices && <OfficeListContainer offices={this.state.offices} onCloseLeftSide={this.onCloseLeftSide} />}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const offices = await Offices.getInstance().get();
this.setState({ offices });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -0,0 +1,21 @@
@import "@Themes/constants.scss";
.root {
width: calc(100vh - 83px);
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
height: 100%;
border-right: 1px solid var(--grey-medium);
}
}

View File

@ -0,0 +1,62 @@
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module";
import { OfficeRole } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router";
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {
roles: OfficeRole[];
onSelectedRole?: (role: OfficeRole) => void;
onCloseLeftSide?: () => void;
};
export default function RoleListContainer(props: IProps) {
const [filteredRoles, setFilteredRoles] = useState<OfficeRole[]>(props.roles);
const router = useRouter();
const { roleUid } = router.query;
const filterRoles = useCallback(
(input: string) => {
const filteredRoles = props.roles.filter((role) => {
return role.name?.toLowerCase().includes(input.toLowerCase());
});
setFilteredRoles(filteredRoles);
},
[props.roles],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const redirectPath = Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterRoles} placeholder="Chercher un rôle" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredRoles.map((role) => {
return {
name: role.name,
id: role.uid!,
selected: role.uid === roleUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,118 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import OfficeRoles, { IGetRolesParams } from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import { OfficeRole } from "le-coffre-resources/dist/Admin";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import RoleListContainer from "./RoleListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedRole: (role: OfficeRole) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
roles: OfficeRole[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultRoleDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
roles: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.roles && <RoleListContainer roles={this.state.roles} onCloseLeftSide={this.onCloseLeftSide} />}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const query: IGetRolesParams = {
include: { rules: true },
};
const roles = await OfficeRoles.getInstance().get(query);
console.log(roles);
this.setState({ roles });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -0,0 +1,21 @@
@import "@Themes/constants.scss";
.root {
width: calc(100vh - 83px);
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
flex: 1;
}
.searchbar {
padding: 40px 24px 24px 24px;
}
.folderlist-container {
height: 100%;
border-right: 1px solid var(--grey-medium);
}
}

View File

@ -0,0 +1,64 @@
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module";
import User from "le-coffre-resources/dist/Notary";
import { useRouter } from "next/router";
import React, { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {
users: User[];
onSelectedUser?: (user: User) => void;
onCloseLeftSide?: () => void;
};
export default function UserListContainer(props: IProps) {
const [filteredUsers, setFilteredUsers] = useState<User[]>(props.users);
const router = useRouter();
const { userUid } = router.query;
const filterUsers = useCallback(
(input: string) => {
const filteredUsers = props.users.filter((user) => {
return (
user.contact?.first_name?.toLowerCase().includes(input.toLowerCase()) ||
user.contact?.last_name?.toLowerCase().includes(input.toLowerCase())
);
});
setFilteredUsers(filteredUsers);
},
[props.users],
);
const onSelectedBlock = useCallback(
(block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const redirectPath = Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id));
},
[props, router],
);
return (
<div className={classes["root"]}>
<div className={classes["header"]}>
<div className={classes["searchbar"]}>
<SearchBar onChange={filterUsers} placeholder="Chercher un utilisateur" />
</div>
<div className={classes["folderlist-container"]}>
<BlockList
blocks={filteredUsers.map((user) => {
return {
name: user.contact?.first_name + " " + user.contact?.last_name,
id: user.uid!,
selected: user.uid === userUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,117 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - 83px);
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: $white;
z-index: 3;
display: flex;
width: 389px;
min-width: 389px;
transition: all 0.3s $custom-easing;
overflow: hidden;
@media (max-width: ($screen-m - 1px)) {
width: 56px;
min-width: 56px;
transform: translateX(-389px);
&.opened {
transform: translateX(0px);
width: 389px;
min-width: 389px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: $white;
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - 83px);
border-right: 1px $grey-medium solid;
@media (min-width: $screen-m) {
display: none;
}
.chevron-icon {
margin-top: 21px;
transform: rotate(180deg);
cursor: pointer;
}
@media (max-width: $screen-s) {
display: none;
}
}
.right-side {
min-width: calc(100vw - 389px);
padding: 64px 48px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
padding: 40px 16px 64px 16px;
flex: 1;
min-width: unset;
}
.back-arrow-mobile {
display: none;
@media (max-width: $screen-s) {
display: block;
margin-bottom: 24px;
}
}
.back-arrow-desktop {
@media (max-width: $screen-s) {
display: none;
}
}
}
}
}

View File

@ -0,0 +1,115 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames";
import User from "le-coffre-resources/dist/Notary";
import Image from "next/image";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import UserListContainer from "./UserListContainer";
type IProps = {
title: string;
children?: ReactNode;
onSelectedUser: (user: User) => void;
hasBackArrow: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IState = {
users: User[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultUserDashboard extends React.Component<IProps, IState> {
private onWindowResize = () => {};
public static defaultProps: Partial<IProps> = {
hasBackArrow: false,
};
public constructor(props: IProps) {
super(props);
this.state = {
users: null,
isLeftSideOpen: false,
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
};
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
}
public override render(): JSX.Element {
return (
<div className={classes["root"]}>
<Header isUserConnected={true} />
<div className={classes["content"]}>
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />}
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}>
{this.state.users && <UserListContainer users={this.state.users} onCloseLeftSide={this.onCloseLeftSide} />}
</div>
<div className={classNames(classes["closable-left-side"])}>
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} />
</div>
<div className={classes["right-side"]}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.mobileBackText && (
<div className={classes["back-arrow-mobile"]}>
<Button
icon={ChevronIcon}
iconposition={"left"}
iconstyle={{ transform: "rotate(180deg)", width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}
onClick={this.onOpenLeftSide}>
{this.props.mobileBackText ?? "Retour"}
</Button>
</div>
)}
{this.props.children}
</div>
</div>
<Version />
</div>
);
}
public override async componentDidMount() {
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
const query: IGetUsersparams = {
include: { contact: true },
};
const users = await Users.getInstance().get(query);
this.setState({ users });
}
public override componentWillUnmount() {
this.onWindowResize();
}
private onOpenLeftSide() {
this.setState({ isLeftSideOpen: true });
}
private onCloseLeftSide() {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ isLeftSideOpen: false });
}
private onResize(window: Window) {
if (window.innerWidth > 1023) {
if (!this.state.leftSideCanBeClosed) return;
this.setState({ leftSideCanBeClosed: false });
}
this.setState({ leftSideCanBeClosed: true });
}
}

View File

@ -10,7 +10,6 @@ import React from "react";
import classes from "./classes.module.scss";
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Customer, { Document } from "le-coffre-resources/dist/Customer";
import { document } from "./../../../Components/Layouts/DesignSystem/dummyData";
type IProps = {
targetedCustormer: string; // MOCK
@ -69,7 +68,7 @@ export default class ClientDashboard extends Base<IProps, IState> {
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant.
</Typography>
<DepositDocument document={document} />
{/* <DepositDocument document={document} /> */}
</div>
</Confirm>
</div>

View File

@ -0,0 +1,46 @@
@import "@Themes/constants.scss";
.root {
.user-infos {
background-color: var(--grey-soft);
display: flex;
justify-content: space-between;
padding: 24px;
margin-top: 32px;
@media (max-width: $screen-l) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
}
@media (max-width: $screen-s) {
grid-template-columns: repeat(1, 1fr);
}
.user-infos-row {
display: flex;
flex-direction: column;
gap: 12px;
}
}
.role-container {
padding: 32px 16px;
border: 1px solid var(--grey);
margin-top: 32px;
.first-line {
display: flex;
justify-content: space-between;
}
.second-line {
margin-top: 32px;
}
.third-line {
margin-top: 32px;
}
}
}

View File

@ -0,0 +1,118 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultCollaboratorDashboard from "@Front/Components/LayoutTemplates/DefaultCollaboratorDashboard";
import Module from "@Front/Config/Module";
import User from "le-coffre-resources/dist/Notary";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {};
export default function CollaboratorInformations(props: IProps) {
const router = useRouter();
let { collaboratorUid } = router.query;
const [userSelected, setUserSelected] = useState<User | null>(null);
const [availableRoles, setAvailableRoles] = useState<IOption[]>([]);
useEffect(() => {
async function getUser() {
if (!collaboratorUid) return;
const user = await Users.getInstance().getByUid(collaboratorUid as string, {
q: {
contact: true,
office_role: true,
role: true,
},
});
if (!user) return;
const roles = await OfficeRoles.getInstance().get();
if (!roles) return;
setAvailableRoles(roles.map((role) => ({ value: role.uid, label: role.name })));
setUserSelected(user);
}
getUser();
}, [collaboratorUid]);
return (
<DefaultCollaboratorDashboard mobileBackText={"Liste des collaborateurs"}>
<div className={classes["root"]}>
<div className={classes["folder-header"]}>
<Typography typo={ITypo.H1Bis}>{userSelected?.contact?.first_name + " " + userSelected?.contact?.last_name}</Typography>
</div>
<div className={classes["user-infos"]}>
<div className={classes["user-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Nom
</Typography>
<Typography typo={ITypo.P_18}>{userSelected?.contact?.first_name}</Typography>
</div>
<div className={classes["user-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Prénom
</Typography>
<Typography typo={ITypo.P_18}>{userSelected?.contact?.last_name}</Typography>
</div>
<div className={classes["user-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Numéro de téléphone
</Typography>
<Typography typo={ITypo.P_18}>{userSelected?.contact?.phone_number}</Typography>
</div>
<div className={classes["user-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Email
</Typography>
<Typography typo={ITypo.P_18}>{userSelected?.contact?.email}</Typography>
</div>
</div>
<div className={classes["role-container"]}>
<div className={classes["first-line"]}>
<Typography typo={ITypo.P_SB_18}>Modifier le rôle</Typography>
<div className={classes["gestion-role"]}>
<Link href={Module.getInstance().get().modules.pages.Roles.props.path}>
<Button
icon={ChevronIcon}
iconposition={"right"}
iconstyle={{ width: "22px", height: "22px" }}
variant={EButtonVariant.LINE}>
Gestion des rôles
</Button>
</Link>
</div>
</div>
<div className={classes["second-line"]}>
<SelectField
placeholder="Rôle"
name="role"
options={availableRoles}
selectedOption={{
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
label: userSelected?.office_role ? userSelected?.office_role?.name : userSelected?.role?.name!,
}}
/>
</div>
<div className={classes["third-line"]}>
<CheckBox
option={{
value: "1",
label: "Nommer administrateur de l'office",
}}
toolTip="blabla"
/>
</div>
</div>
</div>
</DefaultCollaboratorDashboard>
);
}

View File

@ -0,0 +1,72 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.no-folder-selected {
width: 100%;
.choose-a-folder {
margin-top: 96px;
text-align: center;
}
}
.folder-informations {
width: 100%;
min-height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
flex-grow: 1;
.folder-header {
width: 100%;
.header {
margin-bottom: 32px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
}
.second-box {
margin-top: 24px;
margin-bottom: 32px;
}
.progress-bar {
margin-bottom: 32px;
}
.button-container {
width: 100%;
text-align: center;
:first-child {
margin-right: 12px;
}
> * {
margin: auto;
}
@media (max-width: $screen-m) {
:first-child {
margin-right: 0;
margin-bottom: 12px;
}
> * {
width: 100%;
}
}
}
.modal-title {
margin-bottom: 24px;
}
}
}

View File

@ -0,0 +1,26 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import BasePage from "../Base";
import classes from "./classes.module.scss";
import DefaultCollaboratorDashboard from "@Front/Components/LayoutTemplates/DefaultCollaboratorDashboard";
type IProps = {};
type IState = {};
export default class Collaborators extends BasePage<IProps, IState> {
public override render(): JSX.Element {
return (
<DefaultCollaboratorDashboard title={"Dossier"} mobileBackText={"Liste des collaborateurs"}>
<div className={classes["root"]}>
<div className={classes["no-folder-selected"]}>
<Typography typo={ITypo.H1Bis}>Informations du collaboraeur</Typography>
<div className={classes["choose-a-folder"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez un collaborateur
</Typography>
</div>
</div>
</div>
</DefaultCollaboratorDashboard>
);
}
}

View File

@ -0,0 +1,24 @@
@import "@Themes/constants.scss";
.root {
.header {
margin-top: 24px;
}
.form-container {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 32px;
}
.buttons-container {
display: flex;
gap: 32px;
@media (max-width: $screen-s) {
flex-direction: column-reverse;
gap: 16px;
}
}
}

View File

@ -0,0 +1,32 @@
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
import { useCallback } from "react";
import classes from "./classes.module.scss";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {};
export default function DeedTypesCreate(props: IProps) {
const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
return (
<DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"} hasBackArrow title="Créer un type d'acte">
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography>
</div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField name="name" placeholder="Nom de l'acte" />
<TextAreaField name="description" placeholder="Description" />
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
<Button type="submit">Créer le type d'acte</Button>
</div>
</Form>
</div>
</DefaultDeedTypesDashboard>
);
}

View File

@ -0,0 +1,24 @@
@import "@Themes/constants.scss";
.root {
.header {
margin-top: 24px;
}
.form-container {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 32px;
}
.buttons-container {
display: flex;
gap: 32px;
@media (max-width: $screen-s) {
flex-direction: column-reverse;
gap: 16px;
}
}
}

View File

@ -0,0 +1,53 @@
import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import Form from "@Front/Components/DesignSystem/Form";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
import { DeedType } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
export default function DeedTypesEdit() {
const router = useRouter();
let { deedTypeUid } = router.query;
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
useEffect(() => {
async function getDeedType() {
if (!deedTypeUid) return;
const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
q: {
document_types: true,
},
});
setDeedTypeSelected(deedType);
}
getDeedType();
}, [deedTypeUid]);
const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
return (
<DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"} hasBackArrow title="Modifier les informations d'un acte">
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Modifier les informations de l'acte</Typography>
</div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} />
<TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} />
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
<Button type="submit">Enregistrer</Button>
</div>
</Form>
</div>
</DefaultDeedTypesDashboard>
);
}

View File

@ -0,0 +1,91 @@
@import "@Themes/constants.scss";
.root {
.header {
display: flex;
justify-content: space-between;
align-items: flex-end;
@media (max-width: $screen-l) {
flex-direction: column;
align-items: flex-start;
gap: 24px;
}
}
.subtitle {
margin-top: 32px;
}
.deed-type-container {
margin-top: 32px;
display: flex;
gap: 100px;
justify-content: space-between;
padding: 24px;
background-color: var(--grey-soft);
@media (max-width: $screen-l) {
gap: 80px;
}
@media (max-width: $screen-m) {
flex-direction: column;
gap: 32px;
}
.infos {
display: flex;
gap: 100px;
flex: 1;
@media (max-width: $screen-l) {
flex-direction: column;
gap: 32px;
}
.box {
.box-title {
margin-bottom: 8px;
opacity: 0.4;
}
}
.middle-box {
flex: 1;
}
}
.pencil {
align-self: center;
@media (max-width: $screen-m) {
align-self: flex-start;
}
}
}
.documents-container {
margin-top: 32px;
padding: 32px 16px;
border: 1px solid var(--grey);
.container-title {
}
.documents {
margin-top: 32px;
}
.button-container {
margin-top: 32px;
}
}
.delete-container {
display: flex;
justify-content: center;
margin-top: 32px;
}
}

View File

@ -0,0 +1,129 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import PenICon from "@Assets/Icons/pen.svg";
import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
import Module from "@Front/Config/Module";
import classNames from "classnames";
import { DeedType, DocumentType } from "le-coffre-resources/dist/Admin";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import { MultiValue } from "react-select";
import classes from "./classes.module.scss";
type IProps = {};
export default function DeedTypesInformations(props: IProps) {
const router = useRouter();
let { deedTypeUid } = router.query;
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
const [availableDocuments, setAvailableDocuments] = useState<DocumentType[]>([]);
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
useEffect(() => {
async function getDeedType() {
if (!deedTypeUid) return;
const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
q: {
document_types: true,
},
});
setDeedTypeSelected(deedType);
}
async function getDocuments() {
const documents = await DocumentTypes.getInstance().get({});
setAvailableDocuments(documents);
}
setSelectedDocuments([]);
getDocuments();
getDeedType();
}, [deedTypeUid]);
const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
setSelectedDocuments(values as IOption[]);
}, []);
const formattedOptions: IOption[] = availableDocuments.map((document) => {
return {
label: document.name,
value: document.uid,
};
});
return (
<DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"}>
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography>
<Link href={Module.getInstance().get().modules.pages.DocumentTypes.props.path}>
<Button variant={EButtonVariant.LINE}>
Modifier la liste des documents
<Image src={ChevronIcon} alt="Chevron" />
</Button>
</Link>
</div>
<div className={classes["subtitle"]}>
<Typography typo={ITypo.H3}>{deedTypeSelected?.name}</Typography>
</div>
<div className={classes["deed-type-container"]}>
<div className={classes["infos"]}>
<div className={classes["box"]}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
Nom du type d'acte
</Typography>
<Typography typo={ITypo.P_18}>{deedTypeSelected?.name}</Typography>
</div>
<div className={classNames(classes["middle-box"], classes["box"])}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
Description
</Typography>
<Typography typo={ITypo.P_18}>{deedTypeSelected?.description}</Typography>
</div>
</div>
<div className={classes["pencil"]}>
<Link
href={Module.getInstance()
.get()
.modules.pages.DeedTypes.pages.Edit.props.path.replace("[uid]", deedTypeUid as string)}
className={classes["edit-icon-container"]}>
<Image src={PenICon} alt="éditer le type d'acte" />
</Link>
</div>
</div>
<div className={classes["documents-container"]}>
<Form onSubmit={onSubmitHandler}>
<div className={classes["container-title"]}>
<Typography typo={ITypo.P_SB_18}>Documents paramétrés</Typography>
</div>
<div className={classes["documents"]}>
<MultiSelect
options={formattedOptions}
placeholder="Type de document"
onChange={onDocumentChangeHandler}
defaultValue={selectedDocuments}
/>
</div>
<div className={classes["button-container"]}>
<Button type="submit">Enregistrer</Button>
</div>
</Form>
</div>
<div className={classes["delete-container"]}>
<Button variant={EButtonVariant.GHOST}>Supprimer</Button>
</div>
</div>
</DefaultDeedTypesDashboard>
);
}

View File

@ -0,0 +1,17 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.no-role-selected {
width: 100%;
.choose-a-role {
margin-top: 96px;
text-align: center;
}
}
}

View File

@ -0,0 +1,26 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
import BasePage from "../Base";
import classes from "./classes.module.scss";
type IProps = {};
type IState = {};
export default class DeedTypes extends BasePage<IProps, IState> {
public override render(): JSX.Element {
return (
<DefaultDeedTypesDashboard mobileBackText={"Liste des listes de pièces"}>
<div className={classes["root"]}>
<div className={classes["no-role-selected"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography>
<div className={classes["choose-a-role"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez une liste de pièces
</Typography>
</div>
</div>
</div>
</DefaultDeedTypesDashboard>
);
}
}

View File

@ -1,18 +0,0 @@
.root {
margin-left: 35px;
margin-right: 35px;
.section {
margin-bottom: 32px;
}
.sub-section {
margin-bottom: 24px;
}
.inline-flex {
display: inline-flex;
}
.folder-conatainer {
width: 389px;
}
}

View File

@ -1,347 +0,0 @@
import { ECustomerStatus } from "le-coffre-resources/dist/Customer/Customer";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
import {
Address,
Contact,
Customer,
Deed,
DeedType,
Document,
DocumentType,
File,
Office,
OfficeFolder,
OfficeFolderHasCustomer,
} from "le-coffre-resources/dist/Notary";
export const address: Address = {
uid: "a&2azedzaa3",
address: "123",
city: "France",
zip_code: 78140,
created_at: new Date(),
updated_at: new Date(),
};
export const office: Office = {
uid: "111zdazaefez213",
idNot: "12EE12",
name: "Office 1",
crpcen: "AZezdz",
address: address,
created_at: new Date(),
updated_at: new Date(),
office_status: "ACTIVATED",
};
export const deedType: DeedType = {
uid: "123azefezgzeg312",
name: "Acte mariage",
description: "dzsdaf",
archived_at: new Date(),
office: office,
created_at: new Date(),
updated_at: new Date(),
};
export const deed: Deed = {
uid: "zegefzeferg",
deed_type: deedType,
created_at: new Date(),
updated_at: new Date(),
};
export const contact: Contact = {
uid: "contact_1_uid",
first_name: "John",
last_name: "Doe",
email: "johnDoe@gmail.com",
address: address,
created_at: new Date(),
updated_at: new Date(),
cell_phone_number: "0132249865",
phone_number: "0132249865",
civility: "MALE",
};
export const contact2: Contact = {
uid: "contact_2_uid",
first_name: "Customer2",
last_name: "Doe",
email: "johnDoe@gmail.com",
address: address,
created_at: new Date(),
updated_at: new Date(),
cell_phone_number: "0132249865",
phone_number: "0132249865",
civility: "MALE",
};
export const docType: DocumentType = {
name: "Votre document",
uid: "fezezfazegezrgrezg",
created_at: new Date(),
updated_at: new Date(),
public_description: "",
private_description: "",
archived_at: new Date(),
office: office,
};
export const identityDocType: DocumentType = {
name: "Carte d'identité",
uid: "fezezfazegezrgrezg",
created_at: new Date(),
updated_at: new Date(),
public_description: "Carte d'identité public description",
private_description: "Carte d'identité private description",
archived_at: new Date(),
office: office,
};
export const customer: Customer = {
uid: "erhtgerfzeare",
contact: contact,
created_at: new Date(),
updated_at: new Date(),
status: ECustomerStatus.VALIDATED,
};
export const customer2_mock: Customer = {
uid: "yregrgetergrt",
contact: contact2,
created_at: new Date(),
updated_at: new Date(),
status: ECustomerStatus.VALIDATED,
};
export const folder: OfficeFolder = {
uid: "mkovrijvrezviev",
folder_number: "12331",
name: "Mon dossier",
status: EFolderStatus.ARCHIVED,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
};
export const document: Document = {
uid: "fzeafergreztyzgrf",
depositor: customer2_mock,
document_status: "ASKED",
folder: folder,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const fileMock: File = {
uid: "super_file_uid_1",
created_at: new Date(),
updated_at: new Date(),
document: document,
file_name: "file_1",
file_path:
"https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/Qmf_Yb_Eh_X9st_F_Srq_Ve_Bj_Yb_Aj56xv_AV_Nj6_Wjypo_B4r5ubce_U_ae3303e7ab.pdf",
archived_at: null,
mimetype: "image/png",
size: 0,
};
export const fileMock2: File = {
uid: "super_file_uid_2",
created_at: new Date(),
updated_at: new Date(),
document: document,
file_name: "file_2",
file_path:
"https://minteed-prod-euwest3-s3.s3.eu-west-3.amazonaws.com/Qm_Wq_En1_DCA_8yt_RX_Qx_QFA_9_Fm_ZKZH_Qqb_VH_1_Q_Mnv_G_Jtt1_FS_Xp_2a35a36e19",
archived_at: null,
mimetype: "image/png",
size: 0,
};
export const identityFile: File = {
uid: "identity_file_uid",
created_at: new Date(),
updated_at: new Date(),
document: document,
file_name: "file_3",
file_path: "https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/cni_fake_c7259d4923.png",
archived_at: null,
mimetype: "image/png",
size: 0,
};
export const documentIdentity: Document = {
uid: "ethrthbkjtrbporjbh",
depositor: customer2_mock,
document_status: EDocumentStatus.DEPOSITED,
folder: folder,
document_type: identityDocType,
updated_at: new Date(),
created_at: new Date(),
files: [identityFile],
};
export const documentPending: Document = {
uid: "fzefeazdagrtetrury",
depositor: customer2_mock,
document_status: EDocumentStatus.DEPOSITED,
folder: folder,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
files: [fileMock, fileMock2],
};
export const documentDeposited: Document = {
uid: "uè§u§htfgrthytrgr",
depositor: customer2_mock,
document_status: "VALIDATED",
folder: folder,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
files: [fileMock],
};
export const customer2: Customer = {
uid: "yregrgetergrt",
contact: contact2,
created_at: new Date(),
updated_at: new Date(),
status: ECustomerStatus.VALIDATED,
documents: [document, documentPending, documentDeposited, documentIdentity],
};
export const folderWithPendingDocument: OfficeFolder = {
uid: "ferzferzfezeefzdd",
folder_number: "00001",
name: "Mon dossier",
status: EFolderStatus.LIVE,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [],
};
export const folderWithPendingDocument1: OfficeFolder = {
uid: "gtrtyutyhretgytu",
folder_number: "00002",
name: "Mon dossier",
status: EFolderStatus.LIVE,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [documentDeposited],
};
export const folderWithPendingDocument2: OfficeFolder = {
uid: "adzefzefsfrefzrtgtr",
folder_number: "00003",
name: "Mon dossier",
status: EFolderStatus.LIVE,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [document],
};
export const officeFolderHasCustomer1: OfficeFolderHasCustomer = {
uid: "ferzfergrzeyerezrz",
customer: customer,
office_folder: folderWithPendingDocument,
created_at: new Date(),
updated_at: new Date(),
};
export const officeFolderHasCustomer2: OfficeFolderHasCustomer = {
uid: "tezrfzdfgrggeerry",
customer: customer2,
office_folder: folderWithPendingDocument,
created_at: new Date(),
updated_at: new Date(),
};
export const folderWithPendingDocument3: OfficeFolder = {
uid: "mkovrijvrezviev",
folder_number: "00014",
name: "Mon dossier",
status: EFolderStatus.LIVE,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [document, documentDeposited, documentPending, documentIdentity],
office_folder_has_customers: [officeFolderHasCustomer1, officeFolderHasCustomer2],
};
export const document8: Document = {
uid: "eztreggrgbyunjukhg",
depositor: customer,
document_status: "ASKED",
folder: folderWithPendingDocument,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const folderWithPendingDocumentArchived1: OfficeFolder = {
uid: "gtrtyutyhrdazafad&éfytu",
folder_number: "00007",
name: "Mon dossier",
status: EFolderStatus.ARCHIVED,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [documentDeposited],
office_folder_has_customers: [officeFolderHasCustomer1, officeFolderHasCustomer2],
};
export const folderWithPendingDocumentArchived2: OfficeFolder = {
uid: "adzefdazdaazzrtgtr",
folder_number: "00008",
name: "Mon dossier",
status: EFolderStatus.ARCHIVED,
deed: deed,
office: office,
created_at: new Date(),
updated_at: new Date(),
description: "Description",
archived_description: "Archived description",
documents: [document],
};
export const document2: Document = {
uid: "mejfihruehfoire",
depositor: customer,
document_status: "ASKED",
folder: folderWithPendingDocument3,
document_type: docType,
updated_at: new Date(),
created_at: new Date(),
};
export const folders: OfficeFolder[] = [
folderWithPendingDocument,
folderWithPendingDocument1,
folderWithPendingDocument2,
folderWithPendingDocument3,
];
export const foldersArchived: OfficeFolder[] = [folderWithPendingDocumentArchived1, folderWithPendingDocumentArchived2];

View File

@ -1,319 +0,0 @@
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import DocumentNotary from "@Front/Components/DesignSystem/Document/DocumentNotary";
import FilePreview from "@Front/Components/DesignSystem/FilePreview";
import FolderContainer from "@Front/Components/DesignSystem/FolderContainer";
import FolderList from "@Front/Components/DesignSystem/FolderListContainer";
import HeaderLink from "@Front/Components/DesignSystem/Header/HeaderLink";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import ToolTip from "@Front/Components/DesignSystem/ToolTip";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import UserFolder from "@Front/Components/DesignSystem/UserFolder";
import BasePage from "@Front/Components/Layouts/Base";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Toasts, { IToast } from "@Front/Stores/Toasts";
import classes from "./classes.module.scss";
import { customer2, document, documentDeposited, documentPending, folder, folders, folderWithPendingDocument } from "./dummyData";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IState = {
isModalDisplayed: boolean;
selectedOption?: IOption;
};
type IProps = {};
export default class DesignSystem extends BasePage<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
isModalDisplayed: false,
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.onSelectedOption = this.onSelectedOption.bind(this);
}
public override render(): JSX.Element {
const selectOptions: IOption[] = [
{ value: "1", label: "Divorce" },
{ value: "2", label: "Succession" },
{ value: "3", label: "Vente immobilière" },
];
return (
<DefaultTemplate title={"HomePage"}>
<div className={classes["root"]}>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H1}>Website design System</Typography>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_18}>
This page allows to gather all the design system of the site. A Design System is a library of components,
visuals and principles with reusable code. This evolving kit offers a UX and UI repository for designers and
developers of digital products and services. The construction of a design system offers many advantages.
This solution facilitates the work of the teams and reduces the "design debt" and the "technical debt". The
result is a coherent ecosystem and therefore a better experience for users and customers.
</Typography>
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Button components</Typography>
</div>
<Button variant={EButtonVariant.PRIMARY}>Primary</Button>
<Button variant={EButtonVariant.SECONDARY}>Secondary</Button>
<Button variant={EButtonVariant.GHOST}>Ghost</Button>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Toaster component</Typography>
</div>
<Button variant={EButtonVariant.PRIMARY} onClick={this.spawnToast}>
Spawn a toast
</Button>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Modal components</Typography>
</div>
<Button variant={EButtonVariant.PRIMARY} onClick={this.openModal}>
Show Modal
</Button>
<Confirm
isOpen={this.state.isModalDisplayed}
onClose={this.closeModal}
closeBtn
header={"Title"}
cancelText={"Cancel"}
confirmText={"Confirmer"}>
Lorem ipsum dolor sit amet consectetur. Aliquam nunc lobortis lacus vulputate sagittis sed tempor eget feugiat.
Elementum malesuada at sit elit.
</Confirm>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>HeaderLink components</Typography>
</div>
<div className={classes["inline-flex"]}>
<HeaderLink text={"Home"} path={"/"} />
<HeaderLink text={"Design-system"} path={"/design-system"} />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>CheckBox component</Typography>
</div>
<CheckBox
toolTip="Mon super tooltip"
option={{
label: "Check Box 1",
value: "box_1",
}}
/>
<CheckBox
option={{
label: "Check Box 2",
value: "box_2",
}}
/>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>RadioBox component</Typography>
</div>
<RadioBox name="RadioBox" toolTip="Radiobox with tooltip" value="box_1">
Radio Box 1
</RadioBox>
<RadioBox name="RadioBox" value="box_2">
Radio Box 2
</RadioBox>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Tool tip component</Typography>
</div>
<ToolTip title="toolTip" text="tooltip content" isNotFlex={true} />
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Input component</Typography>
</div>
<div className={classes["sub-section"]}>
<TextField name="input field" placeholder="input place hodler" />
</div>
<div className={classes["sub-section"]}>
<TextAreaField name="input field" placeholder="text area place hodler" />
</div>
<div className={classes["sub-section"]}>
<TextField name="input field" placeholder="number place hodler" />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Progress bar component</Typography>
</div>
<div className={classes["sub-section"]}>
<QuantityProgressBar currentNumber={10} total={100} title={"Complétion du dossier client"} />
</div>
<div className={classes["sub-section"]}>
<QuantityProgressBar currentNumber={30} total={100} title={"Complétion du dossier client"} />
</div>
<div className={classes["sub-section"]}>
<QuantityProgressBar currentNumber={70} total={100} title={"Complétion du dossier client"} />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Folder container component</Typography>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Folder with no document to validate</Typography>
<div className={classes["folder-conatainer"]}>
<FolderContainer folder={folder as IDashBoardFolder} />
</div>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Folder with document waiting for being validate</Typography>
<div className={classes["folder-conatainer"]}>
<FolderContainer folder={folderWithPendingDocument as IDashBoardFolder} />
</div>
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Select component</Typography>
</div>
<div className={classes["sub-section"]}>
<div className={classes["folder-conatainer"]}>
<SelectField
name="select"
options={selectOptions}
onChange={this.onSelectedOption}
placeholder={"Type d'acte"}
selectedOption={this.state.selectedOption}
/>
</div>
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Notary Documents</Typography>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Documents ASKED</Typography>
<div className={classes["folder-conatainer"]}>
<DocumentNotary document={document} folderUid="" />
</div>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Documents Depoited</Typography>
<div className={classes["folder-conatainer"]}>
<DocumentNotary document={documentPending} folderUid="" />
</div>
</div>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.P_16}>Documents VALIDATED</Typography>
<div className={classes["folder-conatainer"]}>
<DocumentNotary document={documentDeposited} folderUid="" />
</div>
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Notary Documents</Typography>
</div>
<div className={classes["sub-section"]}>
<UserFolder
customer={customer2}
folder={folder as IDashBoardFolder}
isOpened={true}
onChange={() => {
return;
}}
/>
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>MultiSelect</Typography>
</div>
<div className={classes["sub-section"]}>
<MultiSelect options={selectOptions} placeholder="Numéro CRPCEN" />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Document SearchBar</Typography>
</div>
<div className={classes["sub-section"]}>
<SearchBar folders={folders as IDashBoardFolder[]} />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Folder List</Typography>
</div>
<div className={classes["sub-section"]}>
<FolderList folders={folders as IDashBoardFolder[]} isArchived={false} />
</div>
</div>
<div className={classes["section"]}>
<div className={classes["sub-section"]}>
<Typography typo={ITypo.H3}>Preview Image/Pdf</Typography>
</div>
<div className={classes["sub-section"]}>
<div style={{ height: "500px" }}>
<FilePreview href="https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/Qmf_Yb_Eh_X9st_F_Srq_Ve_Bj_Yb_Aj56xv_AV_Nj6_Wjypo_B4r5ubce_U_ae3303e7ab.pdf" />
</div>
<FilePreview href="https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/Qmf_Vgs_Vr_Mt_TDY_Xme8qhw8quiin_Co_Bo_FBA_Vc9k6_H2d1_Bh_UU_162d84281d.jpeg" />
</div>
</div>
</div>
</DefaultTemplate>
);
}
private openModal() {
this.setState({
isModalDisplayed: true,
});
}
private closeModal() {
this.setState({
isModalDisplayed: false,
});
}
private spawnToast() {
const toast: IToast = {
title: "Un collaborateur veut rejoindre votre office",
text: "12:00 - 1 fev 2023",
};
Toasts.getInstance().open(toast);
}
private onSelectedOption(option: IOption) {
this.setState({
selectedOption: option,
});
}
}

View File

@ -0,0 +1,24 @@
@import "@Themes/constants.scss";
.root {
.header {
margin-top: 24px;
}
.form-container {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 32px;
}
.buttons-container {
display: flex;
gap: 32px;
@media (max-width: $screen-s) {
flex-direction: column-reverse;
gap: 16px;
}
}
}

View File

@ -0,0 +1,78 @@
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module";
import JwtService from "@Front/Services/JwtService/JwtService";
import { validateOrReject, ValidationError } from "class-validator";
import { DocumentType, Office } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router";
import { useCallback, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {};
export default function DocumentTypesCreate(props: IProps) {
const [validationError, setValidationError] = useState<ValidationError[]>([]);
const router = useRouter();
const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try {
const jwt = JwtService.getInstance().decodeJwt();
if (!jwt) return;
const documentToCreate = DocumentType.hydrate<DocumentType>({
...values,
office: Office.hydrate<Office>({
uid: jwt.office_Id,
}),
});
await validateOrReject(documentToCreate, { groups: ["createDocumentType"] });
const documentTypeCreated = await DocumentTypes.getInstance().post(documentToCreate);
router.push(
Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentTypeCreated.uid!),
);
} catch (e) {
if (e instanceof Array) {
setValidationError(e);
}
}
},
[router],
);
return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des types d'actes"} hasBackArrow title="Créer un type d'acte">
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Créer un nouveau document</Typography>
</div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField
name="name"
placeholder="Nom du document"
validationError={validationError.find((error) => error.property === "name")}
/>
<TextAreaField
name="private_description"
placeholder="Description visible par les collaborateurs de l'office"
validationError={validationError.find((error) => error.property === "private_description")}
/>
<TextAreaField
name="public_description"
placeholder="Description visible par les clients de l'office"
validationError={validationError.find((error) => error.property === "public_description")}
/>
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
<Button type="submit">Créer le document</Button>
</div>
</Form>
</div>
</DefaultDocumentTypesDashboard>
);
}

View File

@ -0,0 +1,24 @@
@import "@Themes/constants.scss";
.root {
.header {
margin-top: 24px;
}
.form-container {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 32px;
}
.buttons-container {
display: flex;
gap: 32px;
@media (max-width: $screen-s) {
flex-direction: column-reverse;
gap: 16px;
}
}
}

View File

@ -0,0 +1,76 @@
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import { validateOrReject } from "class-validator";
import { DocumentType } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
export default function DocumentTypesEdit() {
const router = useRouter();
let { documentTypeUid } = router.query;
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null);
useEffect(() => {
async function getDocumentType() {
if (!documentTypeUid) return;
const documentType = await DocumentTypes.getInstance().getByUid(documentTypeUid as string, {
_count: true,
});
setDocumentTypeSelected(documentType);
}
getDocumentType();
}, [documentTypeUid]);
const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try {
const documentToUpdate = DocumentType.hydrate<DocumentType>({
...values,
uid: documentTypeUid as string,
});
await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] });
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
console.log(documentTypeUpdated);
} catch (e) {
console.log(e);
}
},
[documentTypeUid],
);
return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des documents"} hasBackArrow title="Modifier un type de doucment">
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
</div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField name="name" placeholder="Nom du document" defaultValue={documentTypeSelected?.name} />
<TextAreaField
name="private_description"
placeholder="Description visible par les collaborateurs de l'office"
defaultValue={documentTypeSelected?.private_description ?? ""}
/>
<TextAreaField
name="public_description"
placeholder="Description visible par les clients de loffice"
defaultValue={documentTypeSelected?.public_description}
/>
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
<Button type="submit">Enregistrer</Button>
</div>
</Form>
</div>
</DefaultDocumentTypesDashboard>
);
}

View File

@ -0,0 +1,91 @@
@import "@Themes/constants.scss";
.root {
.header {
display: flex;
justify-content: space-between;
align-items: flex-end;
@media (max-width: $screen-l) {
flex-direction: column;
align-items: flex-start;
gap: 24px;
}
}
.subtitle {
margin-top: 32px;
}
.deed-type-container {
margin-top: 32px;
display: flex;
gap: 100px;
justify-content: space-between;
padding: 24px;
background-color: var(--grey-soft);
@media (max-width: $screen-l) {
gap: 80px;
}
@media (max-width: $screen-m) {
flex-direction: column;
gap: 32px;
}
.infos {
display: flex;
gap: 100px;
flex: 1;
@media (max-width: $screen-l) {
flex-direction: column;
gap: 32px;
}
.box {
.box-title {
margin-bottom: 8px;
opacity: 0.4;
}
}
.middle-box {
flex: 1;
}
}
.pencil {
align-self: center;
@media (max-width: $screen-m) {
align-self: flex-start;
}
}
}
.documents-container {
margin-top: 32px;
padding: 32px 16px;
border: 1px solid var(--grey);
.container-title {
}
.documents {
margin-top: 32px;
}
.button-container {
margin-top: 32px;
}
}
.delete-container {
display: flex;
justify-content: center;
margin-top: 32px;
}
}

View File

@ -0,0 +1,90 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import PenICon from "@Assets/Icons/pen.svg";
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module";
import classNames from "classnames";
import { DocumentType } from "le-coffre-resources/dist/Admin";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {};
export default function DocumentTypesInformations(props: IProps) {
const router = useRouter();
let { documentTypeUid } = router.query;
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null);
useEffect(() => {
async function getDocumentType() {
if (!documentTypeUid) return;
const documentType = await DocumentTypes.getInstance().getByUid(documentTypeUid as string);
setDocumentTypeSelected(documentType);
}
getDocumentType();
}, [documentTypeUid]);
const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des types d'actes"}>
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography>
<Button variant={EButtonVariant.LINE}>
Modifier la liste des documents
<Image src={ChevronIcon} alt="Chevron" />
</Button>
</div>
<div className={classes["subtitle"]}>
<Typography typo={ITypo.H3}>{documentTypeSelected?.name}</Typography>
</div>
<div className={classes["deed-type-container"]}>
<div className={classes["infos"]}>
<div className={classes["box"]}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
Nom du type d'acte
</Typography>
<Typography typo={ITypo.P_18}>{documentTypeSelected?.name}</Typography>
</div>
<div className={classNames(classes["middle-box"], classes["box"])}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
Description
</Typography>
</div>
</div>
<div className={classes["pencil"]}>
<Link
href={Module.getInstance()
.get()
.modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentTypeUid as string)}
className={classes["edit-icon-container"]}>
<Image src={PenICon} alt="éditer le type d'acte" />
</Link>
</div>
</div>
<div className={classes["documents-container"]}>
<Form onSubmit={onSubmitHandler}>
<div className={classes["container-title"]}>
<Typography typo={ITypo.P_SB_18}>Documents paramétrés</Typography>
</div>
<div className={classes["button-container"]}>
<Button type="submit">Enregistrer</Button>
</div>
</Form>
</div>
<div className={classes["delete-container"]}>
<Button variant={EButtonVariant.GHOST}>Supprimer</Button>
</div>
</div>
</DefaultDocumentTypesDashboard>
);
}

View File

@ -0,0 +1,17 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.no-role-selected {
width: 100%;
.choose-a-role {
margin-top: 96px;
text-align: center;
}
}
}

View File

@ -0,0 +1,26 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import BasePage from "../Base";
import classes from "./classes.module.scss";
type IProps = {};
type IState = {};
export default class DocumentTypes extends BasePage<IProps, IState> {
public override render(): JSX.Element {
return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des documentss"}>
<div className={classes["root"]}>
<div className={classes["no-role-selected"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
<div className={classes["choose-a-role"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez un document
</Typography>
</div>
</div>
</div>
</DefaultDocumentTypesDashboard>
);
}
}

View File

@ -2,21 +2,21 @@ import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Module from "@Front/Config/Module";
import { TextField } from "@mui/material";
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
import { Customer, OfficeFolder, OfficeFolderHasCustomer } from "le-coffre-resources/dist/Notary";
import { Customer, OfficeFolder } from "le-coffre-resources/dist/Notary";
import Link from "next/link";
import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import { TextField } from "@mui/material";
enum ESelectedOption {
EXISTING_CUSTOMER = "existing_customer",
@ -108,10 +108,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
<TextField name="last_name" placeholder="Nom" />
<TextField name="first_name" placeholder="Prénom" />
<TextField name="email" placeholder="E-mail" />
<TextField
name="cell_phone_number"
placeholder="Numéro de téléphone"
/>
<TextField name="cell_phone_number" placeholder="Numéro de téléphone" />
<div className={classes["button-container"]}>
<Link href={backwardPath} className={classes["cancel-button"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
@ -150,24 +147,20 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
private async getFolderPreSelectedCustomers(folderUid: string): Promise<IOption[] | undefined> {
const query = {
q: {
office_folder_has_customers: {
include: {
customer: {
customers: {
include: {
contact: true,
},
},
},
},
},
};
let preExistingCustomers: IOption[] = [];
try {
const folder = await Folders.getInstance().getByUid(folderUid, query);
preExistingCustomers = folder.office_folder_has_customers!.map((folderHasCustomer) => {
preExistingCustomers = folder.customers!.map((customer) => {
return {
label: folderHasCustomer.customer.contact?.first_name + " " + folderHasCustomer.customer.contact?.last_name,
value: folderHasCustomer.customer.uid,
label: customer.contact?.first_name + " " + customer.contact?.last_name,
value: customer.uid,
};
});
} catch (error) {
@ -209,24 +202,26 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
values["civility"] = ECivility.MALE; // TODO: should maybe be deleted later or added to the form
const allCustomersToLink = this.state.selectedCustomers.concat(this.state.existingCustomers);
let customersToLink: Partial<OfficeFolderHasCustomer>[] = allCustomersToLink.map((customer) => {
let customersToLink: Partial<Customer>[] = allCustomersToLink.map((customer) => {
return {
customer: { uid: customer.value },
};
}) as Partial<OfficeFolderHasCustomer>[];
}) as Partial<Customer>[];
if (this.state.selectedOption === "new_customer") {
const customer: Customer = await Customers.getInstance().post({
contact: values,
});
if (!customer.uid) return;
customersToLink?.push({ customer: { uid: customer.uid } } as Partial<OfficeFolderHasCustomer>);
customersToLink?.push({ customer: { uid: customer.uid } } as Partial<Customer>);
}
if (customersToLink) {
const body = OfficeFolder.hydrate<OfficeFolder>({ office_folder_has_customers: customersToLink.map((customer) => {
return OfficeFolderHasCustomer.hydrate<OfficeFolderHasCustomer>(customer);
}) });
const body = OfficeFolder.hydrate<OfficeFolder>({
customers: customersToLink.map((customer) => {
return Customer.hydrate<Customer>(customer);
}),
});
await Folders.getInstance().put(this.props.selectedFolderUid, body);
this.props.router.push(`/folders/${this.props.selectedFolderUid}`);

View File

@ -169,8 +169,8 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
.map((document) => {
return document.document_type!.uid!;
});
const documentTypes = folder.deed!.deed_has_document_types!.filter((documentType) => {
if (userDocumentTypesUids.includes(documentType.document_type!.uid!)) return false;
const documentTypes = folder.deed!.document_types!.filter((documentType) => {
if (userDocumentTypesUids.includes(documentType!.uid!)) return false;
return true;
});
@ -182,9 +182,9 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
})
.map((documentType) => {
return {
label: documentType.document_type!.name!,
value: documentType.document_type!.uid!,
description: documentType.document_type!.private_description!,
label: documentType!.name!,
value: documentType!.uid!,
description: documentType!.private_description!,
};
});
@ -209,13 +209,11 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
public_description: this.state.visibleDescription,
});
const oldDocumentsType = this.state.folder?.deed?.deed_has_document_types!;
const oldDocumentsType = this.state.folder?.deed?.document_types!;
await Deeds.getInstance().put(this.state.folder?.deed?.uid!, {
deed_has_document_types: [
document_types: [
...oldDocumentsType,
{
document_type: documentType,
},
documentType,
],
});

View File

@ -13,7 +13,7 @@ import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Ty
import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import { ValidationError } from "class-validator";
import { Deed, DeedType, Office, OfficeFolder, OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Notary";
import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
import User from "le-coffre-resources/dist/Notary";
import { NextRouter, useRouter } from "next/router";
import React from "react";
@ -21,6 +21,7 @@ import { ActionMeta, MultiValue } from "react-select";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService";
type IFormValues = {
folder_number: string;
@ -150,7 +151,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
}
public override async componentDidMount() {
const deedTypes = await DeedTypes.getInstance().get({ q: {} });
const deedTypes = await DeedTypes.getInstance().get();
// TODO SETUP userStore and get the user's office membership -> Replace IwJ70M471c by the user's office membership uid
const usersMock = await Users.getInstance().get({ include: { office_membership: true } });
@ -233,23 +234,19 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
[key: string]: any;
},
) {
const officeId = JwtService.getInstance().decodeJwt()?.office_Id;
const selectedDeedTypeUid: DeedType | undefined = this.state.deedTypes.find(
(deedType) => deedType.uid === this.state.formValues.act_typ?.value,
);
/**
* MOCK DATA
*/
const usersMock = await Users.getInstance().get({ include: { office_membership: true } });
const userMock = usersMock[0];
let stakeholders = this.getStakeholders();
let testUsers = stakeholders.map((stakeholder) => ({
user_stakeholder: {
uid: stakeholder.user_stakeholder.uid,
},
}));
let stakeholders = this.state.collaborators;
if (this.state.folder_access === "select_collaborators") {
stakeholders = this.state.collaborators.filter((collaborator) => {
return this.state.formValues.collaborators?.some((selectedCollaborator) => {
return selectedCollaborator.value === collaborator.uid;
});
});
}
const officeFolderForm = OfficeFolder.hydrate<OfficeFolder>({
folder_number: values["folder_number"],
name: values["name"],
@ -260,15 +257,10 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
}),
}),
office: Office.hydrate<Office>({
uid: userMock?.office_membership?.uid,
}),
office_folder_has_stakeholder: testUsers.map((user) => {
return OfficeFolderHasStakeholder.hydrate<OfficeFolderHasStakeholder>({
user_stakeholder: User.hydrate<User>({
uid: user.user_stakeholder.uid,
}),
});
uid: officeId,
}),
customers: [],
stakeholders,
});
try {
@ -285,6 +277,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
if (!newOfficeFolder) return;
this.props.router.push(`/folders/${newOfficeFolder.uid}`);
} catch (backError: any) {
if (!Array.isArray(backError)) return;
this.setState({
validationError: backError as ValidationError[],
});
@ -313,25 +306,6 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
folder_access: e.target.value,
});
}
private getStakeholders() {
let collaborators: User[] = this.state.collaborators;
let office_folder_has_stakeholders = collaborators.map((collaborator) => {
return OfficeFolderHasStakeholder.hydrate<OfficeFolderHasStakeholder>({
user_stakeholder: collaborator,
});
});
if (this.state.folder_access === "select_collaborators") {
office_folder_has_stakeholders = office_folder_has_stakeholders.filter((collaborator) => {
return this.state.formValues.collaborators?.some((selectedCollaborator) => {
return selectedCollaborator.value === collaborator.user_stakeholder.uid;
});
});
}
return office_folder_has_stakeholders;
}
}
export default function CreateFolder(props: IProps): JSX.Element {

View File

@ -60,14 +60,14 @@ export default class ClientSection extends React.Component<IProps, IState> {
}
private renderCustomerFolders() {
const output = this.props.folder.office_folder_has_customers?.map((folderHasCustomer) => {
if (!folderHasCustomer.customer) return null;
const output = this.props.folder.customers?.map((customer) => {
if (!customer) return null;
return (
<UserFolder
folder={this.props.folder}
customer={folderHasCustomer.customer}
key={folderHasCustomer.customer.uid}
isOpened={this.state.openedCustomer === folderHasCustomer.customer.uid}
customer={customer}
key={customer.uid}
isOpened={this.state.openedCustomer === customer.uid}
onChange={this.changeUserFolder}
/>
);
@ -83,7 +83,7 @@ export default class ClientSection extends React.Component<IProps, IState> {
}
private doesFolderHaveCustomer(): boolean {
if (!this.props.folder?.office_folder_has_customers) return false;
return this.props.folder?.office_folder_has_customers!.length > 0;
if (!this.props.folder?.customers) return false;
return this.props.folder?.customers!.length > 0;
}
}

View File

@ -2,6 +2,7 @@ import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
@ -16,7 +17,6 @@ import { ChangeEvent } from "react";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import ClientSection from "./ClientSection";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {};
@ -155,8 +155,8 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
}
private doesFolderHaveCustomer(): boolean {
if (!this.state.selectedFolder?.office_folder_has_customers) return false;
return this.state.selectedFolder?.office_folder_has_customers!.length > 0;
if (!this.state.selectedFolder?.customers) return false;
return this.state.selectedFolder?.customers!.length > 0;
}
private onSelectedFolder(folder: IDashBoardFolder): void {
@ -189,9 +189,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
q: {
deed: { include: { deed_type: true } },
office: true,
office_folder_has_customers: {
include: {
customer: {
customers: {
include: {
contact: true,
documents: {
@ -203,8 +201,6 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
},
},
},
},
},
documents: {
include: {
depositor: {

View File

@ -1,20 +1,20 @@
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import Module from "@Front/Config/Module";
import User from "le-coffre-resources/dist/Notary";
import Link from "next/link";
import { NextRouter, useRouter } from "next/router";
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import Module from "@Front/Config/Module";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import { OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Customer";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import User from "le-coffre-resources/dist/Notary";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
type IPropsClass = {
selectedFolderUid: string;
@ -104,25 +104,21 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
const query = {
q: {
office: true,
office_folder_has_stakeholder: {
include: {
user_stakeholder: {
stakeholders: {
include: {
contact: true,
},
},
},
},
},
};
let folder = null;
try {
folder = await Folders.getInstance().getByUid(folderUid, query);
const preSelectedCollaborators: IOption[] = folder.office_folder_has_stakeholder!.map((collaborator) => {
const preSelectedCollaborators: IOption[] = folder.stakeholders!.map((collaborator) => {
return {
label: collaborator.user_stakeholder.contact?.first_name + " " + collaborator.user_stakeholder.contact?.last_name,
value: collaborator.user_stakeholder.uid,
label: collaborator.contact?.first_name + " " + collaborator.contact?.last_name,
value: collaborator.uid,
};
});
this.setState({ selectedCollaborators: preSelectedCollaborators });
@ -173,17 +169,13 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
private async onFormSubmit(e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) {
try {
let collaboratorsUid: OfficeFolderHasStakeholder[];
let collaboratorsUid: User[] = this.state.availableCollaborators;
if (this.state.selectedOption === ERadioBoxValue.SELECTION) {
collaboratorsUid = this.state.selectedCollaborators.map(
(collaborator) => ({ user_stakeholder: { uid: collaborator.value } } as OfficeFolderHasStakeholder),
);
} else {
collaboratorsUid = this.state.availableCollaborators.map(
(collaborator) => ({ user_stakeholder: { uid: collaborator.uid } } as OfficeFolderHasStakeholder),
collaboratorsUid = this.state.selectedCollaborators.map((collaborator) =>
User.hydrate<User>({ uid: collaborator.value as string }),
);
}
await Folders.getInstance().put(this.props.selectedFolderUid, { office_folder_has_stakeholder: collaboratorsUid });
await Folders.getInstance().put(this.props.selectedFolderUid, { stakeholders: collaboratorsUid });
this.props.router.push(
Module.getInstance()
.get()

View File

@ -12,6 +12,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import { Deed, OfficeFolder } from "le-coffre-resources/dist/Notary";
type IProps = {};
@ -88,7 +89,14 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
},
) {
try {
await Folders.getInstance().put(this.props.folderUid, values);
const newValues = OfficeFolder.hydrate<OfficeFolder>({
...values,
deed: Deed.hydrate<Deed>({
uid: values["deed"],
})
});
await Folders.getInstance().put(this.props.folderUid, newValues);
const url = Module.getInstance()
.get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
@ -103,7 +111,7 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
q: {
deed: { include: { deed_type: true } },
office: true,
office_folder_has_customers: { include: { customer: { include: { contact: true } } } },
customers: { include: { contact: true } },
},
};
const folder = await Folders.getInstance().getByUid(this.props.folderUid, query);

View File

@ -40,14 +40,14 @@ export default class ClientSection extends React.Component<IProps, IState> {
}
private renderCustomerFolders() {
const output = this.props.folder.office_folder_has_customers?.map((folderHasCustomer) => {
if (!folderHasCustomer.customer) return null;
const output = this.props.folder.customers?.map((customer) => {
if (!customer) return null;
return (
<UserFolder
folder={this.props.folder}
customer={folderHasCustomer.customer}
key={folderHasCustomer.customer.uid}
isOpened={this.state.openedCustomer === folderHasCustomer.customer.uid}
customer={customer}
key={customer.uid}
isOpened={this.state.openedCustomer === customer.uid}
onChange={this.changeUserFolder}
/>
);
@ -62,7 +62,7 @@ export default class ClientSection extends React.Component<IProps, IState> {
}
private doesFolderHaveCustomer(): boolean {
if (!this.props.folder?.office_folder_has_customers) return false;
return this.props.folder?.office_folder_has_customers!.length > 0;
if (!this.props.folder?.customers) return false;
return this.props.folder?.customers!.length > 0;
}
}

View File

@ -108,7 +108,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
}
private doesFolderHaveCustomer(): boolean {
return this.state.selectedFolder?.office_folder_has_customers !== undefined;
return this.state.selectedFolder?.customers !== undefined;
}
private onSelectedFolder(folder: IDashBoardFolder): void {
@ -140,7 +140,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
q: {
deed: { include: { deed_type: "true" } },
office: "true",
office_folder_has_customers: { include: { customer: { include: { contact: true } } } },
customers: { include: { contact: true } },
},
};
const folder = await Folders.getInstance().getByUid(this.props.selectedFolderUid, query);

View File

@ -1,13 +1,15 @@
import CoffreIcon from "@Assets/Icons/coffre.svg";
// import { FrontendVariables } from "@Front/Config/VariablesFront";
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import UserStore from "@Front/Stores/UserStore";
import Image from "next/image";
import BasePage from "../Base";
import classes from "./classes.module.scss";
import CoffreIcon from "@Assets/Icons/coffre.svg";
import LandingImage from "./landing-connect.jpeg";
import Image from "next/image";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import { FrontendVariables } from "@Front/Config/VariablesFront";
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
export default class LoginClass extends BasePage {
public override render(): JSX.Element {
@ -30,12 +32,17 @@ export default class LoginClass extends BasePage {
);
}
private redirectUserOnConnection() {
const variables = FrontendVariables.getInstance();
const baseFronturl = variables.BACK_API_PROTOCOL + variables.FRONT_APP_HOST;
const authorizeEndPoint = variables.IDNOT_AUTHORIZE_ENDPOINT;
const clientId = variables.IDNOT_CLIENT_ID;
const url = `${authorizeEndPoint}?client_id=${clientId}&redirect_uri=${baseFronturl}/authorized-client&scope=openid,profile,offline_access&response_type=code`;
window.location.assign(url);
private async redirectUserOnConnection() {
// const variables = FrontendVariables.getInstance();
// const baseFronturl = variables.BACK_API_PROTOCOL + variables.FRONT_APP_HOST;
await UserStore.instance.connect(process.env["NEXT_PUBLIC_ADMIN_ID"] as string);
// await JwtService.getInstance().checkJwt();
// window.location.assign("http://localhost:3000" + "/folders");
// const authorizeEndPoint = variables.IDNOT_AUTHORIZE_ENDPOINT;
// const clientId = variables.IDNOT_CLIENT_ID;
// const url = `${authorizeEndPoint}?client_id=${clientId}&redirect_uri=${baseFronturl}/authorized-client&scope=openid,profile,offline_access&response_type=code`;
// window.location.assign(url);
}
}

View File

@ -0,0 +1,114 @@
@import "@Themes/constants.scss";
.root {
.header {
display: flex;
justify-content: space-between;
align-items: center;
@media (max-width: $screen-s) {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
.round {
width: 16px;
height: 16px;
background-color: var(--green-flash);
border-radius: 100px;
}
}
}
.office-infos {
background-color: var(--grey-soft);
display: flex;
justify-content: space-between;
padding: 24px;
margin-top: 32px;
@media (max-width: $screen-l) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
}
@media (max-width: $screen-s) {
grid-template-columns: repeat(1, 1fr);
}
.office-infos-row {
display: flex;
flex-direction: column;
gap: 12px;
}
}
.office-users {
.users {
.title {
margin-top: 32px;
}
.users-container {
margin-top: 32px;
text-align: center;
display: flex;
flex-direction: column;
gap: 24px;
@media (max-width: $screen-l) {
text-align: left;
display: grid;
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: $screen-s) {
grid-template-columns: repeat(1, 1fr);
}
.first-line,
.user-line-desktop {
display: grid;
grid-template-columns: repeat(5, 1fr);
padding: 24px;
background-color: var(--purple-soft);
@media (max-width: $screen-l) {
display: none;
}
}
.user-line-desktop {
background-color: var(--grey-soft);
}
.user-line-desktop {
&:nth-child(odd) {
background-color: var(--grey-medium);
}
}
.user-line-mobile {
display: none;
@media (max-width: $screen-l) {
display: flex;
flex-direction: column;
gap: 24px;
background-color: var(--grey-soft);
padding: 24px 16px;
}
}
}
}
}
}

View File

@ -0,0 +1,193 @@
import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultOfficeDashboard from "@Front/Components/LayoutTemplates/DefaultOfficeDashboard";
import User, { Office } from "le-coffre-resources/dist/SuperAdmin";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {};
export default function OfficeInformations(props: IProps) {
const router = useRouter();
let { officeUid } = router.query;
const [officeSelected, setOfficeSelected] = useState<Office | null>(null);
useEffect(() => {
async function getOffice() {
if (!officeUid) return;
const office = await Offices.getInstance().getByUid(officeUid as string, {
q: {
address: true,
users: {
include: {
role: true,
office_role: true,
contact: true,
},
},
},
});
if (!office) return;
setOfficeSelected(office);
}
getOffice();
}, [officeUid]);
const renderUser = (user: User) => (
<>
<div key={user.uid} className={classes["user-line-desktop"]}>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography>
</div>
<div key={user.uid} className={classes["user-line-mobile"]}>
<div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}>
Nom
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_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_16} color={ITypoColor.BLACK}>
E-mail pro
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}>
Numéro de téléphone
</Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
</div>
<div className={classes["line"]}>
<Typography typo={ITypo.P_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>
</>
);
return (
<DefaultOfficeDashboard mobileBackText={"Liste des utilisateurs"}>
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Office {officeSelected?.crpcen + " - " + officeSelected?.name}</Typography>
<div className={classes["header-right"]}>
<div className={classes["round"]} />
<Typography typo={ITypo.P_16}>Office active</Typography>
</div>
</div>
<div className={classes["office-infos"]}>
<div className={classes["office-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
CRPCEN
</Typography>
<Typography typo={ITypo.P_18}>{officeSelected?.crpcen}</Typography>
</div>
<div className={classes["office-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Dénomination
</Typography>
<Typography typo={ITypo.P_18}>{officeSelected?.name}</Typography>
</div>
<div className={classes["office-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Addresse
</Typography>
<Typography typo={ITypo.P_18}>
{officeSelected?.address?.address} - {officeSelected?.address?.city} {officeSelected?.address?.zip_code}
</Typography>
</div>
</div>
<div className={classes["office-users"]}>
<div className={classes["users"]}>
<div className={classes["title"]}>
<Typography typo={ITypo.H3}>Administrateurs</Typography>
</div>
<div className={classes["users-container"]}>
<div className={classes["first-line"]}>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Nom
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Prénom
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
E-mail pro
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Numéro de téléphone
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Rôle
</Typography>
</div>
{officeSelected?.users
?.filter((user) => {
if (user.office_role && user.office_role.name === "admin") {
return true;
}
if (!user.office_role && user.role?.name === "admin") {
return true;
}
return false;
})
.map((user) => {
return renderUser(user);
})}
</div>
</div>
<div className={classes["users"]}>
<div className={classes["title"]}>
<Typography typo={ITypo.H3}>Collaborateurs</Typography>
</div>
<div className={classes["users-container"]}>
<div className={classes["first-line"]}>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Nom
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Prénom
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
E-mail pro
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Numéro de téléphone
</Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}>
Rôle
</Typography>
</div>
{officeSelected?.users
?.filter((user) => {
if (user.office_role && user.office_role.name !== "admin") {
return true;
}
if (!user.office_role && user.role?.name !== "admin") {
return true;
}
return false;
})
.map((user) => {
return renderUser(user);
})}
</div>
</div>
</div>
</div>
</DefaultOfficeDashboard>
);
}

View File

@ -0,0 +1,72 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.no-folder-selected {
width: 100%;
.choose-a-folder {
margin-top: 96px;
text-align: center;
}
}
.folder-informations {
width: 100%;
min-height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
flex-grow: 1;
.folder-header {
width: 100%;
.header {
margin-bottom: 32px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
}
.second-box {
margin-top: 24px;
margin-bottom: 32px;
}
.progress-bar {
margin-bottom: 32px;
}
.button-container {
width: 100%;
text-align: center;
:first-child {
margin-right: 12px;
}
> * {
margin: auto;
}
@media (max-width: $screen-m) {
:first-child {
margin-right: 0;
margin-bottom: 12px;
}
> * {
width: 100%;
}
}
}
.modal-title {
margin-bottom: 24px;
}
}
}

View File

@ -0,0 +1,26 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import BasePage from "../Base";
import classes from "./classes.module.scss";
import DefaultOfficeDashboard from "@Front/Components/LayoutTemplates/DefaultOfficeDashboard";
type IProps = {};
type IState = {};
export default class Offices extends BasePage<IProps, IState> {
public override render(): JSX.Element {
return (
<DefaultOfficeDashboard title={"Dossier"} mobileBackText={"Liste des offices"}>
<div className={classes["root"]}>
<div className={classes["no-folder-selected"]}>
<Typography typo={ITypo.H1Bis}>Informations des offices</Typography>
<div className={classes["choose-a-folder"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez une office
</Typography>
</div>
</div>
</div>
</DefaultOfficeDashboard>
);
}
}

View File

@ -0,0 +1,31 @@
@import "@Themes/constants.scss";
.root {
.subtitle {
margin-top: 32px;
}
.rights-container {
margin-top: 32px;
padding: 32px 16px;
border: 1px solid gray;
.select-all-container {
margin-top: 32px;
}
.rights {
margin-top: 32px;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
@media (max-width: $screen-m) {
grid-template-columns: 1fr;
}
}
.save-container {
margin-top: 32px;
}
}
}

View File

@ -0,0 +1,122 @@
import Roles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
import Rules from "@Front/Api/LeCoffreApi/Admin/Rules/Rules";
import Button from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import Form from "@Front/Components/DesignSystem/Form";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultRoleDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
import { Role, Rule } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import classes from "./classes.module.scss";
type IProps = {};
type RuleCheckbox = Rule & {
checked: boolean;
};
export default function RolesInformations(props: IProps) {
const router = useRouter();
let { roleUid } = router.query;
const [roleSelected, setRoleSelected] = useState<Role | null>(null);
const [rulesCheckboxes, setRulesCheckboxes] = useState<RuleCheckbox[]>([]);
const [selectAll, setSelectAll] = useState<boolean>(false);
useEffect(() => {
setSelectAll(false);
async function getUser() {
if (!roleUid) return;
const role = await Roles.getInstance().getByUid(roleUid as string, {
q: {
rules: true,
},
});
const rules = await Rules.getInstance().get({});
if (!role) return;
setRoleSelected(role);
if (!role.rules) return;
const rulesCheckboxes = rules
.map((rule) => {
if (role.rules?.find((r) => r.uid === rule.uid)) {
return { ...rule, checked: true };
}
return { ...rule, checked: false };
})
.sort((ruleA, ruleB) => (ruleA.name < ruleB.name ? 1 : -1))
.sort((rule) => (rule.checked ? -1 : 1));
const selectAll = rulesCheckboxes.every((rule) => rule.checked);
setSelectAll(selectAll);
setRulesCheckboxes(rulesCheckboxes);
}
getUser();
}, [roleUid]);
const handleSelectAllChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setSelectAll(e.target.checked);
const checked = e.target.checked;
rulesCheckboxes.forEach((rule) => (rule.checked = checked));
setRulesCheckboxes([...rulesCheckboxes]);
},
[rulesCheckboxes],
);
const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
if (!roleSelected || !roleSelected.uid) return;
const rules = rulesCheckboxes.filter((rule) => rule.checked)?.map((rule) => Rule.hydrate<Rule>(rule));
const role = await Roles.getInstance().put(roleSelected.uid, {
rules,
});
if (!role) return;
setRoleSelected(role);
if (!role.rules) return;
setRulesCheckboxes(role.rules.map((rule) => ({ ...rule, checked: false })));
},
[roleSelected, rulesCheckboxes],
);
return (
<DefaultRoleDashboard mobileBackText={"Liste des rôles"}>
<div className={classes["root"]}>
<div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Gestion des rôles</Typography>
</div>
<div className={classes["subtitle"]}>
<Typography typo={ITypo.H3}>{roleSelected?.name}</Typography>
</div>
<div className={classes["rights-container"]}>
<div className={classes["rights-header"]}>
<Typography typo={ITypo.P_SB_18}>Modifier les droits</Typography>
</div>
<div className={classes["select-all-container"]}>
<CheckBox
option={{
label: "Tout sélectionner",
value: "all",
}}
toolTip="Tout sélectionner"
onChange={handleSelectAllChange}
checked={selectAll}
/>
</div>
<Form onSubmit={onSubmitHandler}>
<div className={classes["rights"]}>
{rulesCheckboxes.map((rule) => (
<div className={classes["right"]} key={rule.uid}>
<CheckBox option={{ label: rule.name, value: rule.uid }} checked={rule.checked} />
</div>
))}
</div>
<div className={classes["save-container"]}>
<Button type="submit">Enregistrer</Button>
</div>
</Form>
</div>
</div>
</DefaultRoleDashboard>
);
}

View File

@ -0,0 +1,17 @@
@import "@Themes/constants.scss";
.root {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100%;
.no-role-selected {
width: 100%;
.choose-a-role {
margin-top: 96px;
text-align: center;
}
}
}

View File

@ -0,0 +1,26 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultRoleDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
import BasePage from "../Base";
import classes from "./classes.module.scss";
type IProps = {};
type IState = {};
export default class Collaborators extends BasePage<IProps, IState> {
public override render(): JSX.Element {
return (
<DefaultRoleDashboard mobileBackText={"Liste des rôles"}>
<div className={classes["root"]}>
<div className={classes["no-role-selected"]}>
<Typography typo={ITypo.H1Bis}>Gestion des rôles</Typography>
<div className={classes["choose-a-role"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez un rôle
</Typography>
</div>
</div>
</div>
</DefaultRoleDashboard>
);
}
}

View File

@ -0,0 +1,81 @@
@import "@Themes/constants.scss";
.root {
.header {
display: flex;
align-items: flex-end;
gap: 24px;
@media (max-width: $screen-s) {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
}
.user-infos {
background-color: var(--grey-soft);
display: flex;
justify-content: space-between;
padding: 24px;
margin-top: 32px;
@media (max-width: $screen-l) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
}
@media (max-width: $screen-s) {
grid-template-columns: repeat(1, 1fr);
}
.user-infos-row {
display: flex;
flex-direction: column;
gap: 12px;
}
}
.role-container {
padding: 32px 16px;
border: 1px solid var(--grey);
margin-top: 32px;
display: flex;
gap: 32px;
@media (max-width: $screen-m) {
flex-direction: column;
}
.part {
flex: 1;
.first-line {
display: flex;
justify-content: space-between;
}
.second-line {
margin-top: 32px;
display: flex;
gap: 16px;
flex-direction: column;
.votes-block {
align-items: center;
display: flex;
gap: 16px;
background-color: var(--orange-soft);
border: 1px solid #e7e7e7;
border-radius: 5px;
padding: 16px;
}
}
}
.third-line {
margin-top: 32px;
}
}
}

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