Shared:Selectors:Groups: update types

This commit is contained in:
Timofey Boyko 2024-02-21 12:27:45 +03:00
parent dd5d5b48c9
commit de84f7c1be
4 changed files with 162 additions and 186 deletions

View File

@ -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;

View File

@ -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">;

View File

@ -1,3 +0,0 @@
import GroupsSelector from "./GroupsSelector";
export default GroupsSelector;

View 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;