Converted user and website settings.

This commit is contained in:
Mike Cao 2025-02-21 16:55:05 -08:00
parent 4c24e54fdd
commit b5c6194f36
59 changed files with 363 additions and 554 deletions

View file

@ -1,11 +1,11 @@
import { ReactNode } from 'react';
import { Button, LoadingButton, Form, FormButtons } from 'react-basics';
import { Row, Button, FormSubmitButton, Form, FormButtons } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
export interface ConfirmationFormProps {
message: ReactNode;
buttonLabel?: ReactNode;
buttonVariant?: 'none' | 'primary' | 'secondary' | 'quiet' | 'danger';
buttonVariant?: 'primary' | 'quiet' | 'danger';
isLoading?: boolean;
error?: string | Error;
onConfirm?: () => void;
@ -24,13 +24,13 @@ export function ConfirmationForm({
const { formatMessage, labels } = useMessages();
return (
<Form error={error}>
<p>{message}</p>
<FormButtons flex>
<LoadingButton isLoading={isLoading} onClick={onConfirm} variant={buttonVariant}>
<Form onSubmit={onConfirm} error={error}>
<Row marginY="4">{message}</Row>
<FormButtons>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
<FormSubmitButton isLoading={isLoading} variant={buttonVariant}>
{buttonLabel || formatMessage(labels.ok)}
</LoadingButton>
<Button onClick={onClose}>{formatMessage(labels.cancel)}</Button>
</FormSubmitButton>
</FormButtons>
</Form>
);

View file

@ -1,12 +1,10 @@
import { ReactNode } from 'react';
import classNames from 'classnames';
import { Loading, SearchField } from 'react-basics';
import { Loading, SearchField, Row, Column } from '@umami/react-zen';
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';
import { PagedQueryResult } from '@/lib/types';
const DEFAULT_SEARCH_DELAY = 600;
@ -20,7 +18,7 @@ export interface DataTableProps {
children: ReactNode | ((data: any) => ReactNode);
}
export function DataTable({
export function DataGrid({
queryResult,
searchDelay = 600,
allowSearch = true,
@ -30,12 +28,8 @@ export function DataTable({
children,
}: DataTableProps) {
const { formatMessage, labels, messages } = useMessages();
const {
result,
params,
setParams,
query: { error, isLoading, isFetched },
} = queryResult || {};
const { result, params, setParams, query } = queryResult || {};
const { error, isLoading, isFetched } = query || {};
const { page, pageSize, count, data } = result || {};
const { search } = params || {};
const hasData = Boolean(!isLoading && data?.length);
@ -43,45 +37,38 @@ export function DataTable({
const { router, renderUrl } = useNavigation();
const handleSearch = (search: string) => {
setParams({ ...params, search, page: params.page ? page : 1 });
setParams({ ...params, search });
};
const handlePageChange = (page: number) => {
setParams({ ...params, search, page });
setParams({ ...params, 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)}
/>
<Row width="280px" alignItems="center" marginBottom="6">
<SearchField
value={search}
onSearch={handleSearch}
delay={searchDelay || DEFAULT_SEARCH_DELAY}
autoFocus={autoFocus}
placeholder={formatMessage(labels.search)}
/>
</Row>
)}
<LoadingPanel data={data} isLoading={isLoading} isFetched={isFetched} error={error}>
<div
className={classNames(styles.body, {
[styles.status]: isLoading || noResults || !hasData,
})}
>
<Column>
{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>
</Column>
{allowPaging && hasData && (
<Pager
className={styles.pager}
page={page}
pageSize={pageSize}
count={count}
onPageChange={handlePageChange}
/>
<Row marginTop="6">
<Pager page={page} pageSize={pageSize} count={count} onPageChange={handlePageChange} />
</Row>
)}
</LoadingPanel>
</>

View file

@ -1,6 +1,6 @@
import { ReactNode } from 'react';
import classNames from 'classnames';
import { Loading } from 'react-basics';
import { Loading } from '@umami/react-zen';
import { ErrorMessage } from '@/components/common/ErrorMessage';
import { Empty } from '@/components/common/Empty';
import styles from './LoadingPanel.module.css';

View file

@ -1,7 +1,5 @@
import classNames from 'classnames';
import { Button, Icon, Icons } from 'react-basics';
import { Button, Icon, Icons, Row, Text } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
import styles from './Pager.module.css';
export interface PagerProps {
page: string | number;
@ -11,7 +9,7 @@ export interface PagerProps {
className?: string;
}
export function Pager({ page, pageSize, count, onPageChange, className }: PagerProps) {
export function Pager({ page, pageSize, count, onPageChange }: PagerProps) {
const { formatMessage, labels } = useMessages();
const maxPage = pageSize && count ? Math.ceil(+count / +pageSize) : 0;
const lastPage = page === maxPage;
@ -34,24 +32,21 @@ export function Pager({ page, pageSize, count, onPageChange, className }: PagerP
}
return (
<div className={classNames(styles.pager, className)}>
<div className={styles.count}>{formatMessage(labels.numberOfRecords, { x: count })}</div>
<div className={styles.nav}>
<Button onClick={() => handlePageChange(-1)} disabled={firstPage}>
<Icon rotate={90}>
<Icons.ChevronDown />
<Row alignItems="center" justifyContent="space-between" gap="3" flexGrow={1}>
<Text>{formatMessage(labels.numberOfRecords, { x: count })}</Text>
<Row alignItems="center" justifyContent="flex-end" gap="3">
<Text>{formatMessage(labels.pageOf, { current: page, total: maxPage })}</Text>
<Button onPress={() => handlePageChange(-1)} isDisabled={firstPage}>
<Icon size="sm" rotate={180}>
<Icons.Chevron />
</Icon>
</Button>
<div className={styles.text}>
{formatMessage(labels.pageOf, { current: page, total: maxPage })}
</div>
<Button onClick={() => handlePageChange(1)} disabled={lastPage}>
<Icon rotate={270}>
<Icons.ChevronDown />
<Button onPress={() => handlePageChange(1)} isDisabled={lastPage}>
<Icon size="sm">
<Icons.Chevron />
</Icon>
</Button>
</div>
<div></div>
</div>
</Row>
</Row>
);
}