Shared:Selectors:Groups: update types
This commit is contained in:
parent
dd5d5b48c9
commit
de84f7c1be
@ -1,158 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
import EmptyScreenGroupSvgUrl from "PUBLIC_DIR/images/empty_screen_groups_75-75.svg?url";
|
||||
import EmptyScreenGroupSvgDarkUrl from "PUBLIC_DIR/images/empty_screen_groups_dark_75-75.svg?url";
|
||||
|
||||
import api from "../../api";
|
||||
import { RowLoader, SearchLoader } from "../../skeletons/selector";
|
||||
import { Selector, TSelectorItem } from "../../components/selector";
|
||||
|
||||
import {
|
||||
GroupsSelectorItem,
|
||||
GroupsSelectorProps,
|
||||
} from "./GroupsSelector.types";
|
||||
|
||||
const GroupsSelector = (props: GroupsSelectorProps) => {
|
||||
const {
|
||||
id,
|
||||
|
||||
cancelButtonLabel,
|
||||
emptyScreenDescription,
|
||||
emptyScreenHeader,
|
||||
emptyScreenImage,
|
||||
headerLabel,
|
||||
isMultiSelect,
|
||||
onBackClick,
|
||||
onAccept,
|
||||
onCancel,
|
||||
onSelect,
|
||||
withCancelButton,
|
||||
withHeader,
|
||||
searchEmptyScreenDescription,
|
||||
searchEmptyScreenHeader,
|
||||
searchEmptyScreenImage,
|
||||
searchPlaceholder,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation(["GroupsSelector", "Common"]);
|
||||
const theme = useTheme();
|
||||
|
||||
const [isFirstLoad, setIsFirstLoad] = useState(true);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [hasNextPage, setHasNextPage] = useState(false);
|
||||
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [itemsList, setItemsList] = useState<GroupsSelectorItem[]>([]);
|
||||
|
||||
const emptyScreenImg = theme.isBase
|
||||
? EmptyScreenGroupSvgUrl
|
||||
: EmptyScreenGroupSvgDarkUrl;
|
||||
|
||||
const onSearchAction = (value: string, isSearchCallback?: Function) => {
|
||||
setSearchValue(() => {
|
||||
setIsFirstLoad(true);
|
||||
isSearchCallback?.(Boolean(value));
|
||||
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
const onClearSearchAction = () => {
|
||||
setSearchValue(() => {
|
||||
setIsFirstLoad(true);
|
||||
|
||||
return "";
|
||||
});
|
||||
};
|
||||
|
||||
const onAcceptAction = (items: TSelectorItem[]) => {
|
||||
onAccept?.(items);
|
||||
};
|
||||
|
||||
const onLoadNextPage = async (startIndex: number) => {
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
// Todo: fix types after TS API will be done
|
||||
const { items, total } = await api.groups.getGroupsByName(
|
||||
searchValue,
|
||||
startIndex,
|
||||
);
|
||||
|
||||
const convertedItems = items.map((group: any) => ({
|
||||
id: group.id,
|
||||
label: group.name,
|
||||
isGroup: true,
|
||||
}));
|
||||
|
||||
const oldItems = startIndex ? itemsList : [];
|
||||
const newItems = [...oldItems, ...convertedItems];
|
||||
|
||||
setHasNextPage(newItems.length < total);
|
||||
|
||||
setItemsList(newItems);
|
||||
if (isFirstLoad) {
|
||||
setTotal(total);
|
||||
setTimeout(() => {
|
||||
setIsFirstLoad(false);
|
||||
}, 500);
|
||||
} else {
|
||||
// setItems((value) => [...value, ...convertedItems]);
|
||||
}
|
||||
|
||||
setIsNextPageLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onLoadNextPage(0);
|
||||
}, [searchValue]);
|
||||
|
||||
return (
|
||||
<Selector
|
||||
id={id}
|
||||
headerLabel={headerLabel || t("Common:Groups")}
|
||||
onBackClick={onBackClick}
|
||||
searchPlaceholder={searchPlaceholder || t("Common:Search")}
|
||||
onSearch={onSearchAction}
|
||||
searchValue={searchValue}
|
||||
onClearSearch={onClearSearchAction}
|
||||
onSelect={onSelect}
|
||||
items={itemsList || []}
|
||||
acceptButtonLabel={t("Common:SelectAction")}
|
||||
onAccept={onAcceptAction}
|
||||
withHeader={withHeader}
|
||||
withCancelButton={withCancelButton}
|
||||
cancelButtonLabel={cancelButtonLabel || t("Common:CancelButton")}
|
||||
onCancel={onCancel}
|
||||
isMultiSelect={isMultiSelect}
|
||||
emptyScreenImage={emptyScreenImage || emptyScreenImg}
|
||||
emptyScreenHeader={emptyScreenHeader || t("GroupsNotFoundHeader")} // Todo: Update empty screen texts when they are ready
|
||||
emptyScreenDescription={
|
||||
emptyScreenDescription || t("GroupsNotFoundDescription")
|
||||
}
|
||||
searchEmptyScreenImage={searchEmptyScreenImage || emptyScreenImg}
|
||||
searchEmptyScreenHeader={
|
||||
searchEmptyScreenHeader || t("GroupsNotFoundHeader")
|
||||
}
|
||||
searchEmptyScreenDescription={
|
||||
searchEmptyScreenDescription || t("GroupsNotFoundDescription")
|
||||
}
|
||||
totalItems={total}
|
||||
hasNextPage={hasNextPage}
|
||||
isNextPageLoading={isNextPageLoading}
|
||||
loadNextPage={onLoadNextPage}
|
||||
isLoading={isFirstLoad}
|
||||
searchLoader={<SearchLoader />}
|
||||
rowLoader={
|
||||
<RowLoader
|
||||
isMultiSelect={isMultiSelect}
|
||||
isContainer={isFirstLoad}
|
||||
isUser={false}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupsSelector;
|
@ -1,28 +1,18 @@
|
||||
import { SelectorProps, TSelectorItem } from "../../components/selector";
|
||||
import {
|
||||
TAccessRight,
|
||||
TSelectorHeader,
|
||||
} from "../../components/selector/Selector.types";
|
||||
import { TSelectorItem } from "../../components/selector";
|
||||
|
||||
type PickedSelectorProps = Pick<
|
||||
SelectorProps,
|
||||
| "id"
|
||||
| "headerLabel"
|
||||
| "className"
|
||||
| "onBackClick"
|
||||
| "onCancel"
|
||||
| "isMultiSelect"
|
||||
| "cancelButtonLabel"
|
||||
| "emptyScreenDescription"
|
||||
| "emptyScreenHeader"
|
||||
| "emptyScreenImage"
|
||||
| "withCancelButton"
|
||||
| "withHeader"
|
||||
| "searchEmptyScreenDescription"
|
||||
| "searchEmptyScreenHeader"
|
||||
| "searchEmptyScreenImage"
|
||||
| "searchPlaceholder"
|
||||
>;
|
||||
|
||||
export interface GroupsSelectorProps extends PickedSelectorProps {
|
||||
onAccept?: (items: GroupsSelectorItem[]) => void;
|
||||
onSelect?: (item: GroupsSelectorItem) => void;
|
||||
}
|
||||
export type GroupsSelectorProps = TSelectorHeader & {
|
||||
id?: string;
|
||||
className?: string;
|
||||
onSubmit: (
|
||||
selectedItems: TSelectorItem[],
|
||||
access?: TAccessRight | null,
|
||||
fileName?: string,
|
||||
isFooterCheckboxChecked?: boolean,
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type GroupsSelectorItem = Pick<TSelectorItem, "id" | "label">;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import GroupsSelector from "./GroupsSelector";
|
||||
|
||||
export default GroupsSelector;
|
147
packages/shared/selectors/Groups/index.tsx
Normal file
147
packages/shared/selectors/Groups/index.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
import EmptyScreenGroupSvgUrl from "PUBLIC_DIR/images/empty_screen_groups_75-75.svg?url";
|
||||
import EmptyScreenGroupSvgDarkUrl from "PUBLIC_DIR/images/empty_screen_groups_dark_75-75.svg?url";
|
||||
|
||||
import api from "../../api";
|
||||
import { RowLoader, SearchLoader } from "../../skeletons/selector";
|
||||
import { Selector, TSelectorItem } from "../../components/selector";
|
||||
|
||||
import { GroupsSelectorProps } from "./GroupsSelector.types";
|
||||
|
||||
const GroupsSelector = (props: GroupsSelectorProps) => {
|
||||
const {
|
||||
id,
|
||||
className,
|
||||
|
||||
headerProps,
|
||||
|
||||
onSubmit,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation(["GroupsSelector", "Common"]);
|
||||
const theme = useTheme();
|
||||
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [hasNextPage, setHasNextPage] = useState(false);
|
||||
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
|
||||
const [itemsList, setItemsList] = useState<TSelectorItem[]>([]);
|
||||
const isFirstLoad = useRef(true);
|
||||
const afterSearch = useRef(false);
|
||||
const totalRef = useRef(0);
|
||||
|
||||
const emptyScreenImg = theme.isBase
|
||||
? EmptyScreenGroupSvgUrl
|
||||
: EmptyScreenGroupSvgDarkUrl;
|
||||
|
||||
const onSearch = useCallback((value: string, callback?: Function) => {
|
||||
isFirstLoad.current = true;
|
||||
afterSearch.current = true;
|
||||
setSearchValue(() => {
|
||||
return value;
|
||||
});
|
||||
callback?.();
|
||||
}, []);
|
||||
|
||||
const onClearSearch = useCallback((callback?: Function) => {
|
||||
isFirstLoad.current = true;
|
||||
afterSearch.current = true;
|
||||
setSearchValue(() => {
|
||||
return "";
|
||||
});
|
||||
callback?.();
|
||||
}, []);
|
||||
|
||||
const onSubmitAction = useCallback(
|
||||
(items: TSelectorItem[]) => {
|
||||
onSubmit?.(items);
|
||||
},
|
||||
[onSubmit],
|
||||
);
|
||||
|
||||
const onLoadNextPage = useCallback(
|
||||
async (startIndex: number) => {
|
||||
const pageCount = 100;
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
// Todo: fix types after TS API will be done
|
||||
const { items, total } = await api.groups.getGroupsByName(
|
||||
searchValue,
|
||||
startIndex,
|
||||
pageCount,
|
||||
);
|
||||
|
||||
const convertedItems: TSelectorItem[] = items.map((group) => ({
|
||||
id: group.id,
|
||||
label: group.name,
|
||||
isGroup: true,
|
||||
avatar: "",
|
||||
}));
|
||||
|
||||
if (isFirstLoad.current) {
|
||||
totalRef.current = total;
|
||||
setItemsList([...convertedItems]);
|
||||
setHasNextPage(convertedItems.length < total);
|
||||
|
||||
isFirstLoad.current = false;
|
||||
} else {
|
||||
setItemsList((value) => {
|
||||
const arr = [...value, ...convertedItems];
|
||||
setHasNextPage(arr.length < total);
|
||||
return arr;
|
||||
});
|
||||
isFirstLoad.current = false;
|
||||
}
|
||||
|
||||
setIsNextPageLoading(false);
|
||||
},
|
||||
[searchValue],
|
||||
);
|
||||
|
||||
return (
|
||||
<Selector
|
||||
id={id}
|
||||
className={className}
|
||||
withHeader
|
||||
headerProps={{
|
||||
...headerProps,
|
||||
headerLabel: headerProps?.headerLabel || t("Common:Groups"),
|
||||
}}
|
||||
withSearch
|
||||
searchPlaceholder={t("Common:Search")}
|
||||
onSearch={onSearch}
|
||||
searchValue={searchValue}
|
||||
onClearSearch={onClearSearch}
|
||||
isSearchLoading={false}
|
||||
disableSubmitButton={false}
|
||||
isMultiSelect={false}
|
||||
items={itemsList}
|
||||
submitButtonLabel={t("Common:SelectAction")}
|
||||
onSubmit={onSubmitAction}
|
||||
cancelButtonLabel={t("Common:CancelButton")}
|
||||
emptyScreenImage={emptyScreenImg}
|
||||
emptyScreenHeader={t("GroupsNotFoundHeader")} // Todo: Update empty screen texts when they are ready
|
||||
emptyScreenDescription={t("GroupsNotFoundDescription")}
|
||||
searchEmptyScreenImage={emptyScreenImg}
|
||||
searchEmptyScreenHeader={t("GroupsNotFoundHeader")}
|
||||
searchEmptyScreenDescription={t("GroupsNotFoundDescription")}
|
||||
totalItems={totalRef.current}
|
||||
hasNextPage={hasNextPage}
|
||||
isNextPageLoading={isNextPageLoading}
|
||||
loadNextPage={onLoadNextPage}
|
||||
isLoading={isFirstLoad.current}
|
||||
searchLoader={<SearchLoader />}
|
||||
rowLoader={
|
||||
<RowLoader
|
||||
isMultiSelect={false}
|
||||
isContainer={isFirstLoad.current}
|
||||
isUser={false}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupsSelector;
|
Loading…
Reference in New Issue
Block a user