✨ Searchbar fully working
This commit is contained in:
parent
9dedb60b69
commit
4960b51ee3
@ -2,8 +2,8 @@ import Module from "@Front/Config/Module";
|
|||||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
|
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { NextRouter, useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React from "react";
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
|
||||||
import BlockList, { IBlock } from "../BlockList";
|
import BlockList, { IBlock } from "../BlockList";
|
||||||
import Button from "../Button";
|
import Button from "../Button";
|
||||||
@ -19,110 +19,17 @@ type IProps = {
|
|||||||
onCloseLeftSide?: () => void;
|
onCloseLeftSide?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IPropsClass = IProps & {
|
export default function FolderListContainer(props: IProps) {
|
||||||
router: NextRouter;
|
const router = useRouter();
|
||||||
selectedFolder: string;
|
const { folderUid } = router.query;
|
||||||
};
|
const { folders, isArchived } = props;
|
||||||
|
|
||||||
type IState = {
|
const redirectPath: string = isArchived
|
||||||
filteredFolders: OfficeFolder[];
|
|
||||||
blocks: IBlock[];
|
|
||||||
};
|
|
||||||
|
|
||||||
class FolderListContainerClass extends React.Component<IPropsClass, IState> {
|
|
||||||
private redirectPath: string = this.props.isArchived
|
|
||||||
? Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.pages.FolderInformation.props.path
|
? Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.pages.FolderInformation.props.path
|
||||||
: Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path;
|
: Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path;
|
||||||
public constructor(props: IPropsClass) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
filteredFolders: this.props.folders,
|
|
||||||
blocks: this.getBlocks(this.props.folders),
|
|
||||||
};
|
|
||||||
this.filterFolders = this.filterFolders.bind(this);
|
|
||||||
this.onSelectedFolder = this.onSelectedFolder.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override render(): JSX.Element {
|
const filterFolders = (value: string): void => {
|
||||||
const navigatePath = Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path;
|
const filteredFolders: OfficeFolder[] = folders.filter((folder) => {
|
||||||
return (
|
|
||||||
<div className={classes["root"]}>
|
|
||||||
<div className={classes["header"]}>
|
|
||||||
<div className={classes["searchbar"]}>
|
|
||||||
<SearchBar onChange={this.filterFolders} placeholder="Chercher un dossier" />
|
|
||||||
</div>
|
|
||||||
<div className={classes["folderlist-container"]}>
|
|
||||||
<BlockList blocks={this.state.blocks} onSelectedBlock={this.onSelectedFolder} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{!this.props.isArchived && (
|
|
||||||
<div>
|
|
||||||
<Rules
|
|
||||||
mode={RulesMode.NECESSARY}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
action: AppRuleActions.create,
|
|
||||||
name: AppRuleNames.officeFolders,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Link href={navigatePath}>
|
|
||||||
<Button fullwidth={true}>Créer un dossier</Button>
|
|
||||||
</Link>
|
|
||||||
</Rules>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override componentDidUpdate(prevProps: Readonly<IPropsClass>, prevState: Readonly<IState>, snapshot?: any): void {
|
|
||||||
if (prevProps.selectedFolder !== this.props.selectedFolder) {
|
|
||||||
this.setState({ filteredFolders: this.props.folders, blocks: this.getBlocks(this.props.folders) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private getBlocks(folders: OfficeFolder[]): IBlock[] {
|
|
||||||
const pendingFolders = folders
|
|
||||||
.filter((folder) => {
|
|
||||||
const pendingDocuments = (folder.documents ?? []).filter(
|
|
||||||
(document) => document.document_status === EDocumentStatus.DEPOSITED,
|
|
||||||
);
|
|
||||||
return pendingDocuments.length >= 1;
|
|
||||||
})
|
|
||||||
.sort((folder1, folder2) => {
|
|
||||||
return folder1.created_at! > folder2.created_at! ? -1 : 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
const otherFolders = folders
|
|
||||||
.filter((folder) => {
|
|
||||||
const pendingDocuments = (folder.documents ?? []).filter(
|
|
||||||
(document) => document.document_status === EDocumentStatus.DEPOSITED,
|
|
||||||
);
|
|
||||||
return pendingDocuments.length === 0;
|
|
||||||
})
|
|
||||||
.sort((folder1, folder2) => {
|
|
||||||
return folder1.created_at! > folder2.created_at! ? -1 : 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...pendingFolders, ...otherFolders].map((folder) => {
|
|
||||||
return {
|
|
||||||
id: folder.uid!,
|
|
||||||
name: folder.folder_number! + " - " + folder.name!,
|
|
||||||
selected: this.props.selectedFolder === folder.uid,
|
|
||||||
hasFlag: folder.documents?.some((document) => document.document_status === EDocumentStatus.DEPOSITED),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSelectedFolder(block: IBlock) {
|
|
||||||
const folder = this.props.folders.find((folder) => folder.uid === block.id);
|
|
||||||
if (!folder) return;
|
|
||||||
this.props.onSelectedFolder && this.props.onSelectedFolder(folder);
|
|
||||||
const path = this.redirectPath.replace("[folderUid]", folder.uid ?? "");
|
|
||||||
this.props.router.push(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private filterFolders(value: string): void {
|
|
||||||
const filteredFolders: OfficeFolder[] = this.props.folders.filter((folder) => {
|
|
||||||
const name = folder.name.toLowerCase();
|
const name = folder.name.toLowerCase();
|
||||||
const number = folder.folder_number.toLowerCase();
|
const number = folder.folder_number.toLowerCase();
|
||||||
value = value.toLowerCase();
|
value = value.toLowerCase();
|
||||||
@ -139,12 +46,86 @@ class FolderListContainerClass extends React.Component<IPropsClass, IState> {
|
|||||||
return name.includes(value) || number.includes(value);
|
return name.includes(value) || number.includes(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({ filteredFolders, blocks: this.getBlocks(filteredFolders) });
|
setBlocks(getBlocks(filteredFolders));
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default function FolderListContainer(props: IProps) {
|
const getBlocks = useCallback(
|
||||||
const router = useRouter();
|
(folders: OfficeFolder[]): IBlock[] => {
|
||||||
const { folderUid } = router.query;
|
const pendingFolders = folders
|
||||||
return <FolderListContainerClass {...props} router={router} selectedFolder={folderUid as string} />;
|
.filter((folder) => {
|
||||||
|
const pendingDocuments = (folder.documents ?? []).filter(
|
||||||
|
(document) => document.document_status === EDocumentStatus.DEPOSITED,
|
||||||
|
);
|
||||||
|
return pendingDocuments.length >= 1;
|
||||||
|
})
|
||||||
|
.sort((folder1, folder2) => {
|
||||||
|
return folder1.created_at! > folder2.created_at! ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const otherFolders = folders
|
||||||
|
.filter((folder) => {
|
||||||
|
const pendingDocuments = (folder.documents ?? []).filter(
|
||||||
|
(document) => document.document_status === EDocumentStatus.DEPOSITED,
|
||||||
|
);
|
||||||
|
return pendingDocuments.length === 0;
|
||||||
|
})
|
||||||
|
.sort((folder1, folder2) => {
|
||||||
|
return folder1.created_at! > folder2.created_at! ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...pendingFolders, ...otherFolders].map((folder) => {
|
||||||
|
return {
|
||||||
|
id: folder.uid!,
|
||||||
|
name: folder.folder_number! + " - " + folder.name!,
|
||||||
|
selected: folderUid === folder.uid,
|
||||||
|
hasFlag: folder.documents?.some((document) => document.document_status === EDocumentStatus.DEPOSITED),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[folderUid],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [blocks, setBlocks] = React.useState<IBlock[]>(getBlocks(folders));
|
||||||
|
|
||||||
|
const onSelectedFolder = (block: IBlock) => {
|
||||||
|
const folder = folders.find((folder) => folder.uid === block.id);
|
||||||
|
if (!folder) return;
|
||||||
|
props.onSelectedFolder && props.onSelectedFolder(folder);
|
||||||
|
const path = redirectPath.replace("[folderUid]", folder.uid ?? "");
|
||||||
|
router.push(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setBlocks(getBlocks(folders));
|
||||||
|
}, [folders, getBlocks]);
|
||||||
|
|
||||||
|
const navigatePath = Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path;
|
||||||
|
return (
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<div className={classes["header"]}>
|
||||||
|
<div className={classes["searchbar"]}>
|
||||||
|
<SearchBar onChange={filterFolders} placeholder="Chercher un dossier" />
|
||||||
|
</div>
|
||||||
|
<div className={classes["folderlist-container"]}>
|
||||||
|
<BlockList blocks={blocks} onSelectedBlock={onSelectedFolder} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!isArchived && (
|
||||||
|
<div>
|
||||||
|
<Rules
|
||||||
|
mode={RulesMode.NECESSARY}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
action: AppRuleActions.create,
|
||||||
|
name: AppRuleNames.officeFolders,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Link href={navigatePath}>
|
||||||
|
<Button fullwidth={true}>Créer un dossier</Button>
|
||||||
|
</Link>
|
||||||
|
</Rules>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,66 @@
|
|||||||
|
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
|
||||||
padding-left: 24px;
|
align-items: flex-start;
|
||||||
background-color: var(--color-generic-white);
|
gap: var(--spacing-2, 16px);
|
||||||
border: 1px solid var(--color-neutral-200);
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.fake-placeholder {
|
border-radius: var(--input-radius, 0px);
|
||||||
position: absolute;
|
border: 1px solid var(--input-main-border-default, #d7dce0);
|
||||||
left: 47px;
|
background: var(--input-background, #fff);
|
||||||
top: 24px;
|
|
||||||
color: var(--color-neutral-500);
|
&:hover {
|
||||||
pointer-events: none;
|
border-radius: var(--input-radius, 0px);
|
||||||
|
border: 1px solid var(--input-main-border-hovered, #b4bec5);
|
||||||
|
background: var(--input-background, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
&[data-has-value="true"] {
|
||||||
border: 0;
|
border-radius: var(--input-radius, 0px);
|
||||||
margin-left: 8px;
|
border: 1px solid var(--input-main-border-filled, #6d7e8a);
|
||||||
padding: 24px 0;
|
background: var(--input-background, #fff);
|
||||||
width: 100%;
|
}
|
||||||
font-family: "Inter", sans-serif;
|
|
||||||
font-style: normal;
|
&[data-is-focused="true"] {
|
||||||
font-weight: 400;
|
border-radius: var(--input-radius, 0px);
|
||||||
font-size: 18px;
|
border: 1px solid var(--input-main-border-focused, #005bcb);
|
||||||
line-height: 22px;
|
background: var(--input-background, #fff);
|
||||||
color: var(--color-neutral-500);
|
}
|
||||||
|
|
||||||
|
.input-container {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0px var(--spacing-2, 16px);
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
color: var(--input-placeholder-filled, #24282e);
|
||||||
|
|
||||||
|
/* text/md/semibold */
|
||||||
|
font-family: var(--font-text-family, Poppins);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: var(--font-text-weight-semibold, 600);
|
||||||
|
line-height: normal;
|
||||||
|
letter-spacing: 0.08px;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--input-placeholder-empty, #6d7e8a);
|
||||||
|
/* text/md/regular */
|
||||||
|
font-family: var(--font-text-family, Poppins);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: var(--font-text-weight-regular, 400);
|
||||||
|
line-height: normal;
|
||||||
|
letter-spacing: 0.08px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,53 @@
|
|||||||
import LoopIcon from "@Assets/Icons/loop.svg";
|
import React, { useCallback } from "react";
|
||||||
import Image from "next/image";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
onChange?: (input: string) => void;
|
onChange?: (input: string) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = {
|
export default function SearchBar({ onChange, placeholder = "Rechercher" }: IProps) {
|
||||||
hasValue: boolean;
|
const [isFocused, setIsFocused] = React.useState(false);
|
||||||
};
|
const [value, setValue] = React.useState("");
|
||||||
|
|
||||||
export default class SearchBar extends React.Component<IProps, IState> {
|
const handleOnChange = useCallback(
|
||||||
static defaultProps = {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
placeholder: "Search",
|
setValue(event.target.value);
|
||||||
};
|
onChange && onChange(event.target.value);
|
||||||
|
},
|
||||||
|
[onChange],
|
||||||
|
);
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
const handleFocus = useCallback(() => {
|
||||||
super(props);
|
setIsFocused(true);
|
||||||
this.state = {
|
}, []);
|
||||||
hasValue: false,
|
|
||||||
};
|
const handleBlur = useCallback(() => {
|
||||||
this.doesInputHaveValue = this.doesInputHaveValue.bind(this);
|
setIsFocused(false);
|
||||||
this.onChange = this.onChange.bind(this);
|
}, []);
|
||||||
}
|
|
||||||
public override render(): JSX.Element {
|
const clearValue = useCallback(() => {
|
||||||
return (
|
setValue("");
|
||||||
<div className={classes["root"]}>
|
onChange && onChange("");
|
||||||
<Image src={LoopIcon} alt="Loop icon" />
|
}, [onChange]);
|
||||||
{!this.state.hasValue && (
|
|
||||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_ERROR_600}>
|
return (
|
||||||
<div className={classes["fake-placeholder"]}>{this.props.placeholder}</div>
|
<label className={classes["root"]} data-is-focused={isFocused} data-has-value={value !== ""}>
|
||||||
</Typography>
|
<div className={classes["input-container"]}>
|
||||||
)}
|
<MagnifyingGlassIcon width="24" height="24" />
|
||||||
<input type="text" placeholder="" className={classes["input"]} onChange={this.onChange} />
|
<input
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={classes["input"]}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
{value !== "" && <XMarkIcon width="24" height="24" className={classes["cross"]} onClick={clearValue} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
</label>
|
||||||
}
|
);
|
||||||
|
|
||||||
private onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const hasValue = event.target.value.length > 0;
|
|
||||||
this.doesInputHaveValue(hasValue);
|
|
||||||
if (!this.props.onChange) return;
|
|
||||||
this.props.onChange(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private doesInputHaveValue(hasValue: boolean) {
|
|
||||||
this.setState({ hasValue });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user