Deed types with documents and responsive working

This commit is contained in:
Maxime Lalo 2023-07-17 16:29:30 +02:00
parent 729f0cf50a
commit 44d934f288
5 changed files with 339 additions and 99 deletions

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: IPostDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl);
try {
return await this.postRequest<DocumentType>(url, body as any);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async getByUid(uid: string, q?: any): Promise<DocumentType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<DocumentType>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async put(uid: string, body: IPutDocumentTypesParams): Promise<DocumentType> {
const url = new URL(this.baseURl.concat(`/${uid}`));
try {
return await this.putRequest<DocumentType>(url, body);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,93 @@
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import { Document } from "le-coffre-resources/dist/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

@ -1,8 +1,10 @@
import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes"; import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList"; import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import Button from "@Front/Components/DesignSystem/Button";
import SearchBar from "@Front/Components/DesignSystem/SearchBar"; import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { DeedType } from "le-coffre-resources/dist/Admin"; import { DeedType } from "le-coffre-resources/dist/Admin";
import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
@ -55,6 +57,11 @@ export default function DeedListContainer(props: IProps) {
/> />
</div> </div>
</div> </div>
<div>
<Link href={"/"}>
<Button fullwidth={true}>Créer un type d'acte</Button>
</Link>
</div>
</div> </div>
); );
} }

View File

@ -1,31 +1,85 @@
@import "@Themes/constants.scss"; @import "@Themes/constants.scss";
.root { .root {
.header {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.subtitle { .subtitle {
margin-top: 32px; margin-top: 32px;
} }
.rights-container { .deed-type-container {
margin-top: 32px; margin-top: 32px;
padding: 32px 16px; display: flex;
border: 1px solid gray; gap: 100px;
.select-all-container { justify-content: space-between;
margin-top: 32px; padding: 24px;
background-color: var(--grey-soft);
@media (max-width: $screen-l) {
gap: 80px;
} }
.rights { @media (max-width: $screen-m) {
margin-top: 32px; flex-direction: column;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px; gap: 32px;
}
@media (max-width: $screen-m) { .infos {
grid-template-columns: 1fr; 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;
} }
} }
.save-container { .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; margin-top: 32px;
} }
} }
.delete-container {
display: flex;
justify-content: center;
margin-top: 32px;
}
} }

View File

@ -1,121 +1,121 @@
import Roles from "@Front/Api/LeCoffreApi/Admin/Roles/Roles"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Rules from "@Front/Api/LeCoffreApi/Admin/Rules/Rules"; import PenICon from "@Assets/Icons/pen.svg";
import Button from "@Front/Components/DesignSystem/Button"; import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
import CheckBox from "@Front/Components/DesignSystem/CheckBox"; 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 Form from "@Front/Components/DesignSystem/Form";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; 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 DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
import { Role, Rule } from "le-coffre-resources/dist/Admin"; 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 { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { MultiValue } from "react-select";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
type IProps = {}; type IProps = {};
type RuleCheckbox = Rule & {
checked: boolean;
};
export default function DeedTypesInformations(props: IProps) { export default function DeedTypesInformations(props: IProps) {
const router = useRouter(); const router = useRouter();
let { roleUid } = router.query; let { deedTypeUid } = router.query;
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
const [availableDocuments, setAvailableDocuments] = useState<DocumentType[]>([]);
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
const [roleSelected, setRoleSelected] = useState<Role | null>(null);
const [rulesCheckboxes, setRulesCheckboxes] = useState<RuleCheckbox[]>([]);
const [selectAll, setSelectAll] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
setSelectAll(false); async function getDeedType() {
async function getUser() { if (!deedTypeUid) return;
if (!roleUid) return; const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
const role = await Roles.getInstance().getByUid(roleUid as string, {
q: { q: {
rules: true, document_types: true,
}, },
}); });
setDeedTypeSelected(deedType);
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(); async function getDocuments() {
}, [roleUid]); const documents = await DocumentTypes.getInstance().get({});
setAvailableDocuments(documents);
}
const handleSelectAllChange = useCallback( setSelectedDocuments([]);
(e: React.ChangeEvent<HTMLInputElement>) => { getDocuments();
setSelectAll(e.target.checked); getDeedType();
const checked = e.target.checked; }, [deedTypeUid]);
rulesCheckboxes.forEach((rule) => (rule.checked = checked));
setRulesCheckboxes([...rulesCheckboxes]);
},
[rulesCheckboxes],
);
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
if (!roleSelected || !roleSelected.uid) return; const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
const rules = rulesCheckboxes.filter((rule) => rule.checked)?.map((rule) => Rule.hydrate<Rule>(rule)); setSelectedDocuments(values as IOption[]);
const role = await Roles.getInstance().put(roleSelected.uid, { }, []);
rules,
}); const formattedOptions: IOption[] = availableDocuments.map((document) => {
if (!role) return; return {
setRoleSelected(role); label: document.name,
if (!role.rules) return; value: document.uid,
setRulesCheckboxes(role.rules.map((rule) => ({ ...rule, checked: false }))); };
}, });
[roleSelected, rulesCheckboxes],
);
return ( return (
<DefaultDeedTypesDashboard mobileBackText={"Liste des rôles"}> <DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["header"]}> <div className={classes["header"]}>
<Typography typo={ITypo.H1Bis}>Gestion des rôles</Typography> <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>
<div className={classes["subtitle"]}> <div className={classes["subtitle"]}>
<Typography typo={ITypo.H3}>{roleSelected?.name}</Typography> <Typography typo={ITypo.H3}>{deedTypeSelected?.name}</Typography>
</div> </div>
<div className={classes["rights-container"]}> <div className={classes["deed-type-container"]}>
<div className={classes["rights-header"]}> <div className={classes["infos"]}>
<Typography typo={ITypo.P_SB_18}>Modifier les droits</Typography> <div className={classes["box"]}>
</div> <Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
<div className={classes["select-all-container"]}> Nom du type d'acte
<CheckBox </Typography>
option={{ <Typography typo={ITypo.P_18}>{deedTypeSelected?.name}</Typography>
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>
<div className={classes["save-container"]}> <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={"/"} 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> <Button type="submit">Enregistrer</Button>
</div> </div>
</Form> </Form>
</div> </div>
<div className={classes["delete-container"]}>
<Button variant={EButtonVariant.GHOST}>Supprimer</Button>
</div>
</div> </div>
</DefaultDeedTypesDashboard> </DefaultDeedTypesDashboard>
); );