Merge branch 'dev' into staging

This commit is contained in:
Max S 2024-07-29 11:49:58 +02:00
commit d286e7cfcc
39 changed files with 595 additions and 796 deletions

View File

@ -6,6 +6,7 @@ import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
import SearchBar from "../SearchBar"; import SearchBar from "../SearchBar";
import Typography, { ETypo, ETypoColor } from "../Typography"; import Typography, { ETypo, ETypoColor } from "../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { getLabel } from "../Dropdown";
type IProps = { type IProps = {
options: IOption[]; options: IOption[];
@ -51,24 +52,21 @@ export default function Autocomplete(props: IProps) {
}, [selectedOptionProps]); }, [selectedOptionProps]);
const handleSelectOption = useCallback( const handleSelectOption = useCallback(
(option: IOption) => { (newOption: IOption, _options: IOption[]) => {
setSelectedOption(option); setSelectedOption(newOption);
setSearchValue(getLabel(option) || ""); setSearchValue(getLabel(newOption) || "");
openable.close(); openable.close();
}, },
[openable], [openable],
); );
function getLabel(option: IOption | null): string | null {
if (!option) return null;
if (typeof option.label === "string") {
return option.label;
}
return `${option.label.text} ${option.label.subtext}`;
}
return ( return (
<DropdownMenu options={filteredOptions} openable={openable} onSelect={handleSelectOption} selectedOption={selectedOption}> <DropdownMenu
options={filteredOptions}
openable={openable}
onSelect={handleSelectOption}
selectedOptions={selectedOption ? [selectedOption] : []}
>
<div className={classes["root"]}> <div className={classes["root"]}>
{label && ( {label && (
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}> <Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
@ -76,7 +74,14 @@ export default function Autocomplete(props: IProps) {
</Typography> </Typography>
)} )}
</div> </div>
<SearchBar placeholder={placeholder} disabled={disabled} onChange={handleSearchChange} value={searchValue} /> <SearchBar
placeholder={placeholder}
disabled={disabled}
onChange={handleSearchChange}
value={searchValue}
onClear={() => setSelectedOption(null)}
onFocus={openable.open}
/>
</DropdownMenu> </DropdownMenu>
); );
} }

View File

@ -0,0 +1,20 @@
@import "@Themes/constants.scss";
.root {
width: fit-content;
height: 32px;
display: inline-flex;
padding: 4px 12px;
align-items: center;
gap: 8px;
border-radius: var(--input-chip-radius, 360px);
border: 1px solid var(--input-chip-default-border, #b7d1f1);
background: var(--input-chip-default-background, #e5eefa);
&:hover {
background-color: var(--input-chip-hovered-background);
border-color: var(--input-chip-hovered-border);
}
}

View File

@ -0,0 +1,26 @@
import { XMarkIcon } from "@heroicons/react/24/outline";
import classNames from "classnames";
import React from "react";
import IconButton from "../../IconButton";
import Typography, { ETypo, ETypoColor } from "../../Typography";
import classes from "./classes.module.scss";
type IProps = {
text: string;
className?: string;
onDelete?: () => void;
};
export default function Chip(props: IProps) {
const { className, text, onDelete } = props;
return (
<div className={classNames(classes["root"], className)}>
<Typography typo={ETypo.TEXT_MD_SEMIBOLD} color={ETypoColor.INPUT_CHIP_CONTRAST}>
{text}
</Typography>
<IconButton icon={<XMarkIcon />} onClick={onDelete} />
</div>
);
}

View File

@ -0,0 +1,73 @@
@import "@Themes/constants.scss";
.root {
border-radius: var(--input-radius, 0px);
border: 1px solid var(--input-main-border-filled, #6d7e8a);
background: var(--input-background, #fff);
svg {
stroke: var(--button-icon-button-default-default);
}
&:hover {
border-radius: var(--input-radius, 0px);
border: 1px solid var(--input-main-border-hovered, #b4bec5);
background: var(--input-background, #fff);
}
&[data-has-value="true"] {
border-radius: var(--input-radius, 0px);
border: 1px solid var(--input-main-border-filled, #6d7e8a);
background: var(--input-background, #fff);
}
&[data-is-focused="true"] {
border-radius: var(--input-radius, 0px);
border: 1px solid var(--input-main-border-focused, #005bcb);
background: var(--input-background, #fff);
}
&[data-is-disabled="true"] {
opacity: var(--opacity-disabled, 0.3);
pointer-events: none;
}
.content {
display: flex;
align-items: center;
align-content: center;
gap: 16px var(--spacing-2, 16px);
flex-wrap: wrap;
min-height: 56px;
padding: var(--spacing-1-5, 12px) var(--spacing-sm, 8px);
.input {
flex: 1;
border: none;
color: var(--input-placeholder-filled, #24282e);
/* text/md/semibold */
font-family: var(--font-text-family, Poppins);
font-size: 16px;
font-style: normal;
font-weight: var(--font-text-weight-semibold, 600);
line-height: normal;
letter-spacing: 0.08px;
width: 100%;
&::placeholder {
color: var(--input-placeholder-empty, #6d7e8a);
/* text/md/regular */
font-family: var(--font-text-family, Poppins);
font-size: 16px;
font-style: normal;
font-weight: var(--font-text-weight-regular, 400);
line-height: normal;
letter-spacing: 0.08px;
}
}
}
}

View File

@ -0,0 +1,90 @@
import React, { useCallback, useEffect } from "react";
import { getLabel } from "../../Dropdown";
import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
import Chip from "../Chip";
import classes from "./classes.module.scss";
type IProps = {
selectedOptions: IOption[];
onSelectedOptionsChange: (options: IOption[]) => void;
onChange?: (input: string) => void;
value?: string;
placeholder?: string;
disabled?: boolean;
onClear?: () => void;
onFocus?: () => void;
onBlur?: () => void;
};
export default function ChipContainer(props: IProps) {
const {
selectedOptions,
onChange,
value: propValue,
placeholder = "Rechercher",
disabled = false,
onFocus,
onBlur,
onSelectedOptionsChange,
} = props;
const [isFocused, setIsFocused] = React.useState(false);
const [value, setValue] = React.useState(propValue || "");
const changeValue = useCallback(
(value: string) => {
setValue(value);
onChange && onChange(value);
},
[onChange],
);
const handleOnChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value), [changeValue]);
const handleFocus = useCallback(() => {
setIsFocused(true);
onFocus?.();
}, [onFocus]);
const handleBlur = useCallback(
(e: React.FocusEvent<HTMLInputElement, Element>) => {
setIsFocused(false);
onBlur?.();
},
[onBlur],
);
const onChipDelete = useCallback(
(option: IOption) => {
const newSelectedOptions = selectedOptions.filter((selectedOption) => selectedOption.id !== option.id);
onSelectedOptionsChange && onSelectedOptionsChange(newSelectedOptions);
},
[selectedOptions, onSelectedOptionsChange],
);
useEffect(() => {
if (propValue !== undefined) {
setValue(propValue);
}
}, [propValue]);
return (
<div className={classes["root"]} data-is-focused={isFocused} data-has-value={value !== ""} data-is-disabled={disabled}>
<div className={classes["content"]}>
{selectedOptions.map((option) => (
<Chip key={option.id} text={getLabel(option) ?? ""} onDelete={() => onChipDelete(option)} />
))}
<input
type="text"
value={value}
placeholder={placeholder}
className={classes["input"]}
onChange={handleOnChange}
onFocus={handleFocus}
onBlur={handleBlur}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,7 @@
@import "@Themes/constants.scss";
.root {
.label {
padding: 0px var(--spacing-2, 16px);
}
}

View File

@ -0,0 +1,88 @@
import useOpenable from "@Front/Hooks/useOpenable";
import { useCallback, useEffect, useState } from "react";
import DropdownMenu from "../Dropdown/DropdownMenu";
import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
import Typography, { ETypo, ETypoColor } from "../Typography";
import classes from "./classes.module.scss";
import ChipContainer from "./ChipContainer";
import { getLabel } from "../Dropdown";
type IProps = {
options: IOption[];
placeholder?: string;
disabled?: boolean;
label?: string;
onSelect?: (option: IOption) => void;
selectedOptions?: IOption[] | null;
};
export default function AutocompleteMultiSelect(props: IProps) {
const { options, placeholder, disabled, label, selectedOptions: selectedOptionsProps } = props;
const [selectedOptions, setSelectedOptions] = useState<IOption[] | null>(selectedOptionsProps ?? null);
const [searchValue, setSearchValue] = useState("");
const [filteredOptions, setFilteredOptions] = useState<IOption[]>(options);
const openable = useOpenable({ defaultOpen: false });
useEffect(() => {
if (searchValue) {
const filteredOptions = options.filter((option) => getLabel(option)?.toLowerCase().includes(searchValue.toLowerCase()));
console.log(filteredOptions);
if (filteredOptions.length === 0)
return setFilteredOptions([{ id: "no-results", label: "Aucun résulats", notSelectable: true }]);
return setFilteredOptions(filteredOptions);
}
return setFilteredOptions(options);
}, [searchValue, options]);
const handleSearchChange = useCallback(
(value: string) => {
setSearchValue(value);
if (value) {
openable.open();
} else {
openable.close();
}
},
[openable],
);
useEffect(() => {
setSelectedOptions(selectedOptionsProps ?? null);
}, [selectedOptionsProps]);
const handleSelectOption = useCallback(
(_newOption: IOption, options: IOption[]) => {
setSelectedOptions(options);
setSearchValue("");
openable.close();
},
[openable],
);
return (
<DropdownMenu
options={filteredOptions}
openable={openable}
onSelect={handleSelectOption}
selectedOptions={selectedOptions ? selectedOptions : []}>
<div className={classes["root"]}>
{label && (
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
{label}
</Typography>
)}
</div>
<ChipContainer
placeholder={placeholder}
disabled={disabled}
onChange={handleSearchChange}
value={searchValue}
onClear={() => setSelectedOptions(null)}
onFocus={openable.open}
selectedOptions={selectedOptions ?? []}
onSelectedOptionsChange={setSelectedOptions}
/>
</DropdownMenu>
);
}

View File

@ -30,6 +30,7 @@
overflow: visible; overflow: visible;
.content { .content {
max-height: 500px; max-height: 500px;
overflow: auto;
opacity: 1; opacity: 1;
} }
} }

View File

@ -1,12 +1,12 @@
import classNames from "classnames"; import classNames from "classnames";
import React, { useCallback } from "react"; import React, { useCallback, useEffect, useRef } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import DropdownOption, { IOption } from "./DropdownOption"; import DropdownOption, { IOption } from "./DropdownOption";
type IProps = { type IProps = {
options: IOption[]; options: IOption[];
selectedOption: IOption | null; selectedOptions: IOption[];
children: React.ReactNode; children: React.ReactNode;
openable: { openable: {
isOpen: boolean; isOpen: boolean;
@ -14,21 +14,40 @@ type IProps = {
close: () => void; close: () => void;
toggle: () => void; toggle: () => void;
}; };
onSelect?: (option: IOption) => void; onSelect?: (newOption: IOption, options: IOption[]) => void;
}; };
export default function DropdownMenu(props: IProps) { export default function DropdownMenu(props: IProps) {
const { children, options, onSelect, openable, selectedOption } = props; const { children, options, onSelect, openable, selectedOptions } = props;
const ref = useRef<HTMLDivElement>(null);
const handleSelect = useCallback( const handleSelect = useCallback(
(option: IOption) => { (option: IOption) => {
onSelect?.(option); const newOptions = selectedOptions.some((selectedOption) => selectedOption.id === option.id)
? selectedOptions
: [...selectedOptions, option];
onSelect?.(option, newOptions);
openable.close(); openable.close();
}, },
[onSelect, openable], [onSelect, openable, selectedOptions],
); );
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
openable.close();
}
},
[openable],
);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [handleClickOutside]);
return ( return (
<div className={classNames([classes["root"], openable.isOpen && classes["open"]])}> <div className={classNames([classes["root"], openable.isOpen && classes["open"]])} ref={ref}>
{children} {children}
<div className={classes["content"]}> <div className={classes["content"]}>
{options.map((option) => { {options.map((option) => {
@ -39,6 +58,6 @@ export default function DropdownMenu(props: IProps) {
); );
function isActive(option: IOption): boolean { function isActive(option: IOption): boolean {
return selectedOption?.id === option.id; return selectedOptions.some((selectedOption) => selectedOption.id === option.id);
} }
} }

View File

@ -27,15 +27,19 @@ export default function Dropdown(props: IProps) {
}, [selectedOptionProps]); }, [selectedOptionProps]);
const handleOnSelect = useCallback( const handleOnSelect = useCallback(
(option: IOption) => { (newOption: IOption, _options: IOption[]) => {
setSelectedOption(option); setSelectedOption(newOption);
onSelect?.(option); onSelect?.(newOption);
}, },
[onSelect], [onSelect],
); );
return ( return (
<DropdownMenu options={options} openable={openable} onSelect={handleOnSelect} selectedOption={selectedOption}> <DropdownMenu
options={options}
openable={openable}
onSelect={handleOnSelect}
selectedOptions={selectedOption ? [selectedOption] : []}>
<div className={classes["root"]}> <div className={classes["root"]}>
{label && ( {label && (
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}> <Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>

View File

@ -1,14 +0,0 @@
@import "@Themes/constants.scss";
.root {
height: calc(100vh - 290px);
overflow-y: scroll;
&.archived {
height: calc(100vh - 220px);
}
.active {
background-color: var(--color-neutral-200);
}
}

View File

@ -1,76 +0,0 @@
import Module from "@Front/Config/Module";
import classNames from "classnames";
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
import FolderContainer from "../FolderContainer";
import classes from "./classes.module.scss";
type IProps = {
folders: OfficeFolder[];
isArchived: boolean;
onSelectedFolder?: (folder: OfficeFolder) => void;
onCloseLeftSide?: () => void;
};
type IPropsClass = IProps & {
selectedFolder: string;
};
type IState = {};
class FolderListClass 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 override render(): JSX.Element {
return <div className={classNames(classes["root"], this.props.isArchived ? classes["archived"] : "")}>{this.renderFolders()}</div>;
}
private renderFolders(): JSX.Element[] {
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 (
<div
onClick={this.props.onCloseLeftSide}
key={folder.uid}
className={folder.uid === this.props.selectedFolder ? classes["active"] : ""}>
<Link href={this.redirectPath.replace("[folderUid]", folder.uid ?? "")}>
<FolderContainer folder={folder} onSelectedFolder={this.props.onSelectedFolder} />;
</Link>
</div>
);
});
}
}
export default function FolderList(props: IProps) {
const router = useRouter();
let { folderUid } = router.query;
folderUid = folderUid as string;
return <FolderListClass {...props} selectedFolder={folderUid} />;
}

View File

@ -11,8 +11,6 @@ import SearchBlockList from "../SearchBlockList";
type IProps = { type IProps = {
folders: OfficeFolder[]; folders: OfficeFolder[];
isArchived: boolean; isArchived: boolean;
onSelectedFolder?: (folder: OfficeFolder) => void;
onCloseLeftSide?: () => void;
}; };
export default function FolderListContainer(props: IProps) { export default function FolderListContainer(props: IProps) {
@ -64,10 +62,8 @@ export default function FolderListContainer(props: IProps) {
const [blocks, setBlocks] = React.useState<IBlock[]>(getBlocks(folders)); const [blocks, setBlocks] = React.useState<IBlock[]>(getBlocks(folders));
const onSelectedFolder = (block: IBlock) => { const onSelectedFolder = (block: IBlock) => {
props.onCloseLeftSide && props.onCloseLeftSide();
const folder = folders.find((folder) => folder.uid === block.id); const folder = folders.find((folder) => folder.uid === block.id);
if (!folder) return; if (!folder) return;
props.onSelectedFolder && props.onSelectedFolder(folder);
const path = redirectPath.replace("[folderUid]", folder.uid ?? ""); const path = redirectPath.replace("[folderUid]", folder.uid ?? "");
router.push(path); router.push(path);
}; };

View File

@ -94,6 +94,10 @@
} }
} }
&.default {
padding: 0;
}
&.disabled { &.disabled {
cursor: default; cursor: default;
opacity: var(--opacity-disabled, 0.3); opacity: var(--opacity-disabled, 0.3);

View File

@ -1,16 +1,21 @@
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
import React, { useCallback, useEffect } from "react"; import React, { useCallback, useEffect } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
type IProps = { type IProps = {
onChange?: (input: string) => void; onChange?: (input: string) => void;
value?: string; value?: string;
placeholder?: string; placeholder?: string;
disabled?: boolean; disabled?: boolean;
onClear?: () => void;
onFocus?: () => void;
onBlur?: () => void;
}; };
export default function SearchBar({ onChange, value: propValue, placeholder = "Rechercher", disabled = false }: IProps) { export default function SearchBar(props: IProps) {
const { onChange, value: propValue, placeholder = "Rechercher", disabled = false, onClear, onFocus, onBlur } = props;
const [isFocused, setIsFocused] = React.useState(false); const [isFocused, setIsFocused] = React.useState(false);
const [value, setValue] = React.useState(propValue || ""); const [value, setValue] = React.useState(propValue || "");
@ -22,10 +27,25 @@ export default function SearchBar({ onChange, value: propValue, placeholder = "R
[onChange], [onChange],
); );
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value); const handleOnChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value), [changeValue]);
const handleFocus = () => setIsFocused(true);
const handleBlur = () => setIsFocused(false); const handleFocus = useCallback(() => {
const clearValue = () => changeValue(""); setIsFocused(true);
onFocus?.();
}, [onFocus]);
const handleBlur = useCallback(
(e: React.FocusEvent<HTMLInputElement, Element>) => {
setIsFocused(false);
onBlur?.();
},
[onBlur],
);
const clearValue = useCallback(() => {
changeValue("");
onClear?.();
}, [changeValue, onClear]);
useEffect(() => { useEffect(() => {
if (propValue !== undefined) { if (propValue !== undefined) {

View File

@ -1,8 +1,8 @@
import React, { useCallback, useEffect } from "react"; import React, { useEffect } from "react";
import { IBlock } from "../BlockList/Block"; import { IBlock } from "../BlockList/Block";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Typography, { ETypo, ETypoColor } from "../../Typography"; import Dropdown from "../../Dropdown";
import { ChevronDownIcon } from "@heroicons/react/24/outline"; import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
type IProps = { type IProps = {
blocks: IBlock[]; blocks: IBlock[];
onSelectedBlock: (block: IBlock) => void; onSelectedBlock: (block: IBlock) => void;
@ -11,72 +11,36 @@ type IProps = {
export default function DropdownNavigation({ blocks, onSelectedBlock, defaultSelectedBlock = blocks[0] }: IProps) { export default function DropdownNavigation({ blocks, onSelectedBlock, defaultSelectedBlock = blocks[0] }: IProps) {
const [selectedBlock, setSelectedBlock] = React.useState<IBlock | null>(defaultSelectedBlock ?? null); const [selectedBlock, setSelectedBlock] = React.useState<IBlock | null>(defaultSelectedBlock ?? null);
const [isDropdownOpened, setIsDropdownOpened] = React.useState<boolean>(false);
const rootRef = React.useRef<HTMLDivElement>(null);
const selectBlock = useCallback(
(id: string) => {
setIsDropdownOpened(false);
setSelectedBlock(blocks.find((folder) => folder.id === id)!);
onSelectedBlock && onSelectedBlock(blocks.find((folder) => folder.id === id)!);
},
[blocks, onSelectedBlock],
);
const handleOnClick = () => setIsDropdownOpened((prev) => !prev);
useEffect(() => {
// on click outside of root, close dropdown
const handleClickOutside = (event: MouseEvent) => {
if (rootRef.current && !rootRef.current.contains(event.target as Node)) {
setIsDropdownOpened(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
useEffect(() => { useEffect(() => {
if (defaultSelectedBlock) setSelectedBlock(defaultSelectedBlock); if (defaultSelectedBlock) setSelectedBlock(defaultSelectedBlock);
}, [defaultSelectedBlock]); }, [defaultSelectedBlock]);
return (
<div className={classes["root"]} data-is-opened={isDropdownOpened} ref={rootRef}>
{selectedBlock && (
<>
<div className={classes["dropdown-header"]} onClick={handleOnClick}>
<div className={classes["text-container"]}>
{selectedBlock.secondaryText && (
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
{selectedBlock.secondaryText}
</Typography>
)}
<Typography typo={ETypo.TEXT_MD_BOLD} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
{selectedBlock.primaryText}
</Typography>
</div>
<ChevronDownIcon height="24" width="24" color={`var(${ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT})`} />
</div>
{isDropdownOpened && ( const handleDropDownSelect = (option: IOption) => {
<div className={classes["dropdown-content"]}> const block = blocks.find((block) => block.id === option.id);
{blocks if (block) {
.filter((block) => block.id !== selectedBlock.id) setSelectedBlock(block);
.map((block) => ( onSelectedBlock
<div key={block.id} className={classes["dropdown-item"]} onClick={() => selectBlock(block.id)}> ? onSelectedBlock(block)
{block.secondaryText && ( : console.error("DropdownNavigation: onSelectedBlock prop is required to handle block selection");
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}> }
{block.secondaryText} };
</Typography>
)} return (
<Typography typo={ETypo.TEXT_MD_BOLD} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}> <div className={classes["root"]}>
{block.primaryText} <Dropdown
</Typography> options={blocks.map((block) => {
</div> return {
))} id: block.id,
</div> label: {
)} subtext: block.primaryText,
</> text: block.secondaryText,
)} },
} as IOption;
})}
onSelect={handleDropDownSelect}
selectedOption={selectedBlock ? { id: selectedBlock.id, label: selectedBlock.primaryText } : undefined}
/>
</div> </div>
); );
} }

View File

@ -11,6 +11,7 @@
justify-content: flex-start; justify-content: flex-start;
@media (max-width: $screen-m) { @media (max-width: $screen-m) {
height: auto;
gap: 16px; gap: 16px;
padding: var(--spacing-lg, 24px); padding: var(--spacing-lg, 24px);
width: auto; width: auto;

View File

@ -159,6 +159,8 @@ export enum ETypoColor {
DROPDOWN_CONTRAST_DEFAULT = "--dropdown-contrast-default", DROPDOWN_CONTRAST_DEFAULT = "--dropdown-contrast-default",
DROPDOWN_CONTRAST_ACTIVE = "--dropdown-contrast-active", DROPDOWN_CONTRAST_ACTIVE = "--dropdown-contrast-active",
INPUT_CHIP_CONTRAST = "--input-chip-contrast",
} }
export default function Typography(props: IProps) { export default function Typography(props: IProps) {

View File

@ -0,0 +1,23 @@
@import "@Themes/constants.scss";
.root {
position: relative;
.content {
display: flex;
justify-content: flex-start;
height: calc(100vh - var(--header-height));
@media (max-width: $screen-m) {
flex-direction: column;
}
.right-side {
min-width: calc(100% - 336px);
overflow-y: auto;
padding: var(--spacing-lg, 24px);
@media (max-width: $screen-m) {
width: 100%;
}
}
}
}

View File

@ -0,0 +1,45 @@
import Header from "@Front/Components/DesignSystem/Header";
import Version from "@Front/Components/DesignSystem/Version";
import BackArrow from "@Front/Components/Elements/BackArrow";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
export type IPropsDashboardWithList = {
title?: string;
children?: ReactNode;
isArchived?: boolean;
hasBackArrow?: boolean;
backArrowUrl?: string;
mobileBackText?: string;
};
type IProps = IPropsDashboardWithList & {
blocksList: IBlock[];
onSelectedBlock: (block: IBlock) => void;
headerConnected?: boolean;
};
export default function DefaultDashboardWithList(props: IProps) {
const { hasBackArrow, backArrowUrl, children, blocksList, onSelectedBlock, headerConnected = true } = props;
return (
<div className={classes["root"]}>
<Header isUserConnected={headerConnected} />
<div className={classes["content"]}>
<SearchBlockList blocks={blocksList} onSelectedBlock={onSelectedBlock} />
<div className={classes["right-side"]}>
{hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={backArrowUrl ?? ""} />
</div>
)}
{children}
</div>
</div>
<Version />
</div>
);
}

View File

@ -13,14 +13,13 @@ type IProps = {
title: string; title: string;
children?: ReactNode; children?: ReactNode;
isArchived?: boolean; isArchived?: boolean;
onSelectedFolder?: (folder: OfficeFolder) => void;
hasBackArrow?: boolean; hasBackArrow?: boolean;
backArrowUrl?: string; backArrowUrl?: string;
mobileBackText?: string; mobileBackText?: string;
}; };
export default function DefaultNotaryDashboard(props: IProps) { export default function DefaultNotaryDashboard(props: IProps) {
const { hasBackArrow, onSelectedFolder, backArrowUrl, children, isArchived } = props; const { hasBackArrow, backArrowUrl, children, isArchived } = props;
const [folders, setFolders] = React.useState<OfficeFolder[]>([]); const [folders, setFolders] = React.useState<OfficeFolder[]>([]);
@ -67,7 +66,7 @@ export default function DefaultNotaryDashboard(props: IProps) {
<div className={classes["root"]}> <div className={classes["root"]}>
<Header isUserConnected={true} /> <Header isUserConnected={true} />
<div className={classes["content"]}> <div className={classes["content"]}>
<FolderListContainer folders={folders} onSelectedFolder={onSelectedFolder} isArchived={isArchived ?? false} /> <FolderListContainer folders={folders} isArchived={isArchived ?? false} />
<div className={classes["right-side"]}> <div className={classes["right-side"]}>
{hasBackArrow && ( {hasBackArrow && (
<div className={classes["back-arrow-desktop"]}> <div className={classes["back-arrow-desktop"]}>

View File

@ -1,23 +0,0 @@
@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: 100vh;
height: 100vh;
overflow: auto;
border-right: 1px solid var(--color-neutral-200);
}
}

View File

@ -1,44 +0,0 @@
import Module from "@Front/Config/Module";
import { Office } from "le-coffre-resources/dist/SuperAdmin";
import { useRouter } from "next/router";
import React, { useCallback } from "react";
import classes from "./classes.module.scss";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
type IProps = {
offices: Office[];
onSelectedOffice?: (office: Office) => void;
onCloseLeftSide?: () => void;
};
export default function OfficeListContainer(props: IProps) {
const router = useRouter();
const { officeUid } = router.query;
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"]}>
<SearchBlockList
blocks={props.offices.map((office) => {
return {
primaryText: office.crpcen + " - " + office.name,
id: office.uid!,
isActive: office.uid === officeUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
);
}

View File

@ -1,116 +0,0 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - var(--header-height));
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--color-generic-white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: var(--color-generic-white);
z-index: 3;
display: flex;
width: 336px;
min-width: 336px;
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: 336px;
min-width: 336px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: var(--color-generic-white);
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - var(--header-height));
border-right: 1px var(--color-neutral-200) 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: 24px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
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

@ -1,112 +1,42 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import { Office } from "le-coffre-resources/dist/SuperAdmin";
import React, { useEffect } from "react";
import { useRouter } from "next/router";
import Module from "@Front/Config/Module";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices"; import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices";
import Button, { EButtonstyletype, 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"; type IProps = IPropsDashboardWithList;
import OfficeListContainer from "./OfficeListContainer";
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
type IProps = { export default function DefaultOfficeDashboard(props: IProps) {
title: string; const [offices, setOffices] = React.useState<Office[] | null>(null);
children?: ReactNode; const router = useRouter();
onSelectedOffice: (office: Office) => void; const { officeUid } = router.query;
hasBackArrow: boolean; useEffect(() => {
backArrowUrl?: string; Offices.getInstance()
mobileBackText?: string; .get()
}; .then((offices) => setOffices(offices));
type IState = { }, []);
offices: Office[] | null;
isLeftSideOpen: boolean;
leftSideCanBeClosed: boolean;
};
export default class DefaultOfficeDashboard extends React.Component<IProps, IState> { const onSelectedBlock = (block: IBlock) => {
private onWindowResize = () => {}; router.push(Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path.replace("[uid]", block.id));
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 ( return (
<div className={classes["root"]}> <DefaultDashboardWithList
<Header isUserConnected={true} /> {...props}
<div className={classes["content"]}> onSelectedBlock={onSelectedBlock}
{this.state.isLeftSideOpen && <div className={classes["overlay"]} onClick={this.onCloseLeftSide} />} blocksList={
<div className={classNames(classes["left-side"], this.state.isLeftSideOpen && classes["opened"])}> offices
{this.state.offices && <OfficeListContainer offices={this.state.offices} onCloseLeftSide={this.onCloseLeftSide} />} ? offices.map((office) => ({
</div> id: office.uid!,
<div className={classNames(classes["closable-left-side"])}> primaryText: office.name,
<Image alt="open side menu" src={ChevronIcon} className={classes["chevron-icon"]} onClick={this.onOpenLeftSide} /> isActive: office.uid === officeUid,
</div> secondaryText: office.crpcen,
}))
<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
leftIcon={<ChevronLeftIcon />}
variant={EButtonVariant.PRIMARY}
styletype={EButtonstyletype.TEXT}
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

@ -1,23 +0,0 @@
@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: 100vh;
height: 100vh;
overflow: auto;
border-right: 1px solid var(--color-neutral-200);
}
}

View File

@ -1,44 +0,0 @@
import Module from "@Front/Config/Module";
import User from "le-coffre-resources/dist/Notary";
import { useRouter } from "next/router";
import React, { useCallback } from "react";
import classes from "./classes.module.scss";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
type IProps = {
users: User[];
onSelectedUser?: (user: User) => void;
onCloseLeftSide?: () => void;
};
export default function UserListContainer(props: IProps) {
const router = useRouter();
const { userUid } = router.query;
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"]}>
<SearchBlockList
blocks={props.users.map((user) => {
return {
primaryText: user.contact?.first_name + " " + user.contact?.last_name,
id: user.uid!,
isActive: user.uid === userUid,
};
})}
onSelectedBlock={onSelectedBlock}
/>
</div>
);
}

View File

@ -1,116 +0,0 @@
@import "@Themes/constants.scss";
@keyframes growWidth {
0% {
width: 100%;
}
100% {
width: 200%;
}
}
.root {
.content {
display: flex;
overflow: hidden;
height: calc(100vh - var(--header-height));
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--color-generic-white);
opacity: 0.5;
z-index: 2;
transition: all 0.3s $custom-easing;
}
.left-side {
background-color: var(--color-generic-white);
z-index: 3;
display: flex;
width: 336px;
min-width: 336px;
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: 336px;
min-width: 336px;
}
}
@media (max-width: $screen-s) {
width: 0px;
min-width: 0px;
&.opened {
width: 100vw;
min-width: 100vw;
}
}
}
.closable-left-side {
position: absolute;
background-color: var(--color-generic-white);
z-index: 0;
display: flex;
justify-content: center;
min-width: 56px;
max-width: 56px;
height: calc(100vh - var(--header-height));
border-right: 1px var(--color-neutral-200 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: 24px;
overflow-y: auto;
@media (max-width: ($screen-m - 1px)) {
min-width: calc(100vw - 56px);
}
@media (max-width: $screen-s) {
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

@ -1,115 +1,46 @@
import ChevronIcon from "@Assets/Icons/chevron.svg";
import { ChevronLeftIcon } from "@heroicons/react/20/solid";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users"; import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Button, { EButtonstyletype, 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/SuperAdmin"; import User from "le-coffre-resources/dist/SuperAdmin";
import Image from "next/image"; import React, { useEffect } from "react";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss"; import { useRouter } from "next/router";
import UserListContainer from "./UserListContainer"; import Module from "@Front/Config/Module";
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
type IProps = { type IProps = IPropsDashboardWithList;
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> { export default function DefaultUserDashboard(props: IProps) {
private onWindowResize = () => {}; const [users, setUsers] = React.useState<User[] | null>(null);
public static defaultProps: Partial<IProps> = { const router = useRouter();
hasBackArrow: false, const { userUid } = router.query;
}; useEffect(() => {
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
leftIcon={<ChevronLeftIcon />}
variant={EButtonVariant.PRIMARY}
styletype={EButtonstyletype.TEXT}
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 = { const query: IGetUsersparams = {
include: { contact: true }, include: { contact: true },
}; };
const users = await Users.getInstance().get(query); Users.getInstance()
this.setState({ users }); .get(query)
} .then((users) => setUsers(users));
public override componentWillUnmount() { }, []);
this.onWindowResize();
}
private onOpenLeftSide() { const onSelectedBlock = (block: IBlock) => {
this.setState({ isLeftSideOpen: true }); router.push(Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path.replace("[uid]", block.id));
} };
private onCloseLeftSide() { return (
if (!this.state.leftSideCanBeClosed) return; <DefaultDashboardWithList
this.setState({ isLeftSideOpen: false }); {...props}
} onSelectedBlock={onSelectedBlock}
blocksList={
private onResize(window: Window) { users
if (window.innerWidth > 1023) { ? users.map((user) => ({
if (!this.state.leftSideCanBeClosed) return; id: user.uid!,
this.setState({ leftSideCanBeClosed: false }); primaryText: user.contact?.first_name + " " + user.contact?.last_name,
} isActive: user.uid === userUid,
this.setState({ leftSideCanBeClosed: true }); secondaryText: user.contact?.email,
}))
: []
} }
/>
);
} }

View File

@ -1,4 +1,6 @@
import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert"; import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
import Autocomplete from "@Front/Components/DesignSystem/Autocomplete";
import AutocompleteMultiSelect from "@Front/Components/DesignSystem/AutocompleteMultiSelect";
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress"; import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
import Dropdown from "@Front/Components/DesignSystem/Dropdown"; import Dropdown from "@Front/Components/DesignSystem/Dropdown";
@ -19,19 +21,10 @@ import NumberPicker from "@Front/Components/Elements/NumberPicker";
import Tabs from "@Front/Components/Elements/Tabs"; import Tabs from "@Front/Components/Elements/Tabs";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate"; import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import useOpenable from "@Front/Hooks/useOpenable"; import useOpenable from "@Front/Hooks/useOpenable";
import { import { ArchiveBoxIcon, ArrowLongLeftIcon, ArrowLongRightIcon, EllipsisHorizontalIcon, PencilSquareIcon, UsersIcon, XMarkIcon } from "@heroicons/react/24/outline";
ArchiveBoxIcon,
ArrowLongLeftIcon,
ArrowLongRightIcon,
EllipsisHorizontalIcon,
PencilSquareIcon,
UsersIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Autocomplete from "@Front/Components/DesignSystem/Autocomplete";
export default function DesignSystem() { export default function DesignSystem() {
const { isOpen, open, close } = useOpenable(); const { isOpen, open, close } = useOpenable();
@ -84,6 +77,29 @@ export default function DesignSystem() {
<div className={classes["root"]}> <div className={classes["root"]}>
<div /> <div />
<div className={classes["components"]}> <div className={classes["components"]}>
<Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete Multi Select</Typography>
<AutocompleteMultiSelect
options={[
{
id: "1",
label: "Option 1",
},
{
id: "2",
label: "Option 2",
},
{
id: "3",
label: "Option 3",
},
{
id: "4",
label: { text: "Option 4", subtext: "Subtext" },
},
]}
label="Label"
/>
<Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete</Typography> <Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete</Typography>
<Autocomplete <Autocomplete
options={[ options={[
@ -104,7 +120,6 @@ export default function DesignSystem() {
label: { text: "Option 4", subtext: "Subtext" }, label: { text: "Option 4", subtext: "Subtext" },
}, },
]} ]}
placeholder="Placeholder"
label="Label" label="Label"
/> />
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography> <Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography>

View File

@ -50,7 +50,7 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid); .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
return ( return (
<DefaultNotaryDashboard title={"Demander des documents"} onSelectedFolder={() => {}}> <DefaultNotaryDashboard title={"Demander des documents"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<BackArrow url={backUrl} /> <BackArrow url={backUrl} />
<Typography typo={ETypo.DISPLAY_LARGE} color={ETypoColor.COLOR_GENERIC_BLACK} className={classes["title"]}> <Typography typo={ETypo.DISPLAY_LARGE} color={ETypoColor.COLOR_GENERIC_BLACK} className={classes["title"]}>

View File

@ -74,7 +74,7 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
<DefaultNotaryDashboard title={"Ajouter client(s)"} onSelectedFolder={this.onSelectedFolder}> <DefaultNotaryDashboard title={"Ajouter client(s)"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["back-arrow"]}> <div className={classes["back-arrow"]}>
<BackArrow url={this.backwardPath} /> <BackArrow url={this.backwardPath} />

View File

@ -6,10 +6,10 @@ import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNot
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
import { DocumentIcon } from "@heroicons/react/24/outline"; import { DocumentIcon } from "@heroicons/react/24/outline";
import User, { OfficeFolder } from "le-coffre-resources/dist/Notary"; import User from "le-coffre-resources/dist/Notary";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useCallback, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
@ -17,14 +17,10 @@ import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
export default function Folder() { export default function Folder() {
const [_folder, setFolder] = useState<OfficeFolder | null>(null);
const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true); const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true);
const router = useRouter(); const router = useRouter();
const [activeUser, setActiveUser] = useState<User | null>(); const [activeUser, setActiveUser] = useState<User | null>();
const onSelectedFolder = useCallback((folder: OfficeFolder): void => {
setFolder(folder);
}, []);
useEffect(() => { useEffect(() => {
const decodedJwt = JwtService.getInstance().decodeJwt(); const decodedJwt = JwtService.getInstance().decodeJwt();
@ -59,7 +55,7 @@ export default function Folder() {
}, [router]); }, [router]);
return ( return (
<DefaultNotaryDashboard title={"Dossier"} onSelectedFolder={onSelectedFolder} mobileBackText={"Liste des dossiers"}> <DefaultNotaryDashboard title={"Dossier"} mobileBackText={"Liste des dossiers"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["content"]}> <div className={classes["content"]}>
<div className={classes["title-container"]}> <div className={classes["title-container"]}>

View File

@ -39,7 +39,7 @@ class UpdateFolderMetadataClass extends BasePage<IProps, IState> {
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid); .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid);
return ( return (
<DefaultNotaryDashboard title={"Ajouter client(s)"} onSelectedFolder={this.onSelectedFolder}> <DefaultNotaryDashboard title={"Ajouter client(s)"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["back-arrow"]}> <div className={classes["back-arrow"]}>
<BackArrow url={backwardPath} /> <BackArrow url={backwardPath} />

View File

@ -23,11 +23,7 @@ export default class FolderArchived extends BasePage<IProps, IState> {
// TODO: Message if the user has not created any folder yet // TODO: Message if the user has not created any folder yet
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
<DefaultNotaryDashboard <DefaultNotaryDashboard title={"Dossier"} isArchived mobileBackText={"Liste des dossiers"}>
title={"Dossier"}
onSelectedFolder={this.onSelectedFolder}
isArchived
mobileBackText={"Liste des dossiers"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["no-folder-selected"]}> <div className={classes["no-folder-selected"]}>
<Typography typo={ETypo.TITLE_H1}>Informations du dossier</Typography> <Typography typo={ETypo.TITLE_H1}>Informations du dossier</Typography>

View File

@ -117,7 +117,7 @@ export default function OfficeInformations(props: IProps) {
); );
return ( return (
<DefaultOfficeDashboard mobileBackText={"Liste des utilisateurs"}> <DefaultOfficeDashboard>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["header"]}> <div className={classes["header"]}>
<Typography typo={ETypo.TITLE_H1}>Office {officeSelected?.crpcen + " - " + officeSelected?.name}</Typography> <Typography typo={ETypo.TITLE_H1}>Office {officeSelected?.crpcen + " - " + officeSelected?.name}</Typography>

View File

@ -9,7 +9,7 @@ type IState = {};
export default class Offices extends BasePage<IProps, IState> { export default class Offices extends BasePage<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
<DefaultOfficeDashboard title={"Dossier"} mobileBackText={"Liste des offices"}> <DefaultOfficeDashboard title={"Dossier"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["no-folder-selected"]}> <div className={classes["no-folder-selected"]}>
<Typography typo={ETypo.TITLE_H1}>Informations des offices</Typography> <Typography typo={ETypo.TITLE_H1}>Informations des offices</Typography>

View File

@ -210,7 +210,7 @@ export default function UserInformations(props: IProps) {
}, [currentAppointment, getUser]); }, [currentAppointment, getUser]);
return ( return (
<DefaultUserDashboard mobileBackText={"Liste des utilisateurs"}> <DefaultUserDashboard>
{!isLoading && ( {!isLoading && (
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["header"]}> <div className={classes["header"]}>

View File

@ -9,7 +9,7 @@ type IState = {};
export default class Users extends BasePage<IProps, IState> { export default class Users extends BasePage<IProps, IState> {
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
<DefaultUserDashboard title={"Dossier"} mobileBackText={"Liste des utilisateurs"}> <DefaultUserDashboard title={"Dossier"}>
<div className={classes["root"]}> <div className={classes["root"]}>
<div className={classes["no-folder-selected"]}> <div className={classes["no-folder-selected"]}>
<Typography typo={ETypo.TITLE_H1}>Informations des utilisateurs</Typography> <Typography typo={ETypo.TITLE_H1}>Informations des utilisateurs</Typography>