Merge branch 'dev' of github.com:smart-chain-fr/leCoffre-front into dev
This commit is contained in:
commit
d20c13b5e2
@ -1,18 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.content {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
gap: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import classNames from "classnames";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import { IAppRule } from "@Front/Api/Entities/rule";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
import { IHeaderLinkProps } from "../../../ButtonHeader";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import HeaderSubmenuLink from "../../../HeaderSubmenu/HeaderSubmenuLink";
|
||||
import useToggle from "@Front/Hooks/useToggle";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
type IProps = {
|
||||
text: string | JSX.Element;
|
||||
links: (IHeaderLinkProps & {
|
||||
rules?: IAppRule[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export default function HeaderSubmenu(props: IProps) {
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
const [isActive, setIsActive] = useState(true);
|
||||
const { active: isSubmenuOpened, toggle } = useToggle();
|
||||
|
||||
useEffect(() => {
|
||||
setIsActive(false);
|
||||
if (props.links.some((link) => link.path === pathname)) setIsActive(true);
|
||||
if (props.links.some((link) => link.routesActive?.some((routeActive) => pathname.includes(routeActive)))) setIsActive(true);
|
||||
}, [isActive, pathname, props.links]);
|
||||
|
||||
return (
|
||||
<Rules mode={RulesMode.OPTIONAL} rules={props.links.flatMap((link) => link.rules ?? [])}>
|
||||
<div className={classes["container"]}>
|
||||
<div className={classNames(classes["root"], (isActive || isSubmenuOpened) && classes["active"])}>
|
||||
<div className={classes["content"]} onClick={toggle}>
|
||||
<Typography
|
||||
typo={isActive || isSubmenuOpened ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
|
||||
color={isActive || isSubmenuOpened ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
|
||||
{props.text}
|
||||
</Typography>
|
||||
{isSubmenuOpened ? <ChevronUpIcon height="20" width="20" /> : <ChevronDownIcon height="20" width="20" />}
|
||||
</div>
|
||||
<div className={classes["underline"]} data-active={(isActive || isSubmenuOpened).toString()} />
|
||||
{isSubmenuOpened && (
|
||||
<div className={classes["sub-menu"]}>
|
||||
{props.links.map((link) => (
|
||||
<Rules mode={RulesMode.NECESSARY} rules={link.rules ?? []} key={link.path}>
|
||||
<HeaderSubmenuLink {...link} />
|
||||
</Rules>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Rules>
|
||||
);
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
position: absolute;
|
||||
top: var(--header-height);
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
max-height: calc(100vh - var(--header-height));
|
||||
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-generic-white);
|
||||
box-shadow: $shadow-nav;
|
||||
padding: 24px;
|
||||
position: absolute;
|
||||
top: 83px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
max-height: calc(100vh - var(--header-height));
|
||||
|
||||
overflow: auto;
|
||||
> *:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
border-radius: var(--menu-radius, 0px);
|
||||
background: var(--color-generic-white, #FFF);
|
||||
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.10);
|
||||
|
||||
.separator {
|
||||
width: 100%;
|
||||
|
@ -1,170 +1,196 @@
|
||||
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||
import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
|
||||
import MenuItem from "@Front/Components/DesignSystem/Menu/MenuItem";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
import Module from "@Front/Config/Module";
|
||||
import React from "react";
|
||||
|
||||
import NavigationLink from "../../NavigationLink";
|
||||
import classes from "./classes.module.scss";
|
||||
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||
import BurgerModalSubmenu from "./BurgerModalSubmenu";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
|
||||
type IProps = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
};
|
||||
type IState = {};
|
||||
|
||||
export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
// TODO isEnabled depending on role given by DB
|
||||
public override render(): JSX.Element | null {
|
||||
if (!this.props.isOpen) return null;
|
||||
return (
|
||||
<>
|
||||
<div className={classes["background"]} onClick={this.props.closeModal} />
|
||||
<div className={classes["root"]}>
|
||||
<Rules
|
||||
mode={RulesMode.OPTIONAL}
|
||||
rules={[
|
||||
{
|
||||
action: AppRuleActions.read,
|
||||
name: AppRuleNames.officeFolders,
|
||||
},
|
||||
]}>
|
||||
<>
|
||||
<NavigationLink
|
||||
path={Module.getInstance().get().modules.pages.Folder.props.path}
|
||||
text="Dossiers en cours"
|
||||
routesActive={[
|
||||
export default function BurgerModal(props: IProps) {
|
||||
const { isOpen, closeModal } = props;
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes["background"]} onClick={closeModal} />
|
||||
<div className={classes["root"]}>
|
||||
<Rules
|
||||
mode={RulesMode.OPTIONAL}
|
||||
rules={[
|
||||
{
|
||||
action: AppRuleActions.read,
|
||||
name: AppRuleNames.officeFolders,
|
||||
},
|
||||
]}>
|
||||
<>
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Dossiers en cours",
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path,
|
||||
Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path,
|
||||
]}
|
||||
/>
|
||||
<NavigationLink
|
||||
path={Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path}
|
||||
text="Dossiers archivés"
|
||||
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
|
||||
/>
|
||||
<div className={classes["separator"]} />
|
||||
</>
|
||||
</Rules>
|
||||
],
|
||||
link: Module.getInstance().get().modules.pages.Folder.props.path,
|
||||
}}
|
||||
/>
|
||||
|
||||
<BurgerModalSubmenu
|
||||
text={"Espace super admin"}
|
||||
links={[
|
||||
{
|
||||
text: "Gestion des utilisateurs",
|
||||
path: Module.getInstance().get().modules.pages.Users.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Users.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.offices,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Gestion des offices",
|
||||
path: Module.getInstance().get().modules.pages.Offices.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Offices.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.offices,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<BurgerModalSubmenu
|
||||
text="Espace office"
|
||||
links={[
|
||||
{
|
||||
text: "Collaborateurs",
|
||||
path: Module.getInstance().get().modules.pages.Collaborators.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Collaborators.pages.CollaboratorInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Collaborators.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.users,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Gestion des rôles",
|
||||
path: Module.getInstance().get().modules.pages.Roles.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Roles.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Roles.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.officeRoles,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Paramétrage des listes de pièces",
|
||||
path: Module.getInstance().get().modules.pages.DeedTypes.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.DeedTypes.pages.Edit.props.path,
|
||||
Module.getInstance().get().modules.pages.DeedTypes.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.deedTypes,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "RIB Office",
|
||||
path: Module.getInstance().get().modules.pages.OfficesRib.props.path,
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.rib,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Abonnement",
|
||||
path: Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Error.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Success.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Invite.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.ManageCollaborators.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.New.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.props.path,
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className={classes["separator"]} />
|
||||
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
|
||||
<NavigationLink target="blank" path="https://ressources.lecoffre.io/" text="Guide de Prise en Main" />
|
||||
<NavigationLink target="blank" path="https://tally.so/r/mBGaNY" text="Support" />
|
||||
<NavigationLink target="blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
|
||||
<LogOutButton />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Dossiers archivés",
|
||||
routesActive: [Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path],
|
||||
link: Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path,
|
||||
hasSeparator: true,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Rules>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Espace super admin",
|
||||
dropdown: {
|
||||
items: [
|
||||
{
|
||||
text: "Gestion des utilisateurs",
|
||||
link: Module.getInstance().get().modules.pages.Users.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Users.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.offices,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Espace office",
|
||||
hasSeparator: true,
|
||||
dropdown: {
|
||||
items: [
|
||||
{
|
||||
text: "Collaborateurs",
|
||||
link: Module.getInstance().get().modules.pages.Collaborators.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Collaborators.pages.CollaboratorInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Collaborators.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.users,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Gestion des rôles",
|
||||
link: Module.getInstance().get().modules.pages.Roles.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Roles.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path,
|
||||
Module.getInstance().get().modules.pages.Roles.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.officeRoles,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Paramétrage des listes de pièces",
|
||||
link: Module.getInstance().get().modules.pages.DeedTypes.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.DeedTypes.pages.Edit.props.path,
|
||||
Module.getInstance().get().modules.pages.DeedTypes.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.Create.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.Edit.props.path,
|
||||
Module.getInstance().get().modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.deedTypes,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "RIB Office",
|
||||
link: Module.getInstance().get().modules.pages.OfficesRib.props.path,
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
name: AppRuleNames.rib,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Abonnement",
|
||||
link: Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
|
||||
routesActive: [
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Error.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Success.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Invite.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.ManageCollaborators.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.New.props.path,
|
||||
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.props.path,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Mon compte",
|
||||
link: Module.getInstance().get().modules.pages.MyAccount.props.path,
|
||||
hasSeparator: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Guide de Prise en Main",
|
||||
link: "https://ressources.lecoffre.io/",
|
||||
target: "_blank",
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Support",
|
||||
link: "https://tally.so/r/mBGaNY",
|
||||
target: "_blank",
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "CGU",
|
||||
link: "/CGU_LeCoffre_io.pdf",
|
||||
hasSeparator: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
<LogOutButton />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import useHoverable from "@Front/Hooks/useHoverable";
|
||||
|
||||
type IHeaderLinkProps = {
|
||||
text: string | JSX.Element;
|
||||
path: string;
|
||||
routesActive?: string[];
|
||||
};
|
||||
|
||||
export default function HeaderSubmenuLink(props: IHeaderLinkProps) {
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
const [isActive, setIsActive] = React.useState(props.path === pathname);
|
||||
const { handleMouseLeave, handleMouseEnter, isHovered } = useHoverable();
|
||||
|
||||
useEffect(() => {
|
||||
if (props.path === pathname) setIsActive(true);
|
||||
if (props.routesActive) {
|
||||
for (const routeActive of props.routesActive) {
|
||||
if (isActive) break;
|
||||
if (pathname.includes(routeActive)) setIsActive(true);
|
||||
}
|
||||
}
|
||||
}, [isActive, pathname, props.path, props.routesActive]);
|
||||
|
||||
return (
|
||||
<Link href={props.path} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||
<Typography
|
||||
typo={isActive || isHovered ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
|
||||
color={isActive || isHovered ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
|
||||
{props.text}
|
||||
</Typography>
|
||||
</Link>
|
||||
);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
height: 83px;
|
||||
padding: 10px 16px;
|
||||
.content {
|
||||
margin: auto;
|
||||
}
|
||||
.underline {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: var(--color-generic-white);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
&[data-active="true"] {
|
||||
background-color: var(--color-generic-black);
|
||||
}
|
||||
}
|
||||
|
||||
&.desactivated {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
box-shadow: 0px 8px 10px 0px #00000012;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
gap: 24px;
|
||||
left: 0;
|
||||
transform: translateX(-25%);
|
||||
width: 300px;
|
||||
top: 84px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import classNames from "classnames";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { IHeaderLinkProps } from "../ButtonHeader";
|
||||
|
||||
import Typography, { ETypo, ETypoColor } from "../../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import useHoverable from "@Front/Hooks/useHoverable";
|
||||
import HeaderSubmenuLink from "./HeaderSubmenuLink";
|
||||
import { IAppRule } from "@Front/Api/Entities/rule";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
|
||||
type IProps = {
|
||||
text: string | JSX.Element;
|
||||
links: (IHeaderLinkProps & {
|
||||
rules?: IAppRule[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export default function HeaderSubmenu(props: IProps) {
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const { handleMouseLeave, handleMouseEnter, isHovered } = useHoverable(100);
|
||||
|
||||
useEffect(() => {
|
||||
setIsActive(false);
|
||||
if (props.links.some((link) => link.path === pathname)) setIsActive(true);
|
||||
if (props.links.some((link) => link.routesActive?.some((routeActive) => pathname.includes(routeActive)))) setIsActive(true);
|
||||
}, [isActive, pathname, props.links]);
|
||||
|
||||
return (
|
||||
<Rules mode={RulesMode.OPTIONAL} rules={props.links.flatMap((link) => link.rules ?? [])}>
|
||||
<div className={classes["container"]} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||
<div className={classNames(classes["root"], (isActive || isHovered) && classes["active"])}>
|
||||
<div className={classes["content"]}>
|
||||
<Typography
|
||||
typo={isActive || isHovered ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
|
||||
color={isActive || isHovered ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
|
||||
{props.text}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes["underline"]} data-active={(isActive || isHovered).toString()} />
|
||||
{isHovered && (
|
||||
<div className={classes["sub-menu"]}>
|
||||
{props.links.map((link) => (
|
||||
<Rules mode={RulesMode.NECESSARY} rules={link.rules ?? []} key={link.path}>
|
||||
<HeaderSubmenuLink {...link} />
|
||||
</Rules>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Rules>
|
||||
);
|
||||
}
|
@ -8,7 +8,8 @@ import { AdjustmentsVerticalIcon, BanknotesIcon, Square3Stack3DIcon, TagIcon, Us
|
||||
import { usePathname } from "next/navigation";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
|
||||
import Menu, { IItem } from "../../Menu";
|
||||
import Menu from "../../Menu";
|
||||
import { IItem } from "../../Menu/MenuItem";
|
||||
import ButtonHeader from "../ButtonHeader";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
|
||||
.content {
|
||||
align-content: center;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import Link from "next/link";
|
||||
import classNames from "classnames";
|
||||
import { useRouter } from "next/router";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
|
||||
type IProps = {
|
||||
text: string | JSX.Element;
|
||||
path?: string;
|
||||
onClick?: () => void;
|
||||
isEnabled?: boolean;
|
||||
isActive?: boolean;
|
||||
routesActive?: string[];
|
||||
target?: "blank" | "self" | "_blank";
|
||||
};
|
||||
|
||||
type IPropsClass = IProps;
|
||||
type IStateClass = {};
|
||||
|
||||
class NavigationLinkClass extends React.Component<IPropsClass, IStateClass> {
|
||||
static defaultProps = { isEnabled: true };
|
||||
public override render(): JSX.Element | null {
|
||||
if (!this.props.isEnabled) return null;
|
||||
return (
|
||||
<Link
|
||||
href={this.props.path ?? ""}
|
||||
className={classNames(classes["root"], this.props.isActive && [classes["active"]])}
|
||||
onClick={this.props.onClick}
|
||||
target={this.props.target}>
|
||||
<div className={classes["content"]}>
|
||||
<Typography
|
||||
typo={this.props.isActive ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
|
||||
color={this.props.isActive ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
|
||||
{this.props.text}
|
||||
</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function NavigationLink(props: IProps) {
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
let isActive = props.path === pathname;
|
||||
if (props.routesActive) {
|
||||
for (const routeActive of props.routesActive) {
|
||||
if (isActive) break;
|
||||
isActive = pathname.includes(routeActive);
|
||||
}
|
||||
}
|
||||
return <NavigationLinkClass {...props} isActive={isActive} />;
|
||||
}
|
@ -1,14 +1,26 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-generic-white);
|
||||
box-shadow: $shadow-nav;
|
||||
padding: 24px;
|
||||
position: absolute;
|
||||
top: 107px;
|
||||
right: 66px;
|
||||
top: 48px;
|
||||
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
|
||||
|
||||
border-radius: var(--menu-radius, 0);
|
||||
border: 1px solid var(--menu-border, #d7dce0);
|
||||
background: var(--color-generic-white, #fff);
|
||||
|
||||
text-wrap: nowrap;
|
||||
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
|
||||
z-index: 3;
|
||||
top: calc(var(--header-height) + 10px);
|
||||
right: 32px;
|
||||
|
||||
text-align: center;
|
||||
animation: smooth-appear 0.2s ease forwards;
|
||||
|
||||
@ -20,15 +32,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 100%;
|
||||
border: 1px solid var(--color-neutral-200);
|
||||
}
|
||||
}
|
||||
|
||||
.background {
|
||||
|
@ -2,8 +2,8 @@ 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";
|
||||
import MenuItem from "@Front/Components/DesignSystem/Menu/MenuItem";
|
||||
|
||||
type IProps = {
|
||||
isOpen: boolean;
|
||||
@ -19,10 +19,28 @@ 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 target="_blank" path="https://ressources.lecoffre.io/" text="Guide de Prise en Main" />
|
||||
<NavigationLink target="_blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
|
||||
<div className={classes["separator"]} />
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Mon compte",
|
||||
link: Module.getInstance().get().modules.pages.MyAccount.props.path,
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Guide de Prise en Main",
|
||||
link: "https://ressources.lecoffre.io/",
|
||||
target: "_blank",
|
||||
}}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "CGU",
|
||||
link: "/CGU_LeCoffre_io.pdf",
|
||||
hasSeparator: true,
|
||||
}}
|
||||
/>
|
||||
<LogOutButton />
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,27 +1,20 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import DisconnectIcon from "@Assets/Icons/disconnect.svg";
|
||||
import classes from "./classes.module.scss";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import { useRouter } from "next/router";
|
||||
import UserStore from "@Front/Stores/UserStore";
|
||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||
import UserStore from "@Front/Stores/UserStore";
|
||||
import { PowerIcon } from "@heroicons/react/24/outline";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import MenuItem from "../Menu/MenuItem";
|
||||
|
||||
export default function LogOut() {
|
||||
const router = useRouter();
|
||||
const variables = FrontendVariables.getInstance();
|
||||
|
||||
const disconnect = async () => {
|
||||
await UserStore.instance.disconnect();
|
||||
router.push(`https://qual-connexion.idnot.fr/user/auth/logout?sourceURL=${variables.FRONT_APP_HOST}`);
|
||||
};
|
||||
const disconnect = useCallback(() => {
|
||||
UserStore.instance
|
||||
.disconnect()
|
||||
.then(() => router.push(`https://qual-connexion.idnot.fr/user/auth/logout?sourceURL=${variables.FRONT_APP_HOST}`));
|
||||
}, [router, variables.FRONT_APP_HOST]);
|
||||
|
||||
return (
|
||||
<div className={classes["root"]} onClick={disconnect}>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_500}>
|
||||
Déconnexion
|
||||
</Typography>
|
||||
<Image src={DisconnectIcon} className={classes["disconnect-icon"]} alt="disconnect" />
|
||||
</div>
|
||||
);
|
||||
return <MenuItem item={{ text: "Déconnexion", icon: <PowerIcon />, onClick: disconnect }} />;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
.root {
|
||||
width: 100%;
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
padding: var(--spacing-md, 16px);
|
||||
@ -7,12 +8,16 @@
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg, 24px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
> svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: all ease-in-out 0.1s;
|
||||
}
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.chevron.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.separator {
|
||||
@ -20,4 +25,14 @@
|
||||
height: 1px;
|
||||
background-color: var(--separator-stroke-light, #d7dce0);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.dropdown.open {
|
||||
max-height: 500px;
|
||||
}
|
||||
}
|
||||
|
@ -3,27 +3,71 @@ import classes from "./classes.module.scss";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import useHoverable from "@Front/Hooks/useHoverable";
|
||||
import { IItem } from "..";
|
||||
import classNames from "classnames";
|
||||
import { IAppRule } from "@Front/Api/Entities/rule";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
|
||||
type IProps = {
|
||||
item: IItem;
|
||||
closeMenuCb: () => void;
|
||||
};
|
||||
|
||||
type IItemBase = {
|
||||
text: string;
|
||||
icon?: JSX.Element;
|
||||
hasSeparator?: boolean;
|
||||
color?: ETypoColor;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
type IItemWithLink = IItemBase & {
|
||||
link: string;
|
||||
rules?: IAppRule[];
|
||||
routesActive?: string[];
|
||||
onClick?: never;
|
||||
dropdown?: never;
|
||||
target?: "_blank";
|
||||
};
|
||||
|
||||
type IItemWithOnClick = IItemBase & {
|
||||
onClick: () => void;
|
||||
link?: never;
|
||||
rules?: never;
|
||||
routesActive?: never;
|
||||
dropdown?: never;
|
||||
target?: never;
|
||||
};
|
||||
|
||||
type IItemWithDropdown = IItemBase & {
|
||||
dropdown: {
|
||||
items: IItem[];
|
||||
};
|
||||
routesActive?: never;
|
||||
link?: never;
|
||||
rules?: never;
|
||||
onClick?: never;
|
||||
target?: never;
|
||||
};
|
||||
|
||||
export type IItem = IItemWithLink | IItemWithOnClick | IItemWithDropdown;
|
||||
|
||||
export default function MenuItem(props: IProps) {
|
||||
const { item, closeMenuCb } = props;
|
||||
const { item } = props;
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
const [isActive, setIsActive] = React.useState(item.link === pathname);
|
||||
|
||||
const { isOpen, toggle, open } = useOpenable();
|
||||
|
||||
const handleClickElement = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
closeMenuCb();
|
||||
item.onClose?.();
|
||||
const link = e.currentTarget.getAttribute("data-link");
|
||||
if (item.target === "_blank") window.open(item.link, "_blank");
|
||||
if (link) router.push(link);
|
||||
if (item.onClick) item.onClick();
|
||||
},
|
||||
[closeMenuCb, item, router],
|
||||
[item, router],
|
||||
);
|
||||
|
||||
const { handleMouseEnter, handleMouseLeave, isHovered } = useHoverable();
|
||||
@ -44,7 +88,25 @@ export default function MenuItem(props: IProps) {
|
||||
if (pathname.includes(routeActive)) setIsActive(true);
|
||||
}
|
||||
}
|
||||
}, [isActive, item.link, item.routesActive, pathname]);
|
||||
if (item.dropdown) {
|
||||
for (const subItem of item.dropdown.items) {
|
||||
if (isActive) break;
|
||||
if (subItem.link === pathname) {
|
||||
!isOpen && open();
|
||||
setIsActive(true);
|
||||
}
|
||||
if (subItem.routesActive) {
|
||||
for (const routeActive of subItem.routesActive) {
|
||||
if (isActive) break;
|
||||
if (pathname.includes(routeActive)) {
|
||||
!isOpen && open();
|
||||
setIsActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isActive, isOpen, item.dropdown, item.link, item.routesActive, open, pathname]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -53,12 +115,23 @@ export default function MenuItem(props: IProps) {
|
||||
data-link={item.link}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}>
|
||||
<div className={classes["menu-item"]}>
|
||||
{React.cloneElement(item.icon, { color: `var(${getColor()})` })}
|
||||
<div className={classes["menu-item"]} onClick={item.dropdown && toggle}>
|
||||
{item.icon && React.cloneElement(item.icon, { color: `var(${getColor()})` })}
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={getColor()}>
|
||||
{item.text}
|
||||
</Typography>
|
||||
{item.dropdown &&
|
||||
React.cloneElement(<ChevronDownIcon className={classNames(classes["chevron"], isOpen && [classes["open"]])} />, {
|
||||
color: `var(${getColor()})`,
|
||||
})}
|
||||
</div>
|
||||
{item.dropdown && (
|
||||
<div className={classNames(classes["dropdown"], isOpen && [classes["open"]])}>
|
||||
{item.dropdown.items.map((subItem, index) => (
|
||||
<MenuItem key={index} item={subItem} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{item.hasSeparator && <div className={classes["separator"]} />}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,35 +1,10 @@
|
||||
import { IAppRule } from "@Front/Api/Entities/rule";
|
||||
import { ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
import useHoverable from "@Front/Hooks/useHoverable";
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import MenuItem from "./MenuItem";
|
||||
|
||||
type IItemBase = {
|
||||
icon: JSX.Element;
|
||||
text: string;
|
||||
hasSeparator?: boolean;
|
||||
color?: ETypoColor;
|
||||
};
|
||||
|
||||
type IItemWithLink = IItemBase & {
|
||||
link: string;
|
||||
rules?: IAppRule[];
|
||||
routesActive?: string[];
|
||||
onClick?: never;
|
||||
};
|
||||
|
||||
type IItemWithOnClick = IItemBase & {
|
||||
onClick: () => void;
|
||||
link?: never;
|
||||
rules?: never;
|
||||
routesActive?: never;
|
||||
};
|
||||
|
||||
export type IItem = IItemWithLink | IItemWithOnClick;
|
||||
import MenuItem, { IItem } from "./MenuItem";
|
||||
|
||||
type IProps = {
|
||||
children: React.ReactNode;
|
||||
@ -79,7 +54,7 @@ export default function Menu(props: IProps) {
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<Rules mode={RulesMode.NECESSARY} rules={item.rules ?? []} key={item.link}>
|
||||
<MenuItem item={item} key={index} closeMenuCb={close} />
|
||||
<MenuItem item={item} key={index} />
|
||||
</Rules>
|
||||
);
|
||||
})}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
||||
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
|
||||
import Menu, { IItem } from "@Front/Components/DesignSystem/Menu";
|
||||
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import Module from "@Front/Config/Module";
|
||||
@ -10,6 +9,8 @@ import { useCallback } from "react";
|
||||
|
||||
import { AnchorStatus } from "..";
|
||||
import classes from "./classes.module.scss";
|
||||
import { IItem } from "@Front/Components/DesignSystem/Menu/MenuItem";
|
||||
import Menu from "@Front/Components/DesignSystem/Menu";
|
||||
|
||||
type IProps = {
|
||||
folder: OfficeFolder | null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user