umami/src/components/common/DataTable.tsx

90 lines
2.7 KiB
TypeScript
Raw Normal View History

2023-12-02 04:27:59 +00:00
import { ReactNode } from 'react';
2023-09-29 12:29:22 +00:00
import classNames from 'classnames';
import { Loading, SearchField } from 'react-basics';
2024-08-02 04:05:43 +00:00
import { useMessages, useNavigation } from 'components/hooks';
2023-08-25 18:54:44 +00:00
import Empty from 'components/common/Empty';
import Pager from 'components/common/Pager';
import { PagedQueryResult } from 'lib/types';
2024-05-08 05:47:39 +00:00
import styles from './DataTable.module.css';
import { LoadingPanel } from 'components/common/LoadingPanel';
2023-08-25 18:54:44 +00:00
const DEFAULT_SEARCH_DELAY = 600;
2023-08-25 18:54:44 +00:00
2023-09-29 12:29:22 +00:00
export interface DataTableProps {
queryResult: PagedQueryResult<any>;
2023-09-29 12:29:22 +00:00
searchDelay?: number;
2023-10-08 05:58:46 +00:00
allowSearch?: boolean;
allowPaging?: boolean;
renderEmpty?: () => ReactNode;
2023-09-29 12:29:22 +00:00
children: ReactNode | ((data: any) => ReactNode);
}
2023-08-25 18:54:44 +00:00
export function DataTable({
2023-10-04 08:46:00 +00:00
queryResult,
searchDelay = 600,
2023-10-08 05:58:46 +00:00
allowSearch = true,
allowPaging = true,
renderEmpty,
2023-08-25 18:54:44 +00:00
children,
2023-09-29 12:29:22 +00:00
}: DataTableProps) {
2023-08-25 18:54:44 +00:00
const { formatMessage, labels, messages } = useMessages();
2023-12-02 04:27:59 +00:00
const {
result,
params,
setParams,
query: { error, isLoading, isFetched },
2023-12-02 04:27:59 +00:00
} = queryResult || {};
2023-10-04 08:46:00 +00:00
const { page, pageSize, count, data } = result || {};
const { query } = params || {};
const hasData = Boolean(!isLoading && data?.length);
2024-08-08 05:39:36 +00:00
const noResults = Boolean(query && !hasData);
2024-08-02 04:05:43 +00:00
const { router, renderUrl } = useNavigation();
2023-08-25 18:54:44 +00:00
2023-12-03 11:07:03 +00:00
const handleSearch = (query: string) => {
2023-10-15 22:52:34 +00:00
setParams({ ...params, query, page: params.page ? page : 1 });
2023-08-25 18:54:44 +00:00
};
2023-12-03 11:07:03 +00:00
const handlePageChange = (page: number) => {
2023-10-15 22:52:34 +00:00
setParams({ ...params, query, page });
2024-08-02 04:05:43 +00:00
router.push(renderUrl({ page }));
2023-08-25 18:54:44 +00:00
};
return (
<>
2023-10-08 05:58:46 +00:00
{allowSearch && (hasData || query) && (
2023-08-25 18:54:44 +00:00
<SearchField
className={styles.search}
value={query}
2023-12-03 11:07:03 +00:00
onSearch={handleSearch}
2023-08-25 18:54:44 +00:00
delay={searchDelay || DEFAULT_SEARCH_DELAY}
autoFocus={true}
placeholder={formatMessage(labels.search)}
/>
)}
<LoadingPanel data={data} isLoading={isLoading} isFetched={isFetched} error={error}>
<div
className={classNames(styles.body, {
[styles.status]: isLoading || noResults || !hasData,
})}
>
{hasData ? (typeof children === 'function' ? children(result) : children) : null}
{isLoading && <Loading position="page" />}
{!isLoading && !hasData && !query && (renderEmpty ? renderEmpty() : <Empty />)}
{!isLoading && noResults && <Empty message={formatMessage(messages.noResultsFound)} />}
</div>
{allowPaging && hasData && (
<Pager
className={styles.pager}
page={page}
pageSize={pageSize}
count={count}
onPageChange={handlePageChange}
/>
)}
</LoadingPanel>
</>
2023-08-25 18:54:44 +00:00
);
}
export default DataTable;