Merge branch 'staging' into dev

This commit is contained in:
Maxime Lalo 2023-07-31 12:34:45 +02:00
commit af95322a94
26 changed files with 495 additions and 218 deletions

42
package-lock.json generated
View File

@ -22,7 +22,7 @@
"eslint-config-next": "13.2.4", "eslint-config-next": "13.2.4",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.58", "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.66",
"next": "13.2.4", "next": "13.2.4",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"react": "18.2.0", "react": "18.2.0",
@ -328,9 +328,9 @@
} }
}, },
"node_modules/@eslint-community/regexpp": { "node_modules/@eslint-community/regexpp": {
"version": "4.6.1", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.1.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz",
"integrity": "sha512-O7x6dMstWLn2ktjcoiNLDkAGG2EjveHL+Vvc+n0fXumkJYAcSqcVYKtwDU+hDZ0uDUsnUagSYaZrOLAYE8un1A==", "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==",
"engines": { "engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0" "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
} }
@ -441,22 +441,22 @@
} }
}, },
"node_modules/@mui/core-downloads-tracker": { "node_modules/@mui/core-downloads-tracker": {
"version": "5.14.1", "version": "5.14.2",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.1.tgz", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.2.tgz",
"integrity": "sha512-mIa1WmDmNr1LoupV1Rbxt9bTFKMbIn10RHG1bnZ/FJCkAYpuU/D4n+R+ttiycgcZNngU++zyh/OQeJblzbQPzg==", "integrity": "sha512-x+c/MgDL1t/IIy5lDbMlrDouFG5DYZbl3DP4dbbuhlpPFBnE9glYwmJEee/orVHQpOPwLxCAIWQs+2DKSaBVWQ==",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/mui" "url": "https://opencollective.com/mui"
} }
}, },
"node_modules/@mui/material": { "node_modules/@mui/material": {
"version": "5.14.1", "version": "5.14.2",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.1.tgz", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.2.tgz",
"integrity": "sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ==", "integrity": "sha512-TgNR4/YRL11RifsnMWNhITNCkGJYVz20SCvVJBBoU5Y/KhUNSSJxjDpEB8VrnY+sUsV0NigLCkHZJglfsiS3Pw==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.22.6", "@babel/runtime": "^7.22.6",
"@mui/base": "5.0.0-beta.8", "@mui/base": "5.0.0-beta.8",
"@mui/core-downloads-tracker": "^5.14.1", "@mui/core-downloads-tracker": "^5.14.2",
"@mui/system": "^5.14.1", "@mui/system": "^5.14.1",
"@mui/types": "^7.2.4", "@mui/types": "^7.2.4",
"@mui/utils": "^5.14.1", "@mui/utils": "^5.14.1",
@ -2386,9 +2386,9 @@
} }
}, },
"node_modules/execa": { "node_modules/execa": {
"version": "7.1.1", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
"integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
"dependencies": { "dependencies": {
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"get-stream": "^6.0.1", "get-stream": "^6.0.1",
@ -3340,7 +3340,7 @@
} }
}, },
"node_modules/le-coffre-resources": { "node_modules/le-coffre-resources": {
"resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#5c506e8b49401240b8decee77f9b7e34694f490a", "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#e7916d516fe434c93ef13e765d8d63a7ce3c56b2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
@ -3361,9 +3361,9 @@
} }
}, },
"node_modules/libphonenumber-js": { "node_modules/libphonenumber-js": {
"version": "1.10.37", "version": "1.10.38",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.37.tgz", "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.38.tgz",
"integrity": "sha512-Z10PCaOCiAxbUxLyR31DNeeNugSVP6iv/m7UrSKS5JHziEMApJtgku4e9Q69pzzSC9LnQiM09sqsGf2ticZnMw==" "integrity": "sha512-4NjVXVUmpZ9Zsqq6FXa2+MKI+KAI3tOqA0pxXgXGluhpj4ge5didmbWJpMBqGB3AVGv1SnEtKdGTbxjSEG1kCQ=="
}, },
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
@ -4847,9 +4847,9 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.6.0", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
}, },
"node_modules/tsutils": { "node_modules/tsutils": {
"version": "3.21.0", "version": "3.21.0",

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

View File

@ -37,6 +37,46 @@ export default class BurgerModal extends React.Component<IProps, IState> {
text="Collaborateurs" text="Collaborateurs"
routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]} routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
/> />
<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,
Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path,
Module.getInstance().get().modules.pages.DocumentTypes.pages.Create.props.path,
Module.getInstance().get().modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path,
Module.getInstance().get().modules.pages.DocumentTypes.props.path,
]}
/>
<NavigationLink
path={Module.getInstance().get().modules.pages.Roles.props.path}
text="Gestion des rôles"
routesActive={[
Module.getInstance().get().modules.pages.Roles.props.path,
Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.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 path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
<NavigationLink text="CGU" />
<div className={classes["separator"]} /> <div className={classes["separator"]} />
<LogOutButton /> <LogOutButton />
</div> </div>

View File

@ -58,7 +58,6 @@ export default class ProfileModal extends React.Component<IProps, IState> {
Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path, Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path,
]} ]}
/> />
<NavigationLink text="Gestion des noms de domaine" />
<NavigationLink text="CGU" /> <NavigationLink text="CGU" />
<div className={classes["separator"]} /> <div className={classes["separator"]} />
<LogOutButton /> <LogOutButton />

View File

@ -3,6 +3,7 @@
.root { .root {
color: $black; color: $black;
vertical-align: center;
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
&.H1-60 { &.H1-60 {
font-style: normal; font-style: normal;

View File

@ -34,7 +34,7 @@ export default function DocumentTypeListContainer(props: IProps) {
(block: IBlock) => { (block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide(); props.onCloseLeftSide && props.onCloseLeftSide();
console.log("Block selected :", block); console.log("Block selected :", block);
const redirectPath = Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path; const redirectPath = Module.getInstance().get().modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path;
router.push(redirectPath.replace("[uid]", block.id)); router.push(redirectPath.replace("[uid]", block.id));
}, },
[props, router], [props, router],

View File

@ -38,6 +38,7 @@
} }
.second-line { .second-line {
margin-top: 32px; margin-top: 32px;
max-width: 426px;
} }
.third-line { .third-line {
margin-top: 32px; margin-top: 32px;

View File

@ -4,7 +4,7 @@
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-end; align-items: flex-start;
@media (max-width: $screen-l) { @media (max-width: $screen-l) {
flex-direction: column; flex-direction: column;

View File

@ -15,7 +15,7 @@ export default class DeedTypes extends BasePage<IProps, IState> {
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography> <Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography>
<div className={classes["choose-a-role"]}> <div className={classes["choose-a-role"]}>
<Typography typo={ITypo.P_18} color={ITypoColor.GREY}> <Typography typo={ITypo.P_18} color={ITypoColor.GREY}>
Sélectionnez une liste de pièces Sélectionnez un type d'acte
</Typography> </Typography>
</div> </div>
</div> </div>

View File

@ -5,6 +5,7 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import TextField from "@Front/Components/DesignSystem/Form/TextField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard"; import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module";
import { validateOrReject } from "class-validator"; import { validateOrReject } from "class-validator";
import { DocumentType } from "le-coffre-resources/dist/Admin"; import { DocumentType } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -39,12 +40,19 @@ export default function DocumentTypesEdit() {
}); });
await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] }); await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] });
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate); const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
console.log(documentTypeUpdated); router.push(
Module.getInstance()
.get()
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace(
"[uid]",
documentTypeUpdated.uid ?? "",
),
);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
}, },
[documentTypeUid], [documentTypeUid, router],
); );
return ( return (

View File

@ -1,91 +1,39 @@
@import "@Themes/constants.scss"; @import "@Themes/constants.scss";
.root { .root {
.header { .document-infos {
display: flex; display: flex;
align-items: flex-start;
justify-content: space-between; justify-content: space-between;
align-items: flex-end; gap: 24px;
@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; padding: 24px;
margin-top: 32px;
background-color: var(--grey-soft); background-color: var(--grey-soft);
@media (max-width: $screen-l) { .left {
gap: 80px; display: flex;
}
@media (max-width: $screen-m) {
flex-direction: column; flex-direction: column;
gap: 32px; gap: 32px;
} justify-content: space-between;
.infos {
display: flex;
gap: 100px;
flex: 1;
@media (max-width: $screen-l) { @media (max-width: $screen-l) {
flex-direction: column; display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px; gap: 32px;
} }
.box { @media (max-width: $screen-s) {
.box-title { grid-template-columns: repeat(1, 1fr);
margin-bottom: 8px;
opacity: 0.4;
}
} }
.middle-box { .document-infos-row {
flex: 1; display: flex;
flex-direction: column;
gap: 12px;
} }
} }
.pencil { .right {
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

@ -1,89 +1,72 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import PenICon from "@Assets/Icons/pen.svg"; import PenICon from "@Assets/Icons/pen.svg";
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes"; 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 Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard"; import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import classNames from "classnames";
import { DocumentType } from "le-coffre-resources/dist/Admin"; import { DocumentType } from "le-coffre-resources/dist/Admin";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
type IProps = {}; export default function DocumentTypesInformations() {
export default function DocumentTypesInformations(props: IProps) {
const router = useRouter(); const router = useRouter();
let { documentTypeUid } = router.query; let { documentTypeUid } = router.query;
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null); const [documentSelected, setDocumentSelected] = useState<DocumentType | null>(null);
useEffect(() => { useEffect(() => {
async function getDocumentType() { async function getDocument() {
if (!documentTypeUid) return; if (!documentTypeUid) return;
const documentType = await DocumentTypes.getInstance().getByUid(documentTypeUid as string); const document = await DocumentTypes.getInstance().getByUid(documentTypeUid as string, {
setDocumentTypeSelected(documentType); _count: true,
});
if (!document) return;
setDocumentSelected(document);
} }
getDocumentType(); getDocument();
}, [documentTypeUid]); }, [documentTypeUid]);
const onSubmitHandler = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {}, []);
return ( return (
<DefaultDocumentTypesDashboard mobileBackText={"Liste des types d'actes"}> <DefaultDocumentTypesDashboard mobileBackText={"Liste des collaborateurs"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["header"]}> <div className={classes["folder-header"]}>
<Typography typo={ITypo.H1Bis}>Paramétrage des listes de pièces</Typography> <Typography typo={ITypo.H1Bis}>Paramétrage des documents</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["document-infos"]}>
<Typography typo={ITypo.H3}>{documentTypeSelected?.name}</Typography> <div className={classes["left"]}>
</div> <div className={classes["document-infos-row"]}>
<div className={classes["deed-type-container"]}> <Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["infos"]}> Nom du document
<div className={classes["box"]}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}>
Nom du type d'acte
</Typography> </Typography>
<Typography typo={ITypo.P_18}>{documentTypeSelected?.name}</Typography> <Typography typo={ITypo.P_18}>{documentSelected?.name}</Typography>
</div> </div>
<div className={classNames(classes["middle-box"], classes["box"])}> <div className={classes["document-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} className={classes["box-title"]} color={ITypoColor.BLACK}> <Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Description Description visible par les collaborateurs de l'office
</Typography> </Typography>
<Typography typo={ITypo.P_18}>{documentSelected?.private_description}</Typography>
</div>
<div className={classes["document-infos-row"]}>
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
Description visible par les clients de l'office
</Typography>
<Typography typo={ITypo.P_18}>{documentSelected?.public_description}</Typography>
</div> </div>
</div> </div>
<div className={classes["pencil"]}> <div className={classes["right"]}>
<Link <Link
href={Module.getInstance() href={Module.getInstance()
.get() .get()
.modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentTypeUid as string)} .modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentSelected?.uid ?? "")}
className={classes["edit-icon-container"]}> className={classes["edit-icon-container"]}>
<Image src={PenICon} alt="éditer le type d'acte" /> <Image src={PenICon} alt="edit informations" />
</Link> </Link>
</div> </div>
</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> </div>
</DefaultDocumentTypesDashboard> </DefaultDocumentTypesDashboard>
); );

View File

@ -18,7 +18,7 @@ export default function Login() {
const redirectUserOnConnection = useCallback(() => { const redirectUserOnConnection = useCallback(() => {
async function getUser() { async function getUser() {
try { try {
await UserStore.instance.connect(process.env["NEXT_PUBLIC_ADMIN_ID"] as string); await UserStore.instance.connect("jelkvelknvlkn");
await JwtService.getInstance().checkJwt(); await JwtService.getInstance().checkJwt();
router.push(Module.getInstance().get().modules.pages.Folder.props.path); router.push(Module.getInstance().get().modules.pages.Folder.props.path);
} catch (e) { } catch (e) {

View File

@ -47,31 +47,31 @@ export default function OfficeInformations(props: IProps) {
</div> </div>
<div key={user.uid} className={classes["user-line-mobile"]}> <div key={user.uid} className={classes["user-line-mobile"]}>
<div className={classes["line"]}> <div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Nom Nom
</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.last_name}</Typography>
</div> </div>
<div className={classes["line"]}> <div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Prénom Prénom
</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.first_name}</Typography>
</div> </div>
<div className={classes["line"]}> <div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
E-mail pro E-mail
</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.contact?.email}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.email}</Typography>
</div> </div>
<div className={classes["line"]}> <div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Numéro de téléphone Téléphone
</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography> <Typography typo={ITypo.P_16}>{user.contact?.phone_number}</Typography>
</div> </div>
<div className={classes["line"]}> <div className={classes["line"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK}>
Rôle Rôle
</Typography> </Typography>
<Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography> <Typography typo={ITypo.P_16}>{user.office_role ? user.office_role?.name : user.role?.name}</Typography>
@ -118,19 +118,19 @@ export default function OfficeInformations(props: IProps) {
</div> </div>
<div className={classes["users-container"]}> <div className={classes["users-container"]}>
<div className={classes["first-line"]}> <div className={classes["first-line"]}>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Nom Nom
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Prénom Prénom
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
E-mail pro E-mail
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Numéro de téléphone Téléphone
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Rôle Rôle
</Typography> </Typography>
</div> </div>
@ -155,19 +155,19 @@ export default function OfficeInformations(props: IProps) {
</div> </div>
<div className={classes["users-container"]}> <div className={classes["users-container"]}>
<div className={classes["first-line"]}> <div className={classes["first-line"]}>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Nom Nom
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Prénom Prénom
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
E-mail pro E-mail
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Numéro de téléphone Téléphone
</Typography> </Typography>
<Typography typo={ITypo.NAV_HEADER_18} color={ITypoColor.BLACK}> <Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Rôle Rôle
</Typography> </Typography>
</div> </div>

View File

@ -0,0 +1,28 @@
.root {
position: relative;
.background-container {
width: 100%;
height: 100%;
position: absolute;
z-index: -1;
> img {
width: 100%;
object-fit: cover;
}
}
.select-folder-container {
max-width: 530px;
padding: 80px 72px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 48px;
margin: auto;
background-color: white;
.title {
text-align: center;
}
}
}

View File

@ -0,0 +1,61 @@
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import classes from "./classes.module.scss";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BlockList, { IBlock } from "@Front/Components/DesignSystem/BlockList";
import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import { useCallback, useEffect, useState } from "react";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import { useRouter } from "next/router";
import Module from "@Front/Config/Module";
import BackgroundImage from "@Assets/images/background.png";
import Image from "next/image";
export default function SelectFolder() {
const [folders, setFolders] = useState<OfficeFolder[]>([]);
const router = useRouter();
useEffect(() => {
async function getFolders() {
const folders = await Folders.getInstance().get({});
setFolders(folders.slice(0, 3));
}
getFolders();
}, []);
const handleSelectBlock = useCallback(
(block: IBlock) => {
router.push(
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", block.id),
);
},
[router],
);
return (
<DefaultTemplate title="Sélectionner un dossier" hasHeaderLinks={false}>
<div className={classes["root"]}>
<div className={classes["background-container"]}>
<Image src={BackgroundImage} alt="background" />
</div>
<div className={classes["select-folder-container"]}>
<div className={classes["title"]}>
<Typography typo={ITypo.H1}>Vos dossiers</Typography>
</div>
<div className={classes["folders-container"]}>
<BlockList
onSelectedBlock={handleSelectBlock}
blocks={folders.map((folder) => {
return {
id: folder.uid!,
name: folder.name!,
selected: false,
};
})}
/>
</div>
</div>
</div>
</DefaultTemplate>
);
}

View File

@ -79,3 +79,7 @@
} }
} }
} }
.remove-my-vote {
margin-top: 16px;
}

View File

@ -1,13 +1,19 @@
import WarningIcon from "@Assets/images/warning.png"; import WarningIcon from "@Assets/images/warning.png";
import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles"; import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
import Roles from "@Front/Api/LeCoffreApi/Admin/Roles/Roles";
import LiveVotes from "@Front/Api/LeCoffreApi/SuperAdmin/LiveVotes/LiveVotes";
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users"; import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Votes from "@Front/Api/LeCoffreApi/SuperAdmin/Votes/Votes";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField"; import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Switch from "@Front/Components/DesignSystem/Switch"; import Switch from "@Front/Components/DesignSystem/Switch";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultUserDashboard from "@Front/Components/LayoutTemplates/DefaultUserDashboard"; import DefaultUserDashboard from "@Front/Components/LayoutTemplates/DefaultUserDashboard";
import JwtService from "@Front/Services/JwtService/JwtService";
import Toasts from "@Front/Stores/Toasts"; import Toasts from "@Front/Stores/Toasts";
import User from "le-coffre-resources/dist/Notary"; import User, { Appointment, Vote } from "le-coffre-resources/dist/SuperAdmin";
import { EAppointmentStatus, EVote } from "le-coffre-resources/dist/SuperAdmin/Appointment";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
@ -30,6 +36,46 @@ export default function UserInformations(props: IProps) {
const [isAdminChecked, setIsAdminChecked] = useState<boolean>(false); const [isAdminChecked, setIsAdminChecked] = useState<boolean>(false);
const [isAdminModalOpened, setIsAdminModalOpened] = useState<boolean>(false); const [isAdminModalOpened, setIsAdminModalOpened] = useState<boolean>(false);
const [currentAppointment, setCurrentAppointment] = useState<Appointment | null>(null);
/** When page change, get the user of the page */
const getUser = useCallback(async () => {
if (!userUid) return;
const user = await Users.getInstance().getByUid(userUid as string, {
q: {
contact: true,
office_role: true,
office_membership: true,
role: true,
appointment: {
include: {
votes: {
include: {
voter: true,
},
},
},
},
votes: 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);
}, [userUid]);
useEffect(() => {
getUser();
}, [getUser, userUid]);
useEffect(() => {
if (!userSelected) return;
setCurrentAppointment(userSelected?.appointment?.find((appointment) => appointment.status === EAppointmentStatus.OPEN) ?? null);
}, [userSelected]);
/** Functions for the admin modal */ /** Functions for the admin modal */
const openAdminModal = () => { const openAdminModal = () => {
setIsAdminModalOpened(true); setIsAdminModalOpened(true);
@ -49,9 +95,23 @@ export default function UserInformations(props: IProps) {
const handleAdminModalAccepted = useCallback(async () => { const handleAdminModalAccepted = useCallback(async () => {
if (!userSelected) return; if (!userSelected) return;
if (adminModalType === "add") { if (adminModalType === "add") {
// add super admin const adminRole = await Roles.getInstance().getOne({
where: {
name: "admin",
},
});
if (!adminRole) return;
await Users.getInstance().put(
userSelected?.uid as string,
User.hydrate<User>({
uid: userSelected?.uid as string,
office_role: undefined,
role: adminRole,
}),
);
} else { } else {
// remove super admin // retirer rôle admin
} }
setIsAdminModalOpened(false); setIsAdminModalOpened(false);
}, [userSelected, adminModalType]); }, [userSelected, adminModalType]);
@ -74,21 +134,27 @@ export default function UserInformations(props: IProps) {
const handleSuperAdminModalAccepted = useCallback(async () => { const handleSuperAdminModalAccepted = useCallback(async () => {
if (!userSelected) return; if (!userSelected) return;
if (superAdminModalType === "add") {
let vote = Vote.hydrate<Vote>({
appointment: Appointment.hydrate<Appointment>({
uid: currentAppointment?.uid ?? undefined,
targeted_user: User.hydrate<User>({
uid: userSelected.uid,
}),
choice: superAdminModalType === "add" ? EVote.NOMINATE : EVote.DISMISS,
}),
});
const liveVote = await LiveVotes.getInstance().post(vote);
if (liveVote.appointment.votes?.length === 3) {
Toasts.getInstance().open({ Toasts.getInstance().open({
title: "Vote attribué", title: `Le titre de super-administrateur a été attribué à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `,
text: "Vous avez voté pour attribuer le titre de Super Admin à " + userSelected.contact?.first_name,
}); });
// add super admin
} else {
Toasts.getInstance().open({
title: "Vote attribué",
text: "Vous avez voté pour supprimer le titre de Super Admin à " + userSelected.contact?.first_name,
});
// remove super admin
} }
await getUser();
setIsSuperAdminModalOpened(false); setIsSuperAdminModalOpened(false);
}, [userSelected, superAdminModalType]); }, [userSelected, currentAppointment, superAdminModalType, getUser]);
/** Reset switch state when userSelect change */ /** Reset switch state when userSelect change */
useEffect(() => { useEffect(() => {
@ -97,28 +163,20 @@ export default function UserInformations(props: IProps) {
setIsAdminChecked(userSelected.role?.name === "admin" && !userSelected.office_role); setIsAdminChecked(userSelected.role?.name === "admin" && !userSelected.office_role);
}, [userSelected]); }, [userSelected]);
/** When page change, get the user of the page */ const userHasVoted = useCallback(() => {
useEffect(() => { if (!currentAppointment) return false;
async function getUser() { const user = JwtService.getInstance().decodeJwt();
if (!userUid) return; return currentAppointment.votes?.find((vote) => vote.voter?.uid === user?.userId) !== undefined;
const user = await Users.getInstance().getByUid(userUid as string, { }, [currentAppointment]);
q: {
contact: true,
office_role: true,
office_membership: true,
role: true,
},
});
if (!user) return;
const roles = await OfficeRoles.getInstance().get(); const deleteMyVote = useCallback(async () => {
if (!roles) return; if (!currentAppointment) return;
setAvailableRoles(roles.map((role) => ({ value: role.uid, label: role.name }))); const user = JwtService.getInstance().decodeJwt();
setUserSelected(user); const vote = currentAppointment.votes?.find((vote) => vote.voter?.uid === user?.userId);
} if (!vote) return;
await Votes.getInstance().delete({ uid: vote.uid });
getUser(); await getUser();
}, [userUid]); }, [currentAppointment, getUser]);
return ( return (
<DefaultUserDashboard mobileBackText={"Liste des utilisateurs"}> <DefaultUserDashboard mobileBackText={"Liste des utilisateurs"}>
@ -180,22 +238,36 @@ export default function UserInformations(props: IProps) {
<div className={classes["second-line"]}> <div className={classes["second-line"]}>
<Switch label="Admin de son office" checked={isAdminChecked} onChange={handleAdminChanged} /> <Switch label="Admin de son office" checked={isAdminChecked} onChange={handleAdminChanged} />
<Switch label="Super-admin LeCoffre.io" checked={isSuperAdminChecked} onChange={handleSuperAdminChanged} /> <Switch label="Super-admin LeCoffre.io" checked={isSuperAdminChecked} onChange={handleSuperAdminChanged} />
<div className={classes["votes-block"]}> {currentAppointment && (
<div className={classes["left"]}> <div className={classes["votes-block"]}>
<Image src={WarningIcon} alt="warning" width={28} height={28} /> <div className={classes["left"]}>
</div> <Image src={WarningIcon} alt="warning" width={28} height={28} />
<div className={classes["right"]}>
<div>
<Typography typo={ITypo.P_SB_18}>1/3</Typography>
</div> </div>
<div> <div className={classes["right"]}>
<Typography typo={ITypo.CAPTION_14}> <div>
Vous avez voté pour attribuer le titre de Super Admin. Il manque 2 votes pour que le <Typography typo={ITypo.P_SB_18}>{currentAppointment.votes?.length}/3</Typography>
collaborateur se voit attribuer le titre. </div>
</Typography> <div>
<Typography typo={ITypo.CAPTION_14}>
{currentAppointment.choice === EVote.NOMINATE
? `Un ou des collaborateurs souhaitent attribuer le titre de Super Admin à ce collaborateur. Il manque ${
3 - currentAppointment.votes?.length!
} vote(s) pour que le collaborateur se voit attribuer le titre.`
: `Un ou des collaborateurs souhaitent retirer le titre de Super Admin à ce collaborateur. Il manque ${
3 - currentAppointment.votes?.length!
} vote(s) pour que le collaborateur se voit retirer le titre.`}
</Typography>
</div>
{userHasVoted() && (
<div className={classes["remove-my-vote"]}>
<Button variant={EButtonVariant.SECONDARY} onClick={deleteMyVote}>
Retirer mon vote
</Button>
</div>
)}
</div> </div>
</div> </div>
</div> )}
</div> </div>
</div> </div>
</div> </div>
@ -221,9 +293,15 @@ export default function UserInformations(props: IProps) {
onClose={closeAdminModal} onClose={closeAdminModal}
onAccept={handleAdminModalAccepted} onAccept={handleAdminModalAccepted}
closeBtn closeBtn
header={`Souhaitez-vous ${adminModalType === "add" ? "ajouter" : "retirer"} ${ header={
userSelected?.contact?.first_name + " " + userSelected?.contact?.last_name adminModalType === "add"
} aux administrateurs de son office ?`} ? `Souhaitez-vous nommer ${
userSelected?.contact?.first_name + " " + userSelected?.contact?.last_name
} administrateur de son office ?`
: `Souhaitez-vous retirer le rôle administrateur de son office à ${
userSelected?.contact?.first_name + " " + userSelected?.contact?.last_name
} ?`
}
confirmText={adminModalType === "add" ? "Ajouter" : "Retirer"} confirmText={adminModalType === "add" ? "Ajouter" : "Retirer"}
cancelText={"Annuler"}> cancelText={"Annuler"}>
<div className={classes["modal-content"]}></div> <div className={classes["modal-content"]}></div>

View File

@ -109,6 +109,13 @@
} }
} }
} }
},
"Select": {
"enabled": true,
"props": {
"path": "/folders/select",
"labelKey": "select_folder"
}
} }
} }
}, },

View File

@ -109,6 +109,13 @@
} }
} }
} }
},
"Select": {
"enabled": true,
"props": {
"path": "/folders/select",
"labelKey": "select_folder"
}
} }
} }
}, },

View File

@ -109,6 +109,13 @@
} }
} }
} }
},
"Select": {
"enabled": true,
"props": {
"path": "/folders/select",
"labelKey": "select_folder"
}
} }
} }
}, },

View File

@ -109,6 +109,13 @@
} }
} }
} }
},
"Select": {
"enabled": true,
"props": {
"path": "/folders/select",
"labelKey": "select_folder"
}
} }
} }
}, },

View File

@ -0,0 +1,5 @@
import SelectFolder from "@Front/Components/Layouts/SelectFolder";
export default function Route() {
return <SelectFolder />;
}