Merged dev
This commit is contained in:
commit
55c58f702f
@ -1,22 +1,5 @@
|
||||
<svg width="44" height="56" viewBox="0 0 44 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3466_113133)">
|
||||
<path d="M43.8828 9.98633C43.9035 9.4084 43.5713 8.81895 42.8538 8.51041L24.3282 0.58968C22.8379 -0.0481226 21.0845 -0.0481226 19.5942 0.58968L1.17241 8.46666C0.443378 8.7798 0.0996282 9.37616 0.115778 9.96331V46.1454L4.25693 48.091L6.73008 46.9098L18.4499 51.7336V54.1858L22.0396 55.869L25.6617 54.1398V51.7544L37.3584 46.9397L39.8201 48.0887L43.8874 46.1477V9.98633H43.8828Z" fill="#C5B2D4"/>
|
||||
<path d="M22.0369 55.9998L18.3318 54.2614V51.8115L6.73196 47.036L4.2565 48.2172L0 46.2209V9.85003C0.0322987 9.21222 0.454488 8.6481 1.12584 8.36259L19.5499 0.483307C21.061 -0.163706 22.8651 -0.163706 24.374 0.483307L42.8995 8.40403C43.5617 8.68725 43.9723 9.24216 43.9977 9.87075V46.2232L39.8173 48.2195L37.3534 47.0682L25.7743 51.8345V54.2153L22.0369 55.9998ZM18.5625 54.1163L22.0369 55.7465L25.5459 54.0725V51.6825L37.3626 46.8173L39.8196 47.9639L43.7716 46.0781V10.101H43.767V9.98127C43.7924 9.39412 43.4325 8.88296 42.8096 8.61587L24.284 0.69514C22.8305 0.073455 21.0956 0.073455 19.6422 0.69514L1.21812 8.57212C0.583683 8.84382 0.216862 9.36189 0.233012 9.96055V10.078V46.0712L4.25881 47.9616L6.72966 46.7827L18.5671 51.6549V54.1117L18.5625 54.1163Z" fill="#4E1480"/>
|
||||
<path d="M18.0574 49.0688C17.5222 49.0688 16.9823 48.9629 16.4724 48.7441L4.4412 43.6325C2.94854 42.9993 1.98419 41.5441 1.98419 39.9254L1.97266 17.4319C1.97266 16.0711 2.65093 14.8116 3.79061 14.0633C4.93029 13.315 6.35835 13.1906 7.61108 13.7294L19.6539 18.9171C21.135 19.5549 22.0924 21.0078 22.0924 22.6195V45.037C22.0924 46.3932 21.4141 47.6504 20.2814 48.401C19.61 48.8454 18.8349 49.0711 18.0551 49.0711L18.0574 49.0688ZM6.00999 14.0863C5.36632 14.0863 4.72727 14.2728 4.17127 14.6366C3.22769 15.256 2.66246 16.3014 2.66477 17.4296L2.67631 39.9231C2.67631 41.2632 3.47685 42.4697 4.71343 42.9947L16.7447 48.1063C17.7805 48.5461 18.9617 48.4402 19.9007 47.8185C20.842 47.1991 21.4026 46.1561 21.4026 45.0324V22.6149C21.4026 21.2795 20.609 20.0775 19.3816 19.5479L7.33885 14.3603C6.91204 14.1761 6.45986 14.0863 6.00999 14.0863Z" fill="white"/>
|
||||
<path d="M3.09274 20.3118V19.1352C3.09274 19.1052 3.07428 19.0776 3.0466 19.0661L1.58855 18.4375C1.53779 18.4168 1.48242 18.4536 1.48242 18.5066V19.6832C1.48242 19.7131 1.50088 19.7407 1.52856 19.7523L2.98431 20.3808C3.03506 20.4016 3.09043 20.3647 3.09043 20.3118H3.09274Z" fill="white"/>
|
||||
<path d="M3.01696 20.7323C2.95929 20.7323 2.90392 20.7208 2.84855 20.6978L1.3928 20.0715C1.23823 20.0047 1.13672 19.8527 1.13672 19.6846V18.508C1.13672 18.3653 1.20824 18.234 1.3259 18.1558C1.44586 18.0775 1.59582 18.0637 1.72502 18.1212L3.18307 18.7498C3.33764 18.8166 3.43915 18.9686 3.43915 19.1366V20.3132C3.43915 20.456 3.36763 20.5872 3.24767 20.6655C3.17846 20.7116 3.09771 20.7346 3.01696 20.7346V20.7323ZM1.82883 19.505L2.74704 19.9011V19.3116L1.82883 18.9156V19.505Z" fill="white"/>
|
||||
<path d="M3.14743 38.7512V37.5746C3.14743 37.5447 3.12897 37.5171 3.10129 37.5055L1.64323 36.877C1.59248 36.8562 1.53711 36.8931 1.53711 36.946V38.1226C1.53711 38.1526 1.55557 38.1802 1.58325 38.1917L3.039 38.8203C3.08975 38.841 3.14512 38.8042 3.14512 38.7512H3.14743Z" fill="white"/>
|
||||
<path d="M3.0713 39.1721C3.01362 39.1721 2.95825 39.1606 2.90519 39.1376L1.44713 38.509C1.29256 38.4422 1.19336 38.2902 1.19336 38.1221V36.9455C1.19336 36.8051 1.26257 36.6715 1.38254 36.5933C1.5025 36.515 1.65015 36.5012 1.78166 36.5587L3.23971 37.1873C3.39428 37.2541 3.49349 37.4061 3.49349 37.5741V38.7507C3.49349 38.8935 3.42197 39.0247 3.30431 39.103C3.2351 39.1491 3.15435 39.1721 3.0713 39.1721ZM1.88317 37.9448L2.80137 38.3409V37.7514L1.88317 37.3554V37.9448Z" fill="white"/>
|
||||
<path d="M22.415 26.6204V25.4438C22.415 25.4138 22.3965 25.3862 22.3689 25.3747L20.9108 24.7461C20.8601 24.7254 20.8047 24.7622 20.8047 24.8152V25.9918C20.8047 26.0217 20.8231 26.0493 20.8508 26.0608L22.3066 26.6894C22.3573 26.7102 22.4127 26.6733 22.4127 26.6204H22.415Z" fill="white"/>
|
||||
<path d="M22.3392 27.0412C22.2816 27.0412 22.2262 27.0297 22.1708 27.0067L20.7151 26.3781C20.5605 26.3113 20.459 26.1594 20.459 25.9913V24.8147C20.459 24.6719 20.5305 24.5407 20.6482 24.4624C20.7681 24.3841 20.9158 24.3703 21.0473 24.4279L22.5053 25.0565C22.6599 25.1232 22.7614 25.2752 22.7614 25.4433V26.6199C22.7614 26.7626 22.6899 26.8939 22.5699 26.9722C22.5007 27.0182 22.42 27.0412 22.3392 27.0412ZM21.1511 25.814L22.0693 26.21V25.6206L21.1511 25.2245V25.814Z" fill="white"/>
|
||||
<path d="M22.4677 45.0618V43.8852C22.4677 43.8552 22.4493 43.8276 22.4216 43.8161L20.9635 43.1875C20.9128 43.1668 20.8574 43.2036 20.8574 43.2566V44.4332C20.8574 44.4631 20.8759 44.4907 20.9036 44.5023L22.3593 45.1308C22.4101 45.1516 22.4654 45.1147 22.4654 45.0618H22.4677Z" fill="white"/>
|
||||
<path d="M22.3916 45.4823C22.3339 45.4823 22.2786 45.4708 22.2232 45.4478L20.7674 44.8215C20.6129 44.7547 20.5137 44.6027 20.5137 44.4346V43.258C20.5137 43.1176 20.5829 42.984 20.7028 42.9058C20.8228 42.8275 20.9728 42.8137 21.102 42.8712L22.56 43.4998C22.7146 43.5666 22.8138 43.7186 22.8138 43.8866V45.0632C22.8138 45.206 22.7423 45.3372 22.6223 45.4155C22.5531 45.4616 22.4724 45.4846 22.3916 45.4846V45.4823ZM21.2035 44.255L22.1217 44.6511V44.0616L21.2035 43.6656V44.255Z" fill="white"/>
|
||||
<path d="M8.47164 24.259C6.02617 25.2099 5.44248 29.5617 7.16585 33.9803C8.88921 38.3989 12.269 41.2126 14.7145 40.2616C17.16 39.3107 17.7437 34.9589 16.0203 30.5403C14.2969 26.1217 10.9171 23.308 8.47164 24.259ZM12.5989 32.3478C12.5713 32.2442 12.5367 32.1406 12.4974 32.037C12.4144 31.8228 12.3129 31.6294 12.1975 31.4567L12.7835 27.0911C13.7248 28.0098 14.5876 29.3177 15.1944 30.8742C15.7827 32.3823 16.0364 33.8813 15.9857 35.1661L12.5989 32.3478ZM9.10377 25.804C9.92738 25.4839 10.8802 25.6935 11.8122 26.3036L11.2216 30.6946C11.1178 30.6808 11.0163 30.69 10.924 30.7268C10.8664 30.7498 10.8156 30.7821 10.7671 30.8212L7.43347 28.0467C7.70339 26.9437 8.26862 26.1263 9.10377 25.804ZM8.04945 33.651C7.45192 32.1152 7.19815 30.591 7.26044 29.29L10.458 31.9518C10.4811 32.205 10.5457 32.479 10.6518 32.753C10.7187 32.9234 10.7971 33.08 10.8825 33.2251L10.3334 37.3075C9.4406 36.398 8.62852 35.1362 8.04945 33.651ZM11.3001 38.1341L11.8445 34.077C11.976 34.1092 12.1052 34.1069 12.2229 34.0609C12.3752 34.001 12.4905 33.879 12.5713 33.7132L15.8219 36.4187C15.5566 37.5515 14.9867 38.3897 14.1377 38.7212C13.2772 39.0551 12.2713 38.811 11.2978 38.1341H11.3001Z" fill="white"/>
|
||||
<path d="M12.1448 31.9302C12.5924 32.6509 12.5878 33.4591 12.1656 33.7147C11.7434 33.9703 11.0167 33.595 10.6199 32.8466C10.1977 32.0523 10.1885 31.3339 10.583 31.0852C11.0421 30.7951 11.6904 31.1957 12.1471 31.9325L12.1448 31.9302Z" fill="#3FA79E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3466_113133">
|
||||
<rect width="44" height="56" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="256" height="256" rx="128" fill="white"/>
|
||||
<path d="M185.067 93.7936C185.647 94.7771 186.831 95.2561 187.94 94.9282L206.362 89.4058C207.345 89.1284 208 88.2206 208 87.2372V52.4634C208 49.9922 206.009 48 203.539 48H51.7297C49.4869 48 48.126 49.5131 48.126 51.7069L48 131.341V203.537C48 206.008 49.9909 208 52.4606 208H204.27C206.513 208 207.874 206.487 207.874 204.268L208 124.659V124.407C208 121.456 207.572 119.414 203.565 120.548L196.332 122.692C195.173 123.045 194.417 124.129 194.467 125.314L194.593 127.987C194.593 159.685 178.087 182.43 148.955 191.357C101.3 205.957 57.8283 168.536 61.6841 122.213C64.3554 90.4145 89.909 64.5925 121.662 61.6674C148.526 59.1962 172.442 72.6872 185.042 93.7431L185.067 93.7936Z" fill="#005BCB"/>
|
||||
<path d="M153.011 159.962C159.739 155.322 164.931 148.615 167.677 140.772C168.408 138.629 168.988 136.385 169.341 134.09C169.567 132.703 168.257 131.543 166.921 131.972L154.548 135.855C154.019 136.032 153.59 136.435 153.389 136.965C149.482 146.724 139.402 153.305 128.011 151.918C116.898 150.556 108.002 141.327 107.019 130.181C106.691 126.449 107.221 122.869 108.405 119.616C111.807 110.437 120.653 103.88 131.01 103.88C136.857 103.88 142.225 105.973 146.408 109.453C146.887 109.857 147.542 110.008 148.172 109.806L159.916 106.125C160.571 105.923 160.999 105.419 161.176 104.864C161.327 104.284 161.251 103.628 160.823 103.124C153.237 94.0206 141.544 88.4478 128.566 89.2295C128.415 89.2547 128.263 89.2799 128.112 89.2799L124.105 76.5202C123.601 74.8559 121.837 73.9228 120.174 74.4272L111.051 77.2767C109.388 77.8062 108.455 79.5714 108.985 81.2357L111.958 90.7425C112.488 92.4068 111.832 94.2224 110.371 95.1554C103.138 99.7448 97.4679 106.654 94.5194 114.824C93.3098 118.103 92.5538 121.582 92.3017 125.188C92.2261 126.525 92.2009 127.836 92.2513 129.122C92.3269 130.862 91.1929 132.426 89.5297 132.955L79.4997 136.183C77.8365 136.713 76.904 138.478 77.4081 140.142L80.2558 149.27C80.785 150.935 82.5491 151.842 84.2123 151.338L94.4186 148.035C96.0315 147.53 97.8207 148.11 98.7532 149.523C105.709 159.937 117.553 166.821 131.01 166.821C131.59 166.821 132.144 166.821 132.699 166.821C134.463 166.72 136.05 167.855 136.58 169.519L139.679 179.505C140.208 181.17 141.973 182.103 143.636 181.573L152.758 178.724C154.422 178.219 155.329 176.429 154.825 174.765L151.549 164.325C151.045 162.711 151.624 160.946 153.011 159.988V159.962Z" fill="#005BCB"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 2.4 KiB |
@ -3,7 +3,7 @@
|
||||
"name": "lecoffre",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "32x32 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.label {
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
}
|
||||
}
|
82
src/front/Components/DesignSystem/Autocomplete/index.tsx
Normal file
82
src/front/Components/DesignSystem/Autocomplete/index.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import DropdownMenu from "../Dropdown/DropdownMenu";
|
||||
import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
|
||||
import SearchBar from "../SearchBar";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
onSelect?: (option: IOption) => void;
|
||||
selectedOption?: IOption | null;
|
||||
};
|
||||
|
||||
export default function Autocomplete(props: IProps) {
|
||||
const { options, placeholder, disabled, label, selectedOption: selectedOptionProps } = props;
|
||||
const [selectedOption, setSelectedOption] = useState<IOption | null>(selectedOptionProps ?? 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(() => {
|
||||
setSelectedOption(selectedOptionProps ?? null);
|
||||
}, [selectedOptionProps]);
|
||||
|
||||
const handleSelectOption = useCallback(
|
||||
(option: IOption) => {
|
||||
setSelectedOption(option);
|
||||
setSearchValue(getLabel(option) || "");
|
||||
openable.close();
|
||||
},
|
||||
[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 (
|
||||
<DropdownMenu options={filteredOptions} openable={openable} onSelect={handleSelectOption} selectedOption={selectedOption}>
|
||||
<div className={classes["root"]}>
|
||||
{label && (
|
||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<SearchBar placeholder={placeholder} disabled={disabled} onChange={handleSearchChange} value={searchValue} />
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
import { IOption } from "../Form/SelectField";
|
||||
import Tooltip from "../ToolTip";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import classNames from "classnames";
|
||||
import { IOptionOld } from "../Form/SelectFieldOld";
|
||||
|
||||
type IProps = {
|
||||
name?: string;
|
||||
option: IOption;
|
||||
option: IOptionOld;
|
||||
toolTip?: string;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
checked: boolean;
|
||||
|
@ -0,0 +1,38 @@
|
||||
.root {
|
||||
display: flex;
|
||||
padding: var(--spacing-1, 8px) var(--spacing-2, 16px);
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
justify-content: space-between;
|
||||
|
||||
border-radius: var(--dropdown-radius, 0px);
|
||||
border: 1px solid var(--dropdown-border, rgba(0, 0, 0, 0));
|
||||
|
||||
background: var(--dropdown-option-background-default, #fff);
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dropdown-option-background-hovered);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
background-color: var(--dropdown-option-background-pressed);
|
||||
}
|
||||
|
||||
&[data-not-selectable="true"] {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
export type IOption = {
|
||||
id: string;
|
||||
label: string | { text: string; subtext: string };
|
||||
notSelectable?: boolean;
|
||||
};
|
||||
|
||||
type IProps = {
|
||||
option: IOption;
|
||||
isActive: boolean;
|
||||
onClick?: (option: IOption) => void;
|
||||
};
|
||||
|
||||
export default function DropdownOption(props: IProps) {
|
||||
const { option, onClick, isActive } = props;
|
||||
|
||||
const handleOnClick = useCallback(() => onClick && onClick(option), [onClick, option]);
|
||||
|
||||
return (
|
||||
<div className={classes["root"]} data-not-selectable={!!option.notSelectable} onClick={handleOnClick}>
|
||||
{getContent(option.label)}
|
||||
{isActive && <CheckIcon />}
|
||||
</div>
|
||||
);
|
||||
|
||||
function getContent(label: string | { text: string; subtext: string }) {
|
||||
if (typeof label === "string") {
|
||||
return (
|
||||
<Typography
|
||||
typo={isActive ? ETypo.TEXT_MD_SEMIBOLD : ETypo.TEXT_MD_REGULAR}
|
||||
color={isActive ? ETypoColor.DROPDOWN_CONTRAST_ACTIVE : ETypoColor.DROPDOWN_CONTRAST_DEFAULT}>
|
||||
{label}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={classes["content"]}>
|
||||
<Typography
|
||||
typo={ETypo.TEXT_MD_LIGHT}
|
||||
color={isActive ? ETypoColor.NAVIGATION_BUTTON_CONTRAST_ACTIVE : ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
|
||||
{label.text}
|
||||
</Typography>
|
||||
<Typography
|
||||
typo={ETypo.TEXT_MD_BOLD}
|
||||
color={isActive ? ETypoColor.NAVIGATION_BUTTON_CONTRAST_ACTIVE : ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
|
||||
{label.subtext}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
.root {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
z-index: 3;
|
||||
|
||||
padding: var(--spacing-sm, 8px);
|
||||
border-radius: var(--dropdown-radius, 0px);
|
||||
|
||||
background: var(--dropdown-menu-background, #fff);
|
||||
|
||||
border: 1px solid var(--dropdown-menu-border-primary, #005bcb);
|
||||
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
||||
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
|
||||
&.open {
|
||||
overflow: visible;
|
||||
.content {
|
||||
max-height: 500px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import classNames from "classnames";
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import DropdownOption, { IOption } from "./DropdownOption";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
selectedOption: IOption | null;
|
||||
children: React.ReactNode;
|
||||
openable: {
|
||||
isOpen: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
toggle: () => void;
|
||||
};
|
||||
onSelect?: (option: IOption) => void;
|
||||
};
|
||||
export default function DropdownMenu(props: IProps) {
|
||||
const { children, options, onSelect, openable, selectedOption } = props;
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(option: IOption) => {
|
||||
onSelect?.(option);
|
||||
openable.close();
|
||||
},
|
||||
[onSelect, openable],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames([classes["root"], openable.isOpen && classes["open"]])}>
|
||||
{children}
|
||||
<div className={classes["content"]}>
|
||||
{options.map((option) => {
|
||||
return <DropdownOption key={option.id} option={option} onClick={handleSelect} isActive={isActive(option)} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function isActive(option: IOption): boolean {
|
||||
return selectedOption?.id === option.id;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.label {
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
}
|
||||
.container {
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
|
||||
padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
|
||||
gap: var(--spacing-2, 16px);
|
||||
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--dropdown-input-border-default, #d7dce0);
|
||||
background: var(--dropdown-input-background, #fff);
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 24px;
|
||||
.value {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
align-items: center;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
stroke: var(--button-icon-button-default-default);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dropdown-input-border-hovered);
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: var(--dropdown-input-border-filled);
|
||||
}
|
||||
|
||||
&.open {
|
||||
border-color: var(--dropdown-input-border-expanded);
|
||||
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: var(--opacity-disabled, 0.3);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
74
src/front/Components/DesignSystem/Dropdown/index.tsx
Normal file
74
src/front/Components/DesignSystem/Dropdown/index.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
import classNames from "classnames";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import DropdownMenu from "./DropdownMenu";
|
||||
import { IOption } from "./DropdownMenu/DropdownOption";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
onSelect?: (option: IOption) => void;
|
||||
selectedOption?: IOption | null;
|
||||
};
|
||||
|
||||
export default function Dropdown(props: IProps) {
|
||||
const { options, placeholder, disabled, onSelect, selectedOption: selectedOptionProps, label } = props;
|
||||
const [selectedOption, setSelectedOption] = useState<IOption | null>(selectedOptionProps ?? null);
|
||||
const openable = useOpenable({ defaultOpen: false });
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedOption(selectedOptionProps ?? null);
|
||||
}, [selectedOptionProps]);
|
||||
|
||||
const handleOnSelect = useCallback(
|
||||
(option: IOption) => {
|
||||
setSelectedOption(option);
|
||||
onSelect?.(option);
|
||||
},
|
||||
[onSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenu options={options} openable={openable} onSelect={handleOnSelect} selectedOption={selectedOption}>
|
||||
<div className={classes["root"]}>
|
||||
{label && (
|
||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
<div
|
||||
className={classNames([
|
||||
classes["container"],
|
||||
openable.isOpen && classes["open"],
|
||||
disabled && classes["disabled"],
|
||||
!!selectedOption && classes["active"],
|
||||
])}
|
||||
onClick={openable.toggle}>
|
||||
<div className={classes["content"]}>
|
||||
<Typography
|
||||
className={classes["value"]}
|
||||
typo={!!selectedOption ? ETypo.TEXT_MD_SEMIBOLD : ETypo.TEXT_MD_REGULAR}
|
||||
color={!!selectedOption ? ETypoColor.DROPDOWN_CONTRAST_ACTIVE : ETypoColor.DROPDOWN_CONTRAST_DEFAULT}>
|
||||
{getLabel(selectedOption) ?? placeholder}
|
||||
</Typography>
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export 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}`;
|
||||
}
|
@ -1,23 +1,7 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
width: calc(100vh - 83px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
padding: 40px 24px 24px 24px;
|
||||
}
|
||||
|
||||
.folderlist-container {
|
||||
max-height: calc(100vh - 290px);
|
||||
height: calc(100vh - 290px);
|
||||
overflow: auto;
|
||||
border-right: 1px solid var(--color-neutral-200);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
.errors-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import Autocomplete from "../../Autocomplete";
|
||||
import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
|
||||
import BaseField, { IProps as IBaseFieldProps, IState as IBaseFieldState } from "../BaseField";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
export type IProps = IBaseFieldProps & {
|
||||
onSelect?: (option: IOption) => void;
|
||||
options: IOption[];
|
||||
selectedOption?: IOption | null;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
type IState = IBaseFieldState & {
|
||||
selectedOption: IOption | null;
|
||||
};
|
||||
|
||||
export default class AutocompleteField extends BaseField<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedOption: this.props.selectedOption ?? null,
|
||||
...this.getDefaultState(),
|
||||
};
|
||||
|
||||
this.handleOnChange = this.handleOnChange.bind(this);
|
||||
}
|
||||
|
||||
private handleOnChange = (option: IOption) => {
|
||||
this.setState({ selectedOption: option });
|
||||
this.props.onSelect?.(option);
|
||||
};
|
||||
|
||||
public override componentDidUpdate(prevProps: IProps): void {
|
||||
if (prevProps.selectedOption !== this.props.selectedOption) {
|
||||
this.setState({ selectedOption: this.props.selectedOption ?? null });
|
||||
}
|
||||
}
|
||||
|
||||
public override render(): ReactNode {
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<Autocomplete
|
||||
options={this.props.options}
|
||||
placeholder={this.props.placeholder}
|
||||
onSelect={this.handleOnChange}
|
||||
selectedOption={this.state.selectedOption}
|
||||
label={this.props.label}
|
||||
/>
|
||||
{this.state.selectedOption && (
|
||||
<input
|
||||
className={classes["hidden-input"]}
|
||||
name={this.props.name}
|
||||
type="text"
|
||||
defaultValue={this.state.selectedOption.id}
|
||||
hidden
|
||||
/>
|
||||
)}
|
||||
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ export type IProps = {
|
||||
label?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
export type IState = {
|
||||
value: string;
|
||||
validationError: ValidationError | null;
|
||||
};
|
||||
|
@ -1,143 +1,11 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
border: 1px solid var(--color-neutral-200);
|
||||
|
||||
&[data-errored="true"] {
|
||||
border: 1px solid var(--color-error-600);
|
||||
}
|
||||
|
||||
&[data-disabled="true"] {
|
||||
.container-label {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.container-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background-color: var(--color-generic-white);
|
||||
cursor: pointer;
|
||||
padding: 24px;
|
||||
z-index: 1;
|
||||
|
||||
&[data-border-right-collapsed="true"] {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.container-input chevron-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
margin-right: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
left: 8px;
|
||||
background-color: var(--color-generic-white);
|
||||
padding: 0 16px;
|
||||
|
||||
&[data-open="true"] {
|
||||
transform: translateY(-36px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chevron-icon {
|
||||
height: 24px;
|
||||
|
||||
fill: var(--color-neutral-500);
|
||||
transition: all 350ms $custom-easing;
|
||||
transform: rotate(90deg);
|
||||
|
||||
&[data-open="true"] {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-ul {
|
||||
padding-left: 24px;
|
||||
z-index: 3;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
transition: height 350ms $custom-easing, opacity 350ms $custom-easing;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
top: 50px;
|
||||
background-color: var(--color-generic-white);
|
||||
|
||||
&[data-open="false"] {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container-li {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-bottom: 24px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
background: var(--color-neutral-50);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--color-neutral-200);
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.token-icon {
|
||||
max-width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 11px;
|
||||
|
||||
> svg {
|
||||
height: 20px;
|
||||
margin-right: 11px;
|
||||
}
|
||||
|
||||
> img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
}
|
||||
.errors-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
@ -1,212 +1,65 @@
|
||||
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
||||
import WindowStore from "@Front/Stores/WindowStore";
|
||||
import { ValidationError } from "class-validator";
|
||||
import classNames from "classnames";
|
||||
import Image from "next/image";
|
||||
import React, { FormEvent, ReactNode } from "react";
|
||||
import React from "react";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import Typography, { ETypo, ETypoColor } from "../../Typography";
|
||||
import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
|
||||
import BaseField, { IProps as IBaseFieldProps, IState as IBaseFieldState } from "../BaseField";
|
||||
import classes from "./classes.module.scss";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
import Dropdown from "../../Dropdown";
|
||||
|
||||
type IProps = {
|
||||
selectedOption?: IOption;
|
||||
onChange?: (selectedOption: IOption) => void;
|
||||
export type IProps = IBaseFieldProps & {
|
||||
onSelect?: (option: IOption) => void;
|
||||
options: IOption[];
|
||||
hasBorderRightCollapsed?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
errors?: ValidationError;
|
||||
selectedOption?: IOption | null;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type IOption = {
|
||||
value: unknown;
|
||||
label: string;
|
||||
icon?: ReactNode;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
isOpen: boolean;
|
||||
listWidth: number;
|
||||
listHeight: number;
|
||||
type IState = IBaseFieldState & {
|
||||
selectedOption: IOption | null;
|
||||
errors: ValidationError | null;
|
||||
};
|
||||
|
||||
type IPropsClass = IProps & {
|
||||
router: NextRouter;
|
||||
};
|
||||
|
||||
class SelectFieldClass extends React.Component<IPropsClass, IState> {
|
||||
private contentRef = React.createRef<HTMLUListElement>();
|
||||
private rootRef = React.createRef<HTMLDivElement>();
|
||||
private removeOnresize = () => {};
|
||||
|
||||
static defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
constructor(props: IPropsClass) {
|
||||
export default class SelectField extends BaseField<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
listHeight: 0,
|
||||
listWidth: 0,
|
||||
selectedOption: null,
|
||||
errors: this.props.errors ?? null,
|
||||
};
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const selectedOption = this.state.selectedOption ?? this.props.selectedOption;
|
||||
return (
|
||||
<div className={classes["container"]}>
|
||||
<div
|
||||
className={classNames(classes["root"], this.props.className)}
|
||||
ref={this.rootRef}
|
||||
data-disabled={this.props.disabled?.toString()}
|
||||
data-errored={(this.state.errors !== null).toString()}>
|
||||
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
|
||||
<label
|
||||
className={classNames(classes["container-label"])}
|
||||
data-open={this.state.isOpen}
|
||||
onClick={this.toggle}
|
||||
data-border-right-collapsed={this.props.hasBorderRightCollapsed}>
|
||||
<div className={classNames(classes["container-input"])}>
|
||||
{selectedOption && (
|
||||
<>
|
||||
<span className={classNames(classes["icon"], classes["token-icon"])}>{selectedOption?.icon}</span>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>
|
||||
<span className={classes["text"]}>{selectedOption?.label}</span>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
{!selectedOption && (
|
||||
<div className={classes["placeholder"]} data-open={(selectedOption ? true : false).toString()}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR}>
|
||||
<span className={classes["text"]}>{this.props.placeholder ?? ""}</span>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Image className={classes["chevron-icon"]} data-open={this.state.isOpen} src={ChevronIcon} alt="chevron icon" />
|
||||
</label>
|
||||
|
||||
<ul
|
||||
className={classes["container-ul"]}
|
||||
data-open={this.state.isOpen}
|
||||
ref={this.contentRef}
|
||||
style={{
|
||||
height: this.state.listHeight + "px",
|
||||
}}>
|
||||
{this.props.options.map((option, index) => (
|
||||
<li
|
||||
key={`${index}-${option.value}`}
|
||||
className={classes["container-li"]}
|
||||
onClick={(e) => this.onSelect(option, e)}>
|
||||
<div className={classes["token-icon"]}>{option.icon}</div>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>{option.label}</Typography>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{this.state.isOpen && <div className={classes["backdrop"]} onClick={this.toggle} />}
|
||||
</div>
|
||||
{this.state.errors !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
public override componentDidMount(): void {
|
||||
this.onResize();
|
||||
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
|
||||
|
||||
this.props.router.events.on("routeChangeStart", () => {
|
||||
this.setState({
|
||||
isOpen: false,
|
||||
selectedOption: null,
|
||||
listHeight: 0,
|
||||
listWidth: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnresize();
|
||||
}
|
||||
|
||||
public override componentDidUpdate(prevProps: IProps) {
|
||||
if (this.props.errors !== prevProps.errors) {
|
||||
this.setState({
|
||||
errors: this.props.errors ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.selectedOption !== prevProps.selectedOption) {
|
||||
this.setState({
|
||||
selectedOption: this.props.selectedOption ?? null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: IProps, state: IState) {
|
||||
if (props.selectedOption?.value) {
|
||||
return {
|
||||
value: props.selectedOption?.value,
|
||||
...this.getDefaultState(),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
|
||||
this.handleOnChange = this.handleOnChange.bind(this);
|
||||
}
|
||||
|
||||
private onResize() {
|
||||
let listHeight = 0;
|
||||
let listWidth = 0;
|
||||
listWidth = this.rootRef.current?.scrollWidth ?? 0;
|
||||
if (this.state.listHeight) {
|
||||
listHeight = this.contentRef.current?.scrollHeight ?? 0;
|
||||
private handleOnChange = (option: IOption) => {
|
||||
this.setState({ selectedOption: option });
|
||||
this.props.onSelect?.(option);
|
||||
};
|
||||
|
||||
public override componentDidUpdate(prevProps: IProps): void {
|
||||
if (prevProps.selectedOption !== this.props.selectedOption) {
|
||||
this.setState({ selectedOption: this.props.selectedOption ?? null });
|
||||
}
|
||||
this.setState({ listHeight, listWidth });
|
||||
}
|
||||
|
||||
private toggle(e: FormEvent) {
|
||||
if (this.props.disabled) return;
|
||||
e.preventDefault();
|
||||
let listHeight = 0;
|
||||
let listWidth = this.rootRef.current?.scrollWidth ?? 0;
|
||||
|
||||
if (!this.state.listHeight) {
|
||||
listHeight = this.contentRef.current?.scrollHeight ?? 0;
|
||||
}
|
||||
|
||||
this.setState((state) => {
|
||||
return { isOpen: !state.isOpen, listHeight, listWidth };
|
||||
});
|
||||
}
|
||||
|
||||
private onSelect(option: IOption, e: React.MouseEvent<HTMLLIElement, MouseEvent>) {
|
||||
if (this.props.disabled) return;
|
||||
this.props.onChange && this.props.onChange(option);
|
||||
this.setState({
|
||||
selectedOption: option,
|
||||
});
|
||||
this.toggle(e);
|
||||
}
|
||||
|
||||
private renderErrors(): JSX.Element | null {
|
||||
if (!this.state.errors) return null;
|
||||
public override render(): ReactNode {
|
||||
return (
|
||||
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_ERROR_600}>
|
||||
{this.props.placeholder} ne peut pas être vide
|
||||
</Typography>
|
||||
<div className={classes["root"]}>
|
||||
<Dropdown
|
||||
options={this.props.options}
|
||||
placeholder={this.props.placeholder}
|
||||
onSelect={this.handleOnChange}
|
||||
selectedOption={this.state.selectedOption}
|
||||
label={this.props.label}
|
||||
/>
|
||||
{this.state.selectedOption && (
|
||||
<input
|
||||
className={classes["hidden-input"]}
|
||||
name={this.props.name}
|
||||
type="text"
|
||||
defaultValue={this.state.selectedOption.id}
|
||||
hidden
|
||||
/>
|
||||
)}
|
||||
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function SelectField(props: IProps) {
|
||||
const router = useRouter();
|
||||
return <SelectFieldClass {...props} router={router} />;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
.errors-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
212
src/front/Components/DesignSystem/Form/SelectFieldOld/index.tsx
Normal file
212
src/front/Components/DesignSystem/Form/SelectFieldOld/index.tsx
Normal file
@ -0,0 +1,212 @@
|
||||
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
||||
import WindowStore from "@Front/Stores/WindowStore";
|
||||
import { ValidationError } from "class-validator";
|
||||
import classNames from "classnames";
|
||||
import Image from "next/image";
|
||||
import React, { FormEvent, ReactNode } from "react";
|
||||
|
||||
import Typography, { ETypo, ETypoColor } from "../../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
|
||||
type IProps = {
|
||||
selectedOption?: IOptionOld;
|
||||
onChange?: (selectedOption: IOptionOld) => void;
|
||||
options: IOptionOld[];
|
||||
hasBorderRightCollapsed?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
errors?: ValidationError;
|
||||
};
|
||||
|
||||
export type IOptionOld = {
|
||||
value: unknown;
|
||||
label: string;
|
||||
icon?: ReactNode;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
isOpen: boolean;
|
||||
listWidth: number;
|
||||
listHeight: number;
|
||||
selectedOption: IOptionOld | null;
|
||||
errors: ValidationError | null;
|
||||
};
|
||||
|
||||
type IPropsClass = IProps & {
|
||||
router: NextRouter;
|
||||
};
|
||||
|
||||
class SelectFieldClass extends React.Component<IPropsClass, IState> {
|
||||
private contentRef = React.createRef<HTMLUListElement>();
|
||||
private rootRef = React.createRef<HTMLDivElement>();
|
||||
private removeOnresize = () => {};
|
||||
|
||||
static defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
constructor(props: IPropsClass) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
listHeight: 0,
|
||||
listWidth: 0,
|
||||
selectedOption: null,
|
||||
errors: this.props.errors ?? null,
|
||||
};
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const selectedOption = this.state.selectedOption ?? this.props.selectedOption;
|
||||
return (
|
||||
<div className={classes["container"]}>
|
||||
<div
|
||||
className={classNames(classes["root"], this.props.className)}
|
||||
ref={this.rootRef}
|
||||
data-disabled={this.props.disabled?.toString()}
|
||||
data-errored={(this.state.errors !== null).toString()}>
|
||||
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
|
||||
<label
|
||||
className={classNames(classes["container-label"])}
|
||||
data-open={this.state.isOpen}
|
||||
onClick={this.toggle}
|
||||
data-border-right-collapsed={this.props.hasBorderRightCollapsed}>
|
||||
<div className={classNames(classes["container-input"])}>
|
||||
{selectedOption && (
|
||||
<>
|
||||
<span className={classNames(classes["icon"], classes["token-icon"])}>{selectedOption?.icon}</span>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>
|
||||
<span className={classes["text"]}>{selectedOption?.label}</span>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
{!selectedOption && (
|
||||
<div className={classes["placeholder"]} data-open={(selectedOption ? true : false).toString()}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR}>
|
||||
<span className={classes["text"]}>{this.props.placeholder ?? ""}</span>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Image className={classes["chevron-icon"]} data-open={this.state.isOpen} src={ChevronIcon} alt="chevron icon" />
|
||||
</label>
|
||||
|
||||
<ul
|
||||
className={classes["container-ul"]}
|
||||
data-open={this.state.isOpen}
|
||||
ref={this.contentRef}
|
||||
style={{
|
||||
height: this.state.listHeight + "px",
|
||||
}}>
|
||||
{this.props.options.map((option, index) => (
|
||||
<li
|
||||
key={`${index}-${option.value}`}
|
||||
className={classes["container-li"]}
|
||||
onClick={(e) => this.onSelect(option, e)}>
|
||||
<div className={classes["token-icon"]}>{option.icon}</div>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>{option.label}</Typography>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{this.state.isOpen && <div className={classes["backdrop"]} onClick={this.toggle} />}
|
||||
</div>
|
||||
{this.state.errors !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
public override componentDidMount(): void {
|
||||
this.onResize();
|
||||
this.removeOnresize = WindowStore.getInstance().onResize(() => this.onResize());
|
||||
|
||||
this.props.router.events.on("routeChangeStart", () => {
|
||||
this.setState({
|
||||
isOpen: false,
|
||||
selectedOption: null,
|
||||
listHeight: 0,
|
||||
listWidth: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public override componentWillUnmount() {
|
||||
this.removeOnresize();
|
||||
}
|
||||
|
||||
public override componentDidUpdate(prevProps: IProps) {
|
||||
if (this.props.errors !== prevProps.errors) {
|
||||
this.setState({
|
||||
errors: this.props.errors ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.selectedOption !== prevProps.selectedOption) {
|
||||
this.setState({
|
||||
selectedOption: this.props.selectedOption ?? null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: IProps, state: IState) {
|
||||
if (props.selectedOption?.value) {
|
||||
return {
|
||||
value: props.selectedOption?.value,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private onResize() {
|
||||
let listHeight = 0;
|
||||
let listWidth = 0;
|
||||
listWidth = this.rootRef.current?.scrollWidth ?? 0;
|
||||
if (this.state.listHeight) {
|
||||
listHeight = this.contentRef.current?.scrollHeight ?? 0;
|
||||
}
|
||||
this.setState({ listHeight, listWidth });
|
||||
}
|
||||
|
||||
private toggle(e: FormEvent) {
|
||||
if (this.props.disabled) return;
|
||||
e.preventDefault();
|
||||
let listHeight = 0;
|
||||
let listWidth = this.rootRef.current?.scrollWidth ?? 0;
|
||||
|
||||
if (!this.state.listHeight) {
|
||||
listHeight = this.contentRef.current?.scrollHeight ?? 0;
|
||||
}
|
||||
|
||||
this.setState((state) => {
|
||||
return { isOpen: !state.isOpen, listHeight, listWidth };
|
||||
});
|
||||
}
|
||||
|
||||
private onSelect(option: IOptionOld, e: React.MouseEvent<HTMLLIElement, MouseEvent>) {
|
||||
if (this.props.disabled) return;
|
||||
this.props.onChange && this.props.onChange(option);
|
||||
this.setState({
|
||||
selectedOption: option,
|
||||
});
|
||||
this.toggle(e);
|
||||
}
|
||||
|
||||
private renderErrors(): JSX.Element | null {
|
||||
if (!this.state.errors) return null;
|
||||
return (
|
||||
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_ERROR_600}>
|
||||
{this.props.placeholder} ne peut pas être vide
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function SelectField(props: IProps) {
|
||||
const router = useRouter();
|
||||
return <SelectFieldClass {...props} router={router} />;
|
||||
}
|
@ -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 - 83px);
|
||||
|
||||
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,26 +1,24 @@
|
||||
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;
|
||||
export default function BurgerModal(props: IProps) {
|
||||
const { isOpen, closeModal } = props;
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes["background"]} onClick={this.props.closeModal} />
|
||||
<div className={classes["background"]} onClick={closeModal} />
|
||||
<div className={classes["root"]}>
|
||||
<Rules
|
||||
mode={RulesMode.OPTIONAL}
|
||||
@ -31,29 +29,36 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
]}>
|
||||
<>
|
||||
<NavigationLink
|
||||
path={Module.getInstance().get().modules.pages.Folder.props.path}
|
||||
text="Dossiers en cours"
|
||||
routesActive={[
|
||||
<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,
|
||||
]}
|
||||
],
|
||||
link: Module.getInstance().get().modules.pages.Folder.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]}
|
||||
|
||||
<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,
|
||||
}}
|
||||
/>
|
||||
<div className={classes["separator"]} />
|
||||
</>
|
||||
</Rules>
|
||||
|
||||
<BurgerModalSubmenu
|
||||
text={"Espace super admin"}
|
||||
links={[
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Espace super admin",
|
||||
dropdown: {
|
||||
items: [
|
||||
{
|
||||
text: "Gestion des utilisateurs",
|
||||
path: Module.getInstance().get().modules.pages.Users.props.path,
|
||||
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,
|
||||
@ -65,28 +70,20 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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={[
|
||||
|
||||
<MenuItem
|
||||
item={{
|
||||
text: "Espace office",
|
||||
hasSeparator: true,
|
||||
dropdown: {
|
||||
items: [
|
||||
{
|
||||
text: "Collaborateurs",
|
||||
path: Module.getInstance().get().modules.pages.Collaborators.props.path,
|
||||
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,
|
||||
@ -100,7 +97,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
{
|
||||
text: "Gestion des rôles",
|
||||
path: Module.getInstance().get().modules.pages.Roles.props.path,
|
||||
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,
|
||||
@ -115,7 +112,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
{
|
||||
text: "Paramétrage des listes de pièces",
|
||||
path: Module.getInstance().get().modules.pages.DeedTypes.props.path,
|
||||
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,
|
||||
@ -134,7 +131,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
{
|
||||
text: "RIB Office",
|
||||
path: Module.getInstance().get().modules.pages.OfficesRib.props.path,
|
||||
link: Module.getInstance().get().modules.pages.OfficesRib.props.path,
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
@ -144,7 +141,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
},
|
||||
{
|
||||
text: "Abonnement",
|
||||
path: Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
|
||||
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,
|
||||
@ -155,16 +152,45 @@ export default class BurgerModal extends React.Component<IProps, IState> {
|
||||
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" />
|
||||
|
||||
<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";
|
||||
|
||||
@ -175,6 +176,7 @@ const officeItems: IItem[] = [
|
||||
icon: <BanknotesIcon />,
|
||||
text: "RIB Office",
|
||||
link: Module.getInstance().get().modules.pages.OfficesRib.props.path,
|
||||
routesActive: [Module.getInstance().get().modules.pages.OfficesRib.props.path],
|
||||
rules: [
|
||||
{
|
||||
action: AppRuleActions.update,
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -6,7 +6,7 @@
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 75px;
|
||||
height: var(--header-height);
|
||||
padding: 0px var(--spacing-lg, 24px);
|
||||
|
||||
border-bottom: 1px solid var(--menu-border, #d7dce0);
|
||||
|
@ -23,6 +23,8 @@ type IProps = {
|
||||
isUserConnected: boolean;
|
||||
};
|
||||
|
||||
const headerHeight = 75;
|
||||
|
||||
export default function Header(props: IProps) {
|
||||
const { isUserConnected } = props;
|
||||
|
||||
@ -44,6 +46,7 @@ export default function Header(props: IProps) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty("--header-height", `${headerHeight}px`);
|
||||
loadSubscription();
|
||||
}, [loadSubscription]);
|
||||
|
||||
|
@ -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 {
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: all ease-in-out 0.1s;
|
||||
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,28 +1,28 @@
|
||||
import { ValidationError } from "class-validator";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select";
|
||||
|
||||
import { IOption } from "../Form/SelectField";
|
||||
import { IOptionOld } from "../Form/SelectFieldOld";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import { styles } from "./styles";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
options: IOptionOld[];
|
||||
label?: string | JSX.Element;
|
||||
placeholder?: string;
|
||||
onChange?: (newValue: MultiValue<IOption>, actionMeta: ActionMeta<IOption>) => void;
|
||||
defaultValue?: PropsValue<IOption>;
|
||||
value?: PropsValue<IOption>;
|
||||
onChange?: (newValue: MultiValue<IOptionOld>, actionMeta: ActionMeta<IOptionOld>) => void;
|
||||
defaultValue?: PropsValue<IOptionOld>;
|
||||
value?: PropsValue<IOptionOld>;
|
||||
isMulti?: boolean;
|
||||
shouldCloseMenuOnSelect: boolean;
|
||||
isOptionDisabled?: (option: IOption, selectValue: Options<IOption>) => boolean;
|
||||
isOptionDisabled?: (option: IOptionOld, selectValue: Options<IOptionOld>) => boolean;
|
||||
validationError?: ValidationError;
|
||||
};
|
||||
type IState = {
|
||||
isFocused: boolean;
|
||||
selectedOptions: MultiValue<IOption>;
|
||||
selectedOptions: MultiValue<IOptionOld>;
|
||||
validationError: ValidationError | null;
|
||||
};
|
||||
|
||||
@ -124,7 +124,7 @@ export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
this.setState({ isFocused: false });
|
||||
}
|
||||
|
||||
private onChange(newValue: MultiValue<IOption>, actionMeta: ActionMeta<IOption>) {
|
||||
private onChange(newValue: MultiValue<IOptionOld>, actionMeta: ActionMeta<IOptionOld>) {
|
||||
this.props.onChange && this.props.onChange(newValue, actionMeta);
|
||||
this.setState({
|
||||
selectedOptions: newValue,
|
||||
|
@ -1,6 +1,8 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
height: 56px;
|
||||
|
||||
display: flex;
|
||||
padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
|
||||
align-items: flex-start;
|
||||
@ -10,6 +12,10 @@
|
||||
border: 1px solid var(--input-main-border-default, #d7dce0);
|
||||
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);
|
||||
@ -28,8 +34,14 @@
|
||||
background: var(--input-background, #fff);
|
||||
}
|
||||
|
||||
&[data-is-disabled="true"] {
|
||||
opacity: var(--opacity-disabled, 0.3);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
|
@ -1,16 +1,18 @@
|
||||
import React, { useCallback } from "react";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
type IProps = {
|
||||
onChange?: (input: string) => void;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export default function SearchBar({ onChange, placeholder = "Rechercher" }: IProps) {
|
||||
export default function SearchBar({ onChange, value: propValue, placeholder = "Rechercher", disabled = false }: IProps) {
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const [value, setValue] = React.useState("");
|
||||
const [value, setValue] = React.useState(propValue || "");
|
||||
|
||||
const changeValue = useCallback(
|
||||
(value: string) => {
|
||||
@ -25,8 +27,14 @@ export default function SearchBar({ onChange, placeholder = "Rechercher" }: IPro
|
||||
const handleBlur = () => setIsFocused(false);
|
||||
const clearValue = () => changeValue("");
|
||||
|
||||
useEffect(() => {
|
||||
if (propValue !== undefined) {
|
||||
setValue(propValue);
|
||||
}
|
||||
}, [propValue]);
|
||||
|
||||
return (
|
||||
<label className={classes["root"]} data-is-focused={isFocused} data-has-value={value !== ""}>
|
||||
<label className={classes["root"]} data-is-focused={isFocused} data-has-value={value !== ""} data-is-disabled={disabled}>
|
||||
<div className={classes["input-container"]}>
|
||||
<MagnifyingGlassIcon width="24" height="24" />
|
||||
<input
|
||||
|
@ -1,35 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: inline-flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 24px;
|
||||
border: 1px solid var(--color-neutral-200);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-neutral-200);
|
||||
}
|
||||
|
||||
&[data-selected="true"] {
|
||||
background-color: var(--color-neutral-200);
|
||||
}
|
||||
|
||||
.left-side {
|
||||
display: inline-flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow-wrap: anywhere;
|
||||
.warning {
|
||||
margin-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.dropdown-header {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
|
||||
align-items: center;
|
||||
gap: var(--spacing-2, 16px);
|
||||
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--dropdown-input-border-hovered, #b4bec5);
|
||||
background: var(--dropdown-input-background, #fff);
|
||||
|
||||
.text-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--spacing-6, 48px);
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
> svg {
|
||||
transition: transform 200ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
padding: var(--spacing-sm, 8px);
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
align-self: stretch;
|
||||
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border-radius: var(--dropdown-radius, 0px);
|
||||
border: 1px solid var(--dropdown-menu-border-primary, #005bcb);
|
||||
background: var(--dropdown-menu-background, #fff);
|
||||
|
||||
.dropdown-item {
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-1, 8px) var(--spacing-2, 16px);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-is-opened="true"] {
|
||||
.dropdown-header {
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--dropdown-input-border-expanded, #005bcb);
|
||||
background: var(--dropdown-input-background, #fff);
|
||||
|
||||
> svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { IBlock } from "../BlockList/Block";
|
||||
import classes from "./classes.module.scss";
|
||||
import Typography, { ETypo, ETypoColor } from "../../Typography";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
type IProps = {
|
||||
blocks: IBlock[];
|
||||
onSelectedBlock: (block: IBlock) => void;
|
||||
defaultSelectedBlock?: IBlock;
|
||||
};
|
||||
|
||||
export default function DropdownNavigation({ blocks, onSelectedBlock, defaultSelectedBlock = blocks[0] }: IProps) {
|
||||
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(() => {
|
||||
if (defaultSelectedBlock) setSelectedBlock(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 && (
|
||||
<div className={classes["dropdown-content"]}>
|
||||
{blocks
|
||||
.filter((block) => block.id !== selectedBlock.id)
|
||||
.map((block) => (
|
||||
<div key={block.id} className={classes["dropdown-item"]} onClick={() => selectBlock(block.id)}>
|
||||
{block.secondaryText && (
|
||||
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
|
||||
{block.secondaryText}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography typo={ETypo.TEXT_MD_BOLD} color={ETypoColor.NAVIGATION_BUTTON_CONTRAST_DEFAULT}>
|
||||
{block.primaryText}
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
@import "@Themes/constants.scss";
|
||||
.root {
|
||||
width: 336px;
|
||||
height: 100%;
|
||||
@ -9,6 +10,11 @@
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
@media (max-width: $screen-m) {
|
||||
gap: 16px;
|
||||
padding: var(--spacing-lg, 24px);
|
||||
width: auto;
|
||||
}
|
||||
.block-list {
|
||||
overflow-y: auto;
|
||||
::-webkit-scrollbar {
|
||||
@ -16,13 +22,55 @@
|
||||
}
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
|
||||
@media (max-width: $screen-m) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.responsive-dropdown {
|
||||
display: none;
|
||||
|
||||
@media (max-width: $screen-m) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
padding: var(--spacing-md, 16px);
|
||||
@media (max-width: $screen-m) {
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
gap: var(--spacing-md, 16px);
|
||||
}
|
||||
|
||||
> label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.responsive-button-container {
|
||||
display: none;
|
||||
|
||||
@media (max-width: $screen-m) {
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-s) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
margin-top: auto;
|
||||
|
||||
@media (max-width: $screen-m) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-s) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import classes from "./classes.module.scss";
|
||||
import SearchBar from "../SearchBar";
|
||||
import Button from "../Button";
|
||||
import { useRouter } from "next/router";
|
||||
import DropdownNavigation from "./DropdownNavigation";
|
||||
|
||||
type IProps = {
|
||||
blocks: IBlock[];
|
||||
@ -17,6 +18,7 @@ type IProps = {
|
||||
export default function SearchBlockList(props: IProps) {
|
||||
const { blocks, onSelectedBlock, bottomButton } = props;
|
||||
|
||||
const [selectedBlock, setSelectedBlock] = useState<IBlock | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
const [blocksShowing, setBlocksShowing] = useState<IBlock[]>(blocks);
|
||||
@ -54,6 +56,14 @@ export default function SearchBlockList(props: IProps) {
|
||||
router.push(bottomButton.link);
|
||||
}, [bottomButton, router]);
|
||||
|
||||
const handleSelectedBlock = useCallback(
|
||||
(block: IBlock) => {
|
||||
setSelectedBlock(block);
|
||||
onSelectedBlock(block);
|
||||
},
|
||||
[onSelectedBlock],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setBlocksShowing(blocks);
|
||||
}, [blocks]);
|
||||
@ -62,9 +72,23 @@ export default function SearchBlockList(props: IProps) {
|
||||
<div className={classes["root"]}>
|
||||
<div className={classes["searchbar"]} ref={searchBarRef}>
|
||||
<SearchBar placeholder="Chercher" onChange={handleSearchChange} />
|
||||
{bottomButton && (
|
||||
<div className={classes["responsive-button-container"]}>
|
||||
<Button fullwidth onClick={redirectOnBottomButtonClick}>
|
||||
{bottomButton.text}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={classes["block-list"]} style={getHeight()}>
|
||||
<BlockList blocks={blocksShowing} onSelectedBlock={onSelectedBlock} />
|
||||
<BlockList blocks={blocksShowing} onSelectedBlock={handleSelectedBlock} />
|
||||
</div>
|
||||
<div className={classes["responsive-dropdown"]}>
|
||||
<DropdownNavigation
|
||||
blocks={blocksShowing}
|
||||
onSelectedBlock={handleSelectedBlock}
|
||||
defaultSelectedBlock={selectedBlock ?? undefined}
|
||||
/>
|
||||
</div>
|
||||
{bottomButton && (
|
||||
<div className={classes["bottom-container"]}>
|
||||
|
@ -156,6 +156,9 @@ export enum ETypoColor {
|
||||
CONTRAST_ACTIVED = "--contrast-actived",
|
||||
NAVIGATION_BUTTON_CONTRAST_DEFAULT = "--navigation-button-contrast-default",
|
||||
NAVIGATION_BUTTON_CONTRAST_ACTIVE = "--navigation-button-contrast-active",
|
||||
|
||||
DROPDOWN_CONTRAST_DEFAULT = "--dropdown-contrast-default",
|
||||
DROPDOWN_CONTRAST_ACTIVE = "--dropdown-contrast-active",
|
||||
}
|
||||
|
||||
export default function Typography(props: IProps) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200) solid;
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200) solid;
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200) solid;
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
.sides {
|
||||
min-height: calc(100vh - 83px);
|
||||
min-height: calc(100vh - var(--header-height));
|
||||
width: 50%;
|
||||
@media (max-width: $screen-m) {
|
||||
width: 100%;
|
||||
@ -31,7 +31,7 @@
|
||||
width: 50vw;
|
||||
top: 83px;
|
||||
right: 0;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
@media (max-width: $screen-m) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1,132 +1,23 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
@keyframes growWidth {
|
||||
0% {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 200%;
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
justify-content: flex-start;
|
||||
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;
|
||||
@media (max-width: $screen-m) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.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 - 83px);
|
||||
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;
|
||||
min-width: calc(100% - 336px);
|
||||
overflow-y: auto;
|
||||
padding: var(--spacing-lg, 24px);
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.background-image-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@media (max-width: $screen-l) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background-image {
|
||||
@media (max-width: $screen-m) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,11 @@
|
||||
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
||||
import Folders, { IGetFoldersParams } from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import FolderArchivedListContainer from "@Front/Components/DesignSystem/FolderArchivedListContainer";
|
||||
import FolderListContainer from "@Front/Components/DesignSystem/FolderListContainer";
|
||||
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 { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||
import classNames from "classnames";
|
||||
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
|
||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import Image, { StaticImageData } from "next/image";
|
||||
import React, { ReactNode } from "react";
|
||||
import React, { ReactNode, useEffect } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
@ -20,98 +13,20 @@ type IProps = {
|
||||
title: string;
|
||||
children?: ReactNode;
|
||||
isArchived?: boolean;
|
||||
onSelectedFolder: (folder: OfficeFolder) => void;
|
||||
hasBackArrow: boolean;
|
||||
onSelectedFolder?: (folder: OfficeFolder) => void;
|
||||
hasBackArrow?: boolean;
|
||||
backArrowUrl?: string;
|
||||
mobileBackText?: string;
|
||||
image?: StaticImageData;
|
||||
};
|
||||
type IState = {
|
||||
folders: OfficeFolder[] | null;
|
||||
isLeftSideOpen: boolean;
|
||||
leftSideCanBeClosed: boolean;
|
||||
};
|
||||
|
||||
export default class DefaultNotaryDashboard extends React.Component<IProps, IState> {
|
||||
private onWindowResize = () => {};
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
hasBackArrow: false,
|
||||
isArchived: false,
|
||||
};
|
||||
export default function DefaultNotaryDashboard(props: IProps) {
|
||||
const { hasBackArrow, onSelectedFolder, backArrowUrl, children, isArchived } = props;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
folders: null,
|
||||
isLeftSideOpen: false,
|
||||
leftSideCanBeClosed: typeof window !== "undefined" ? window.innerWidth < 1024 : false,
|
||||
};
|
||||
this.onOpenLeftSide = this.onOpenLeftSide.bind(this);
|
||||
this.onCloseLeftSide = this.onCloseLeftSide.bind(this);
|
||||
}
|
||||
const [folders, setFolders] = React.useState<OfficeFolder[]>([]);
|
||||
|
||||
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.props.isArchived && this.state.folders && (
|
||||
<FolderArchivedListContainer
|
||||
folders={this.state.folders}
|
||||
onSelectedFolder={this.props.onSelectedFolder}
|
||||
onCloseLeftSide={this.onCloseLeftSide}
|
||||
isArchived={true}
|
||||
/>
|
||||
)}
|
||||
{!this.props.isArchived && this.state.folders && (
|
||||
<FolderListContainer
|
||||
folders={this.state.folders}
|
||||
onSelectedFolder={this.props.onSelectedFolder}
|
||||
onCloseLeftSide={this.onCloseLeftSide}
|
||||
isArchived={false}
|
||||
/>
|
||||
)}
|
||||
</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>
|
||||
{this.props.image && (
|
||||
<div className={classes["background-image-container"]}>
|
||||
<Image alt={"right side image"} src={this.props.image} className={classes["background-image"]} priority />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Version />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public override async componentDidMount() {
|
||||
this.onWindowResize = WindowStore.getInstance().onResize((window) => this.onResize(window));
|
||||
useEffect(() => {
|
||||
let targetedStatus: EFolderStatus = EFolderStatus["LIVE" as keyof typeof EFolderStatus];
|
||||
if (this.props.isArchived) targetedStatus = EFolderStatus.ARCHIVED;
|
||||
if (isArchived) targetedStatus = EFolderStatus.ARCHIVED;
|
||||
const query: IGetFoldersParams = {
|
||||
q: {
|
||||
where: { status: targetedStatus },
|
||||
@ -143,27 +58,26 @@ export default class DefaultNotaryDashboard extends React.Component<IProps, ISta
|
||||
},
|
||||
};
|
||||
|
||||
const folders = await Folders.getInstance().get(query);
|
||||
this.setState({ folders: folders });
|
||||
}
|
||||
public override componentWillUnmount() {
|
||||
this.onWindowResize();
|
||||
}
|
||||
Folders.getInstance()
|
||||
.get(query)
|
||||
.then((folders) => setFolders(folders));
|
||||
}, [isArchived]);
|
||||
|
||||
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 });
|
||||
}
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<Header isUserConnected={true} />
|
||||
<div className={classes["content"]}>
|
||||
<FolderListContainer folders={folders} onSelectedFolder={onSelectedFolder} isArchived={isArchived ?? false} />
|
||||
<div className={classes["right-side"]}>
|
||||
{hasBackArrow && (
|
||||
<div className={classes["back-arrow-desktop"]}>
|
||||
<BackArrow url={backArrowUrl ?? ""} />
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<Version />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200) solid;
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200) solid;
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -66,7 +66,7 @@
|
||||
justify-content: center;
|
||||
min-width: 56px;
|
||||
max-width: 56px;
|
||||
height: calc(100vh - 83px);
|
||||
height: calc(100vh - var(--header-height));
|
||||
border-right: 1px var(--color-neutral-200 solid);
|
||||
|
||||
@media (min-width: $screen-m) {
|
||||
|
@ -3,7 +3,6 @@ import OfficeRoles from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
|
||||
import Roles from "@Front/Api/LeCoffreApi/Admin/Roles/Roles";
|
||||
import Users from "@Front/Api/LeCoffreApi/Admin/Users/Users";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import Switch from "@Front/Components/DesignSystem/Switch";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
@ -15,6 +14,9 @@ import { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||
import { getLabel } from "@Front/Components/DesignSystem/Dropdown";
|
||||
import SelectField from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
|
||||
type IProps = {};
|
||||
export default function CollaboratorInformations(props: IProps) {
|
||||
@ -44,7 +46,7 @@ export default function CollaboratorInformations(props: IProps) {
|
||||
const closeRoleModal = useCallback(() => {
|
||||
setRoleModalOpened(false);
|
||||
setSelectedOption({
|
||||
value: userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid,
|
||||
id: (userSelected?.office_role ? userSelected?.office_role?.uid : userSelected?.role?.uid) ?? "",
|
||||
label: userSelected?.office_role ? userSelected?.office_role?.name : "Utilisateur restreint",
|
||||
});
|
||||
}, [userSelected?.office_role, userSelected?.role?.uid]);
|
||||
@ -55,7 +57,7 @@ export default function CollaboratorInformations(props: IProps) {
|
||||
User.hydrate<User>({
|
||||
uid: userSelected?.uid as string,
|
||||
office_role: OfficeRole.hydrate<OfficeRole>({
|
||||
uid: selectedOption?.value as string,
|
||||
uid: selectedOption?.id as string,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
@ -134,10 +136,10 @@ export default function CollaboratorInformations(props: IProps) {
|
||||
|
||||
const roles = await OfficeRoles.getInstance().get();
|
||||
if (!roles) return;
|
||||
setAvailableRoles(roles.map((role) => ({ value: role.uid, label: role.name })));
|
||||
setAvailableRoles(roles.map((role) => ({ id: role.uid ?? "", label: role.name })));
|
||||
setUserSelected(user);
|
||||
setSelectedOption({
|
||||
value: user?.office_role ? user?.office_role?.uid : user?.role?.uid,
|
||||
id: (user?.office_role ? user?.office_role?.uid : user?.role?.uid) ?? "",
|
||||
label: user?.office_role ? user?.office_role?.name : "Utilisateur restreint",
|
||||
});
|
||||
}
|
||||
@ -206,8 +208,8 @@ export default function CollaboratorInformations(props: IProps) {
|
||||
options={availableRoles.filter((role) => {
|
||||
return role.label !== "admin";
|
||||
})}
|
||||
selectedOption={selectedOption!}
|
||||
onChange={handleRoleChange}
|
||||
onSelect={handleRoleChange}
|
||||
selectedOption={selectedOption}
|
||||
/>
|
||||
</div>
|
||||
{userSelected?.role?.name !== "super-admin" && (
|
||||
@ -226,7 +228,7 @@ export default function CollaboratorInformations(props: IProps) {
|
||||
cancelText={"Annuler"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Attribuer le rôle de <span className={classes["role-name"]}>{selectedOption?.label}</span> à{" "}
|
||||
Attribuer le rôle de <span className={classes["role-name"]}>{getLabel(selectedOption)}</span> à{" "}
|
||||
{userSelected?.contact?.first_name} {userSelected?.contact?.last_name} ?
|
||||
</Typography>
|
||||
</div>
|
||||
|
@ -4,9 +4,10 @@ import DeedTypes from "@Front/Api/LeCoffreApi/Admin/DeedTypes/DeedTypes";
|
||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Admin/DocumentTypes/DocumentTypes";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import Tooltip from "@Front/Components/DesignSystem/ToolTip";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
|
||||
import Module from "@Front/Config/Module";
|
||||
@ -19,7 +20,6 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { MultiValue } from "react-select";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import Tooltip from "@Front/Components/DesignSystem/ToolTip";
|
||||
|
||||
type IProps = {};
|
||||
export default function DeedTypesInformations(props: IProps) {
|
||||
@ -28,7 +28,7 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
|
||||
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
|
||||
const [availableDocuments, setAvailableDocuments] = useState<DocumentType[]>([]);
|
||||
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
|
||||
const [selectedDocuments, setSelectedDocuments] = useState<IOptionOld[]>([]);
|
||||
|
||||
const [isDeleteModalOpened, setIsDeleteModalOpened] = useState<boolean>(false);
|
||||
const [isSaveModalOpened, setIsSaveModalOpened] = useState<boolean>(false);
|
||||
@ -71,7 +71,7 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
setDeedTypeSelected(deedType);
|
||||
|
||||
if (!deedType.document_types) return;
|
||||
const documentsOptions: IOption[] = deedType.document_types
|
||||
const documentsOptions: IOptionOld[] = deedType.document_types
|
||||
?.map((documentType) => {
|
||||
return {
|
||||
label: documentType.name,
|
||||
@ -106,11 +106,11 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
closeSaveModal();
|
||||
}, [closeSaveModal, deedTypeUid, selectedDocuments]);
|
||||
|
||||
const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
|
||||
setSelectedDocuments(values as IOption[]);
|
||||
const onDocumentChangeHandler = useCallback((values: MultiValue<IOptionOld>) => {
|
||||
setSelectedDocuments(values as IOptionOld[]);
|
||||
}, []);
|
||||
|
||||
const formattedOptions: IOption[] = availableDocuments
|
||||
const formattedOptions: IOptionOld[] = availableDocuments
|
||||
.map((document) => ({
|
||||
label: document.name,
|
||||
value: document.uid,
|
||||
|
@ -3,6 +3,7 @@
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
.components {
|
||||
max-width: 600px;
|
||||
.inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -1,12 +1,17 @@
|
||||
import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
|
||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
||||
import Dropdown from "@Front/Components/DesignSystem/Dropdown";
|
||||
import Footer from "@Front/Components/DesignSystem/Footer";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
|
||||
import Menu from "@Front/Components/DesignSystem/Menu";
|
||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||
import Newsletter from "@Front/Components/DesignSystem/Newsletter";
|
||||
import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
|
||||
import DropdownNavigation from "@Front/Components/DesignSystem/SearchBlockList/DropdownNavigation";
|
||||
import Table from "@Front/Components/DesignSystem/Table";
|
||||
import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
@ -14,7 +19,6 @@ import NumberPicker from "@Front/Components/Elements/NumberPicker";
|
||||
import Tabs from "@Front/Components/Elements/Tabs";
|
||||
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import Footer from "@Front/Components/DesignSystem/Footer";
|
||||
import {
|
||||
ArchiveBoxIcon,
|
||||
ArrowLongLeftIcon,
|
||||
@ -27,7 +31,7 @@ import {
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import Menu from "@Front/Components/DesignSystem/Menu";
|
||||
import Autocomplete from "@Front/Components/DesignSystem/Autocomplete";
|
||||
import CheckboxesInputElement from "@Front/Components/DesignSystem/CheckBox";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
|
||||
@ -78,9 +82,128 @@ export default function DesignSystem() {
|
||||
return (
|
||||
<DefaultTemplate title={"DesignSystem"}>
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>DesignSystem</Typography>
|
||||
<Newsletter isOpen />
|
||||
<Newsletter isOpen={false} />
|
||||
<div className={classes["root"]}>
|
||||
<div />
|
||||
<div className={classes["components"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete</Typography>
|
||||
<Autocomplete
|
||||
options={[
|
||||
{
|
||||
id: "1",
|
||||
label: "Option 1",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Option 2",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Option 3",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: { text: "Option 4", subtext: "Subtext" },
|
||||
},
|
||||
]}
|
||||
placeholder="Placeholder"
|
||||
label="Label"
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography>
|
||||
<Dropdown
|
||||
options={[
|
||||
{
|
||||
id: "1",
|
||||
label: "Option 1",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Option 2",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Option 3",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: { text: "Option 4", subtext: "Subtext" },
|
||||
},
|
||||
]}
|
||||
placeholder="Placeholder"
|
||||
label="Label"
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Navigation latérale</Typography>
|
||||
<SearchBlockList
|
||||
blocks={[
|
||||
{
|
||||
id: "1",
|
||||
primaryText: "Folder 1",
|
||||
isActive: true,
|
||||
secondaryText: "Secondary folder 1",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
primaryText: "Folder 2",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 2",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
primaryText: "Folder 3",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 3",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
primaryText: "Folder 4",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 4",
|
||||
showAlert: false,
|
||||
},
|
||||
]}
|
||||
onSelectedBlock={() => {}}
|
||||
bottomButton={{
|
||||
link: "/",
|
||||
text: "Créer un dossier",
|
||||
}}
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown navigation</Typography>
|
||||
<DropdownNavigation
|
||||
blocks={[
|
||||
{
|
||||
id: "1",
|
||||
primaryText: "Folder 1",
|
||||
isActive: true,
|
||||
secondaryText: "Secondary folder 1",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
primaryText: "Folder 2",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 2",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
primaryText: "Folder 3",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 3",
|
||||
showAlert: false,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
primaryText: "Folder 4",
|
||||
isActive: false,
|
||||
secondaryText: "Secondary folder 4",
|
||||
showAlert: false,
|
||||
},
|
||||
]}
|
||||
onSelectedBlock={() => {}}
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Button icon with menu</Typography>
|
||||
<Menu
|
||||
items={[
|
||||
|
@ -2,7 +2,7 @@ import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
|
||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
@ -27,8 +27,8 @@ type IProps = {};
|
||||
type IState = {
|
||||
selectedOption: ESelectedOption;
|
||||
availableCustomers: Customer[];
|
||||
selectedCustomers: readonly IOption[];
|
||||
existingCustomers: IOption[];
|
||||
selectedCustomers: readonly IOptionOld[];
|
||||
existingCustomers: IOptionOld[];
|
||||
isLoaded: boolean;
|
||||
validationError: ValidationError[];
|
||||
};
|
||||
@ -54,7 +54,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
this.onFormSubmit = this.onFormSubmit.bind(this);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
const selectOptions: IOption[] = this.getSelectedOptions();
|
||||
const selectOptions: IOptionOld[] = this.getSelectedOptions();
|
||||
|
||||
const backwardPath = Module.getInstance()
|
||||
.get()
|
||||
@ -153,7 +153,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
public override async componentDidMount() {
|
||||
const query = {};
|
||||
const availableCustomers = await Customers.getInstance().get(query);
|
||||
let preExistingCustomers: IOption[] | undefined = await this.getFolderPreSelectedCustomers(this.props.selectedFolderUid);
|
||||
let preExistingCustomers: IOptionOld[] | undefined = await this.getFolderPreSelectedCustomers(this.props.selectedFolderUid);
|
||||
const existingCustomers = preExistingCustomers ?? [];
|
||||
|
||||
existingCustomers.forEach((customer) => {
|
||||
@ -169,7 +169,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
this.setState({ availableCustomers, existingCustomers, isLoaded: true, selectedOption });
|
||||
}
|
||||
|
||||
private async getFolderPreSelectedCustomers(folderUid: string): Promise<IOption[] | undefined> {
|
||||
private async getFolderPreSelectedCustomers(folderUid: string): Promise<IOptionOld[] | undefined> {
|
||||
const query = {
|
||||
q: {
|
||||
customers: {
|
||||
@ -179,7 +179,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
},
|
||||
},
|
||||
};
|
||||
let preExistingCustomers: IOption[] = [];
|
||||
let preExistingCustomers: IOptionOld[] = [];
|
||||
try {
|
||||
const folder = await Folders.getInstance().getByUid(folderUid, query);
|
||||
preExistingCustomers = folder.customers!.map((customer) => {
|
||||
@ -195,7 +195,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
return preExistingCustomers;
|
||||
}
|
||||
|
||||
private getSelectedOptions(): IOption[] {
|
||||
private getSelectedOptions(): IOptionOld[] {
|
||||
let options = this.state.availableCustomers?.map((customer) => {
|
||||
return {
|
||||
label: customer.contact?.first_name + " " + customer.contact?.last_name,
|
||||
@ -206,7 +206,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
return options;
|
||||
}
|
||||
|
||||
private onMutiSelectChange(selectedCustomers: readonly IOption[]): void {
|
||||
private onMutiSelectChange(selectedCustomers: readonly IOptionOld[]): void {
|
||||
this.setState({ selectedCustomers });
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import classes from "./classes.module.scss";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
||||
import { DocumentType, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import Deeds from "@Front/Api/LeCoffreApi/Notary/Deeds/Deeds";
|
||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import { MultiValue } from "react-select";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import { DocumentType, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
||||
import { MultiValue } from "react-select";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
isCreateDocumentModalVisible: boolean;
|
||||
@ -23,13 +24,13 @@ export default function ParameterDocuments(props: IProps) {
|
||||
|
||||
const [addOrEditDocument, setAddOrEditDocument] = useState<"add" | "edit">("edit");
|
||||
|
||||
const [selectedDocuments, setSelectedDocuments] = useState<IOption[]>([]);
|
||||
const [formattedOptions, setFormattedOptions] = useState<IOption[]>([]);
|
||||
const [selectedDocuments, setSelectedDocuments] = useState<IOptionOld[]>([]);
|
||||
const [formattedOptions, setFormattedOptions] = useState<IOptionOld[]>([]);
|
||||
|
||||
const getAvailableDocuments = useCallback(async () => {
|
||||
const documents = await DocumentTypes.getInstance().get({});
|
||||
|
||||
const formattedOptions: IOption[] = documents
|
||||
const formattedOptions: IOptionOld[] = documents
|
||||
.filter((document) => {
|
||||
return !props.folder.deed?.document_types?.some((documentType) => documentType.uid === document.uid);
|
||||
})
|
||||
@ -51,8 +52,8 @@ export default function ParameterDocuments(props: IProps) {
|
||||
setDocumentName(event.target.value);
|
||||
};
|
||||
|
||||
const onDocumentChangeHandler = useCallback((values: MultiValue<IOption>) => {
|
||||
setSelectedDocuments(values as IOption[]);
|
||||
const onDocumentChangeHandler = useCallback((values: MultiValue<IOptionOld>) => {
|
||||
setSelectedDocuments(values as IOptionOld[]);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
|
||||
import Module from "@Front/Config/Module";
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
import React from "react";
|
||||
@ -16,6 +15,7 @@ import React from "react";
|
||||
import BasePage from "../../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
import ParameterDocuments from "./ParameterDocuments";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
|
||||
type IProps = {};
|
||||
type IPropsClass = IProps & {
|
||||
@ -25,7 +25,7 @@ type IPropsClass = IProps & {
|
||||
};
|
||||
type IState = {
|
||||
isCreateDocumentModalVisible: boolean;
|
||||
documentTypes: IOption[];
|
||||
documentTypes: IOptionOld[];
|
||||
folder: OfficeFolder | null;
|
||||
};
|
||||
|
||||
@ -138,7 +138,7 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private async getAvailableDocuments(folder: OfficeFolder): Promise<IOption[]> {
|
||||
private async getAvailableDocuments(folder: OfficeFolder): Promise<IOptionOld[]> {
|
||||
// Getting already asked documents UIDs in an array
|
||||
const userDocumentTypesUids = folder
|
||||
.documents!.filter((document) => document.depositor!.uid! === this.props.customerUid!)
|
||||
@ -157,7 +157,7 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
if (!documentTypes) return [];
|
||||
|
||||
// Else, return an array document types formatted as IOPtions
|
||||
const documentTypesOptions: IOption[] = documentTypes.map((documentType) => {
|
||||
const documentTypesOptions: IOptionOld[] = documentTypes.map((documentType) => {
|
||||
return {
|
||||
label: documentType!.name!,
|
||||
value: documentType!.uid!,
|
||||
|
@ -3,25 +3,27 @@ import DeedTypes from "@Front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes";
|
||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Users from "@Front/Api/LeCoffreApi/Notary/Users/Users";
|
||||
import Button from "@Front/Components/DesignSystem/Button";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import SelectField from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import { ValidationError } from "class-validator/types/validation/ValidationError";
|
||||
import { Deed, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import User from "le-coffre-resources/dist/Notary";
|
||||
import { DeedType } from "le-coffre-resources/dist/Notary";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { MultiValue } from "react-select";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import { DeedType } from "le-coffre-resources/dist/Notary";
|
||||
import { MultiValue } from "react-select";
|
||||
|
||||
export default function CreateFolder(): JSX.Element {
|
||||
/**
|
||||
@ -87,7 +89,7 @@ export default function CreateFolder(): JSX.Element {
|
||||
const radioOnChange = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFolderAccessType(e.target.value as "whole_office" | "select_collaborators");
|
||||
|
||||
const onSelectedCollaboratorsChange = (values: MultiValue<IOption>) => {
|
||||
const onSelectedCollaboratorsChange = (values: MultiValue<IOptionOld>) => {
|
||||
const selectedCollaborators = availableCollaborators.filter((collaborator) =>
|
||||
values.some((value) => value.value === collaborator.uid),
|
||||
);
|
||||
@ -142,16 +144,18 @@ export default function CreateFolder(): JSX.Element {
|
||||
validationError={validationError.find((error) => error.property === "name")}
|
||||
/>
|
||||
<SelectField
|
||||
name="deed"
|
||||
label="Type d'acte"
|
||||
placeholder="Sélectionner un type d'acte"
|
||||
options={
|
||||
availableDeedTypes.map((deedType) => ({
|
||||
id: deedType.uid,
|
||||
label: deedType.name,
|
||||
value: deedType.uid,
|
||||
})) as IOption[]
|
||||
}
|
||||
name="deed"
|
||||
placeholder={"Type d'acte"}
|
||||
errors={validationError.find((error) => error.property === "deed")}
|
||||
validationError={validationError.find((error) => error.property === "deed")}
|
||||
/>
|
||||
|
||||
<TextAreaField
|
||||
name="description"
|
||||
label="Note de dossier"
|
||||
@ -186,7 +190,7 @@ export default function CreateFolder(): JSX.Element {
|
||||
availableCollaborators.map((collaborator) => ({
|
||||
label: collaborator.contact?.last_name.concat(" ", collaborator.contact.first_name),
|
||||
value: collaborator.uid,
|
||||
})) as IOption[]
|
||||
})) as IOptionOld[]
|
||||
}
|
||||
defaultValue={selectedCollaborators.map((collaborator) => ({
|
||||
label: collaborator.contact?.last_name.concat(" ", collaborator.contact.first_name) as string,
|
||||
|
@ -28,10 +28,17 @@ export default function ClientView(props: IProps) {
|
||||
|
||||
const customers: ICustomer[] = useMemo(
|
||||
() =>
|
||||
folder?.customers?.map((customer) => ({
|
||||
folder?.customers
|
||||
?.map((customer) => ({
|
||||
id: customer.uid ?? "",
|
||||
...customer,
|
||||
})) ?? [],
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.documents &&
|
||||
a.documents.filter((document) => document.document_status === EDocumentStatus.DEPOSITED).length > 0
|
||||
? -1
|
||||
: 1;
|
||||
}) ?? [],
|
||||
[folder],
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1,22 +1,22 @@
|
||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Users, { IGetUsersParams } from "@Front/Api/LeCoffreApi/Notary/Users/Users";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import Module from "@Front/Config/Module";
|
||||
import { ValidationError } from "class-validator";
|
||||
import User from "le-coffre-resources/dist/Notary/User";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import { ValidationError } from "class-validator";
|
||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
enum ERadioBoxValue {
|
||||
ALL = "all",
|
||||
@ -28,7 +28,7 @@ export default function UpdateFolderCollaborators() {
|
||||
let { folderUid } = router.query;
|
||||
|
||||
const [availableCollaborators, setAvailableCollaborators] = useState<User[]>([]);
|
||||
const [selectedCollaborators, setSelectedCollaborators] = useState<readonly IOption[]>([]);
|
||||
const [selectedCollaborators, setSelectedCollaborators] = useState<readonly IOptionOld[]>([]);
|
||||
const [defaultCheckedAllOffice, setDefaultCheckedAllOffice] = useState<boolean>(false);
|
||||
const [selectedOption, setSelectedOption] = useState<ERadioBoxValue>(ERadioBoxValue.SELECTION);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
@ -61,7 +61,7 @@ export default function UpdateFolderCollaborators() {
|
||||
setSelectedOption(ERadioBoxValue.ALL);
|
||||
}, []);
|
||||
|
||||
const onChangeSelectedCollaborators = useCallback((selectedCollaborators: readonly IOption[]) => {
|
||||
const onChangeSelectedCollaborators = useCallback((selectedCollaborators: readonly IOptionOld[]) => {
|
||||
setSelectedCollaborators(selectedCollaborators);
|
||||
}, []);
|
||||
|
||||
@ -86,7 +86,7 @@ export default function UpdateFolderCollaborators() {
|
||||
let folder = null;
|
||||
try {
|
||||
folder = await Folders.getInstance().getByUid(folderUid, query);
|
||||
const preSelectedCollaborators: IOption[] = folder.stakeholders!.map((collaborator) => {
|
||||
const preSelectedCollaborators: IOptionOld[] = folder.stakeholders!.map((collaborator) => {
|
||||
return {
|
||||
label: collaborator.contact?.first_name + " " + collaborator.contact?.last_name,
|
||||
value: collaborator.uid,
|
||||
@ -126,7 +126,7 @@ export default function UpdateFolderCollaborators() {
|
||||
|
||||
const foldersInformationPath = Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path;
|
||||
const backwardPath = foldersInformationPath.replace("[folderUid]", folderUid as string);
|
||||
const selectOptions: IOption[] = availableCollaborators.map((collaborator) => {
|
||||
const selectOptions: IOptionOld[] = availableCollaborators.map((collaborator) => {
|
||||
return {
|
||||
label: collaborator.contact?.first_name + " " + collaborator.contact?.last_name,
|
||||
value: collaborator.uid,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import Select, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import Select, { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
@ -81,7 +81,7 @@ export default function UpdateFolderMetadata() {
|
||||
const deedOption = {
|
||||
label: selectedFolder?.deed?.deed_type?.name,
|
||||
value: selectedFolder?.deed?.deed_type?.uid,
|
||||
} as IOption;
|
||||
} as IOptionOld;
|
||||
const openingDate = new Date(selectedFolder?.created_at ?? "");
|
||||
if (!selectedFolder?.created_at) return <></>;
|
||||
const defaultValue = openingDate.toISOString().split("T")[0];
|
||||
|
@ -1,4 +1,3 @@
|
||||
import backgroundImage from "@Assets/images/background_refonte_reverse.svg";
|
||||
import LogoIcon from "@Assets/logo_small_blue.svg";
|
||||
import Users from "@Front/Api/LeCoffreApi/Notary/Users/Users";
|
||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
@ -60,11 +59,7 @@ export default function Folder() {
|
||||
}, [router]);
|
||||
|
||||
return (
|
||||
<DefaultNotaryDashboard
|
||||
title={"Dossier"}
|
||||
onSelectedFolder={onSelectedFolder}
|
||||
mobileBackText={"Liste des dossiers"}
|
||||
image={backgroundImage}>
|
||||
<DefaultNotaryDashboard title={"Dossier"} onSelectedFolder={onSelectedFolder} mobileBackText={"Liste des dossiers"}>
|
||||
<div className={classes["root"]}>
|
||||
<div className={classes["content"]}>
|
||||
<div className={classes["title-container"]}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import Select, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||
import Select, { IOptionOld } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
@ -18,7 +18,7 @@ type IProps = {
|
||||
};
|
||||
type IState = {
|
||||
selectedFolder: OfficeFolder | null;
|
||||
selectedOption?: IOption;
|
||||
selectedOption?: IOptionOld;
|
||||
};
|
||||
class UpdateFolderMetadataClass extends BasePage<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
@ -74,7 +74,7 @@ class UpdateFolderMetadataClass extends BasePage<IProps, IState> {
|
||||
);
|
||||
}
|
||||
|
||||
private onSelectedOption(option: IOption) {
|
||||
private onSelectedOption(option: IOptionOld) {
|
||||
this.setState({
|
||||
selectedOption: option,
|
||||
});
|
||||
|
@ -46,13 +46,6 @@ a,
|
||||
a:visited {
|
||||
color: initial;
|
||||
text-decoration: none !important;
|
||||
opacity: 1;
|
||||
transition: opacity 0.15s ease-in-out;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.react-select__input-container {
|
||||
|
Loading…
x
Reference in New Issue
Block a user