mirror of
https://github.com/umami-software/umami.git
synced 2026-02-05 13:17:19 +01:00
91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
import { ReactNode } from 'react';
|
|
import classNames from 'classnames';
|
|
import { Loading, SearchField } from 'react-basics';
|
|
import { useMessages, useNavigation } from '@/components/hooks';
|
|
import Empty from '@/components/common/Empty';
|
|
import Pager from '@/components/common/Pager';
|
|
import { PagedQueryResult } from '@/lib/types';
|
|
import styles from './DataTable.module.css';
|
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
|
|
|
const DEFAULT_SEARCH_DELAY = 600;
|
|
|
|
export interface DataTableProps {
|
|
queryResult: PagedQueryResult<any>;
|
|
searchDelay?: number;
|
|
allowSearch?: boolean;
|
|
allowPaging?: boolean;
|
|
autoFocus?: boolean;
|
|
renderEmpty?: () => ReactNode;
|
|
children: ReactNode | ((data: any) => ReactNode);
|
|
}
|
|
|
|
export function DataTable({
|
|
queryResult,
|
|
searchDelay = 600,
|
|
allowSearch = true,
|
|
allowPaging = true,
|
|
autoFocus = true,
|
|
renderEmpty,
|
|
children,
|
|
}: DataTableProps) {
|
|
const { formatMessage, labels, messages } = useMessages();
|
|
const {
|
|
result,
|
|
params,
|
|
setParams,
|
|
query: { error, isLoading, isFetched },
|
|
} = queryResult || {};
|
|
const { page, pageSize, count, data } = result || {};
|
|
const { search } = params || {};
|
|
const hasData = Boolean(!isLoading && data?.length);
|
|
const noResults = Boolean(search && !hasData);
|
|
const { router, renderUrl } = useNavigation();
|
|
|
|
const handleSearch = (search: string) => {
|
|
setParams({ ...params, search, page: params.page ? page : 1 });
|
|
};
|
|
|
|
const handlePageChange = (page: number) => {
|
|
setParams({ ...params, search, page });
|
|
router.push(renderUrl({ page }));
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{allowSearch && (hasData || search) && (
|
|
<SearchField
|
|
className={styles.search}
|
|
value={search}
|
|
onSearch={handleSearch}
|
|
delay={searchDelay || DEFAULT_SEARCH_DELAY}
|
|
autoFocus={autoFocus}
|
|
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 && !search && (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>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default DataTable;
|