Zen components conversion.

This commit is contained in:
Mike Cao 2025-03-07 03:11:58 -08:00
parent aac1a12e51
commit 5999bf6256
142 changed files with 1235 additions and 1454 deletions

View file

@ -3,11 +3,10 @@ import {
Button,
Form,
FormButtons,
FormInput,
FormRow,
SubmitButton,
FormField,
FormSubmitButton,
TextField,
} from 'react-basics';
} from '@umami/react-zen';
export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
const { formatMessage, labels } = useMessages();
@ -27,16 +26,14 @@ export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose:
return (
<Form onSubmit={handleSubmit} error={error}>
<FormRow label={formatMessage(labels.name)}>
<FormInput name="name" rules={{ required: formatMessage(labels.required) }}>
<TextField autoComplete="off" />
</FormInput>
</FormRow>
<FormButtons flex>
<SubmitButton variant="primary" disabled={isPending}>
<FormField name="name" label={formatMessage(labels.name)}>
<TextField autoComplete="off" />
</FormField>
<FormButtons>
<FormSubmitButton variant="primary" disabled={isPending}>
{formatMessage(labels.save)}
</SubmitButton>
<Button disabled={isPending} onClick={onClose}>
</FormSubmitButton>
<Button isDisabled={isPending} onPress={onClose}>
{formatMessage(labels.cancel)}
</Button>
</FormButtons>

View file

@ -1,20 +1,17 @@
import { useRef } from 'react';
import {
Form,
FormRow,
FormInput,
FormField,
FormButtons,
TextField,
Button,
SubmitButton,
} from 'react-basics';
FormSubmitButton,
} from '@umami/react-zen';
import { useApi, useMessages, useModified } from '@/components/hooks';
export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) {
const { formatMessage, labels } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation({ mutationFn: (data: any) => post('/teams/join', data) });
const ref = useRef(null);
const { touch } = useModified();
const handleSubmit = async (data: any) => {
@ -28,15 +25,17 @@ export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose:
};
return (
<Form ref={ref} onSubmit={handleSubmit} error={error}>
<FormRow label={formatMessage(labels.accessCode)}>
<FormInput name="accessCode" rules={{ required: formatMessage(labels.required) }}>
<TextField autoComplete="off" />
</FormInput>
</FormRow>
<FormButtons flex>
<SubmitButton variant="primary">{formatMessage(labels.join)}</SubmitButton>
<Button onClick={onClose}>{formatMessage(labels.cancel)}</Button>
<Form onSubmit={handleSubmit} error={error}>
<FormField
label={formatMessage(labels.accessCode)}
name="accessCode"
rules={{ required: formatMessage(labels.required) }}
>
<TextField autoComplete="off" />
</FormField>
<FormButtons>
<FormSubmitButton variant="primary">{formatMessage(labels.join)}</FormSubmitButton>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
</FormButtons>
</Form>
);

View file

@ -1,6 +1,6 @@
import { useLocale, useLogin, useMessages, useModified } from '@/components/hooks';
import { useRouter } from 'next/navigation';
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
import { Button, Icon, Icons, Modal, DialogTrigger, Dialog, Text } from '@umami/react-zen';
import { TeamLeaveForm } from './TeamLeaveForm';
export function TeamLeaveButton({ teamId, teamName }: { teamId: string; teamName: string }) {
@ -16,24 +16,26 @@ export function TeamLeaveButton({ teamId, teamName }: { teamId: string; teamName
};
return (
<ModalTrigger>
<DialogTrigger>
<Button variant="secondary">
<Icon rotate={dir === 'rtl' ? 180 : 0}>
<Icons.Logout />
</Icon>
<Text>{formatMessage(labels.leave)}</Text>
</Button>
<Modal title={formatMessage(labels.leaveTeam)}>
{(close: () => void) => (
<TeamLeaveForm
teamId={teamId}
userId={user.id}
teamName={teamName}
onSave={handleLeave}
onClose={close}
/>
)}
<Modal>
<Dialog title={formatMessage(labels.leaveTeam)}>
{({ close }) => (
<TeamLeaveForm
teamId={teamId}
userId={user.id}
teamName={teamName}
onSave={handleLeave}
onClose={close}
/>
)}
</Dialog>
</Modal>
</ModalTrigger>
</DialogTrigger>
);
}

View file

@ -1,4 +1,4 @@
import { Button, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
import { Button, Icon, Modal, DialogTrigger, Dialog, Text, useToast } from '@umami/react-zen';
import { Icons } from '@/components/icons';
import { useMessages, useModified } from '@/components/hooks';
import { TeamAddForm } from './TeamAddForm';
@ -6,26 +6,28 @@ import { messages } from '@/components/messages';
export function TeamsAddButton({ onSave }: { onSave?: () => void }) {
const { formatMessage, labels } = useMessages();
const { showToast } = useToasts();
const { toast } = useToast();
const { touch } = useModified();
const handleSave = async () => {
showToast({ message: formatMessage(messages.saved), variant: 'success' });
toast(formatMessage(messages.saved));
touch('teams');
onSave?.();
};
return (
<ModalTrigger>
<DialogTrigger>
<Button variant="primary">
<Icon>
<Icons.Plus />
</Icon>
<Text>{formatMessage(labels.createTeam)}</Text>
</Button>
<Modal title={formatMessage(labels.createTeam)}>
{(close: () => void) => <TeamAddForm onSave={handleSave} onClose={close} />}
<Modal>
<Dialog title={formatMessage(labels.createTeam)}>
{({ close }) => <TeamAddForm onSave={handleSave} onClose={close} />}
</Dialog>
</Modal>
</ModalTrigger>
</DialogTrigger>
);
}

View file

@ -1,4 +1,4 @@
import { Flexbox } from 'react-basics';
import { Row } from '@umami/react-zen';
import { PageHeader } from '@/components/layout/PageHeader';
import { ROLES } from '@/lib/constants';
import { useLogin, useMessages } from '@/components/hooks';
@ -12,10 +12,10 @@ export function TeamsHeader({ allowCreate = true }: { allowCreate?: boolean }) {
return (
<PageHeader title={formatMessage(labels.teams)}>
<Flexbox gap={10}>
<Row gap="3">
{!cloudMode && <TeamsJoinButton />}
{allowCreate && user.role !== ROLES.viewOnly && <TeamsAddButton />}
</Flexbox>
</Row>
</PageHeader>
);
}

View file

@ -1,29 +1,31 @@
import { Button, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
import { Button, Icon, Modal, DialogTrigger, Dialog, Text, useToast } from '@umami/react-zen';
import { Icons } from '@/components/icons';
import { useMessages, useModified } from '@/components/hooks';
import { TeamJoinForm } from './TeamJoinForm';
export function TeamsJoinButton() {
const { formatMessage, labels, messages } = useMessages();
const { showToast } = useToasts();
const { toast } = useToast();
const { touch } = useModified();
const handleJoin = () => {
showToast({ message: formatMessage(messages.saved), variant: 'success' });
toast(formatMessage(messages.saved));
touch('teams');
};
return (
<ModalTrigger>
<DialogTrigger>
<Button variant="secondary">
<Icon>
<Icons.AddUser />
</Icon>
<Text>{formatMessage(labels.joinTeam)}</Text>
</Button>
<Modal title={formatMessage(labels.joinTeam)}>
{close => <TeamJoinForm onSave={handleJoin} onClose={close} />}
<Modal>
<Dialog title={formatMessage(labels.joinTeam)}>
{({ close }) => <TeamJoinForm onSave={handleJoin} onClose={close} />}
</Dialog>
</Modal>
</ModalTrigger>
</DialogTrigger>
);
}

View file

@ -1,4 +1,4 @@
import { GridColumn, GridTable, Icon, Text } from 'react-basics';
import { DataColumn, DataTable, Icon, Text } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
import { Icons } from '@/components/icons';
import { ROLES } from '@/lib/constants';
@ -15,33 +15,33 @@ export function TeamsTable({
const { formatMessage, labels } = useMessages();
return (
<GridTable data={data}>
<GridColumn name="name" label={formatMessage(labels.name)} />
<GridColumn name="owner" label={formatMessage(labels.owner)}>
{row => row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username}
</GridColumn>
<GridColumn name="websites" label={formatMessage(labels.websites)}>
{row => row._count.website}
</GridColumn>
<GridColumn name="members" label={formatMessage(labels.members)}>
{row => row._count.teamUser}
</GridColumn>
<DataTable data={data}>
<DataColumn id="name" label={formatMessage(labels.name)} />
<DataColumn id="owner" label={formatMessage(labels.owner)}>
{(row: any) => row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username}
</DataColumn>
<DataColumn id="websites" label={formatMessage(labels.websites)}>
{(row: any) => row._count.website}
</DataColumn>
<DataColumn id="members" label={formatMessage(labels.members)}>
{(row: any) => row._count.teamUser}
</DataColumn>
{showActions && (
<GridColumn name="action" label=" " alignment="end">
{row => {
<DataColumn id="action" label=" " align="end">
{(row: any) => {
const { id } = row;
return (
<LinkButton href={`/teams/${id}/settings`}>
<Icon>
<Icons.ArrowRight />
<Icons.Arrow />
</Icon>
<Text>{formatMessage(labels.view)}</Text>
</LinkButton>
);
}}
</GridColumn>
</DataColumn>
)}
</GridTable>
</DataTable>
);
}

View file

@ -1,4 +1,4 @@
import { Button, Icon, Icons, Text } from 'react-basics';
import { Button, Icon, Icons, Text } from '@umami/react-zen';
import styles from './WebsiteTags.module.css';
export function WebsiteTags({
@ -21,7 +21,7 @@ export function WebsiteTags({
return (
<div key={websiteId} className={styles.tag}>
<Button onClick={() => onClick(websiteId)} variant="primary" size="sm">
<Button onPress={() => onClick(websiteId)} variant="primary" size="sm">
<Text>
<b>{`${website.name}`}</b>
</Text>