Refactored intl messages.

This commit is contained in:
Mike Cao 2023-01-25 07:42:46 -08:00
parent fbccf4d3af
commit 7725b5c129
44 changed files with 558 additions and 485 deletions

View file

@ -1,8 +1,19 @@
import { useRef } from 'react';
import { Form, FormRow, FormInput, FormButtons, TextField, Button } from 'react-basics';
import { useIntl } from 'react-intl';
import {
Form,
FormRow,
FormInput,
FormButtons,
TextField,
Button,
SubmitButton,
} from 'react-basics';
import useApi from 'hooks/useApi';
import { labels } from 'components/messages';
export default function TeamAddForm({ onSave, onClose }) {
const { formatMessage } = useIntl();
const { post, useMutation } = useApi();
const { mutate, error, isLoading } = useMutation(data => post('/teams', data));
const ref = useRef(null);
@ -17,17 +28,17 @@ export default function TeamAddForm({ onSave, onClose }) {
return (
<Form ref={ref} onSubmit={handleSubmit} error={error}>
<FormRow label="Name">
<FormRow label={formatMessage(labels.name)}>
<FormInput name="name" rules={{ required: 'Required' }}>
<TextField autoComplete="off" />
</FormInput>
</FormRow>
<FormButtons flex>
<Button type="submit" variant="primary" disabled={isLoading}>
Save
</Button>
<SubmitButton variant="primary" disabled={isLoading}>
{formatMessage(labels.save)}
</SubmitButton>
<Button disabled={isLoading} onClick={onClose}>
Cancel
{formatMessage(labels.cancel)}
</Button>
</FormButtons>
</Form>

View file

@ -8,13 +8,16 @@ import {
Button,
Flexbox,
} from 'react-basics';
import { useIntl } from 'react-intl';
import { getRandomChars } from 'next-basics';
import { useRef, useState } from 'react';
import useApi from 'hooks/useApi';
import { labels } from 'components/messages';
const generateId = () => getRandomChars(16);
export default function TeamEditForm({ teamId, data, onSave }) {
const { formatMessage } = useIntl();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation(data => post(`/teams/${teamId}`, data));
const ref = useRef(null);
@ -40,22 +43,22 @@ export default function TeamEditForm({ teamId, data, onSave }) {
return (
<Form ref={ref} onSubmit={handleSubmit} error={error} values={data}>
<FormRow label="Team ID">
<FormRow label={formatMessage(labels.teamId)}>
<TextField value={teamId} readOnly allowCopy />
</FormRow>
<FormRow label="Name">
<FormRow label={formatMessage(labels.name)}>
<FormInput name="name" rules={{ required: 'Required' }}>
<TextField />
</FormInput>
</FormRow>
<FormRow label="Access code">
<FormRow label={formatMessage(labels.accessCode)}>
<Flexbox gap={10}>
<TextField value={accessCode} readOnly allowCopy />
<Button onClick={handleRegenerate}>Regenerate</Button>
<Button onClick={handleRegenerate}>{formatMessage(labels.regenerate)}</Button>
</Flexbox>
</FormRow>
<FormButtons>
<SubmitButton variant="primary">Save</SubmitButton>
<SubmitButton variant="primary">{formatMessage(labels.save)}</SubmitButton>
</FormButtons>
</Form>
);

View file

@ -7,18 +7,27 @@ import {
TableColumn,
Button,
Icon,
Icons,
Flexbox,
Text,
} from 'react-basics';
import styles from './TeamsTable.module.css';
import { ROLES } from 'lib/constants';
import { labels } from 'components/messages';
import { useIntl } from 'react-intl';
const columns = [
{ name: 'username', label: 'Username', style: { flex: 4 } },
{ name: 'role', label: 'Role' },
{ name: 'action', label: '' },
];
const { Close } = Icons;
export default function TeamMembersTable({ data = [] }) {
const { formatMessage } = useIntl();
const columns = [
{ name: 'username', label: formatMessage(labels.username), style: { flex: 4 } },
{ name: 'role', label: formatMessage(labels.role) },
{ name: 'action', label: '' },
];
return (
<Table className={styles.table} columns={columns} rows={data}>
<Table columns={columns} rows={data}>
<TableHeader>
{(column, index) => {
return (
@ -30,25 +39,35 @@ export default function TeamMembersTable({ data = [] }) {
</TableHeader>
<TableBody>
{(row, keys, rowIndex) => {
row.action = (
<div className={styles.actions}>
<Button>
<Icon icon="cross" />
Remove
</Button>
</div>
);
const rowData = {
username: row?.user?.username,
role: formatMessage(
labels[Object.keys(ROLES).find(key => ROLES[key] === row.role) || labels.unknown],
),
action: (
<div>
<Button>
<Icon>
<Close />
</Icon>
<Text>{formatMessage(labels.remove)}</Text>
</Button>
</div>
),
};
return (
<TableRow key={rowIndex} data={row} keys={keys}>
<TableRow key={rowIndex} data={rowData} keys={keys}>
{(data, key, colIndex) => {
return (
<TableCell
key={colIndex}
className={styles.cell}
style={{ ...columns[colIndex]?.style }}
>
{data[key] ?? data?.user?.[key]}
<TableCell key={colIndex} style={{ ...columns[colIndex]?.style }}>
<Flexbox
flex={1}
alignItems="center"
justifyContent={key === 'action' ? 'end' : undefined}
>
{data[key]}
</Flexbox>
</TableCell>
);
}}

View file

@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
import { Breadcrumbs, Item, Tabs, useToast } from 'react-basics';
import useApi from 'hooks/useApi';
import Link from 'next/link';
@ -6,8 +7,11 @@ import Page from 'components/layout/Page';
import TeamEditForm from 'components/pages/settings/teams/TeamEditForm';
import PageHeader from 'components/layout/PageHeader';
import TeamMembers from 'components/pages/settings/teams/TeamMembers';
import { labels, messages } from 'components/messages';
import TeamWebsites from './TeamWebsites';
export default function TeamDetails({ teamId }) {
export default function TeamSettings({ teamId }) {
const { formatMessage } = useIntl();
const [values, setValues] = useState(null);
const [tab, setTab] = useState('details');
const { get, useQuery } = useApi();
@ -23,7 +27,7 @@ export default function TeamDetails({ teamId }) {
);
const handleSave = data => {
showToast({ message: 'Saved successfully.', variant: 'success' });
showToast({ message: formatMessage(messages.saved), variant: 'success' });
setValues(state => ({ ...state, ...data }));
};
@ -44,13 +48,14 @@ export default function TeamDetails({ teamId }) {
<Item>{values?.name}</Item>
</Breadcrumbs>
</PageHeader>
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30, fontSize: 14 }}>
<Item key="details">Details</Item>
<Item key="members">Members</Item>
<Item key="websites">Websites</Item>
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30 }}>
<Item key="details">{formatMessage(labels.details)}</Item>
<Item key="members">{formatMessage(labels.members)}</Item>
<Item key="websites">{formatMessage(labels.websites)}</Item>
</Tabs>
{tab === 'details' && <TeamEditForm teamId={teamId} data={values} onSave={handleSave} />}
{tab === 'members' && <TeamMembers teamId={teamId} />}
{tab === 'websites' && <TeamWebsites teamId={teamId} />}
</Page>
);
}

View file

@ -0,0 +1,25 @@
import { Loading } from 'react-basics';
import { useIntl } from 'react-intl';
import useApi from 'hooks/useApi';
import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
import { messages } from 'components/messages';
export default function TeamWebsites({ teamId }) {
const { formatMessage } = useIntl();
const { get, useQuery } = useApi();
const { data, isLoading } = useQuery(['team/websites', teamId], () =>
get(`/teams/${teamId}/websites`),
);
const hasData = data && data.length !== 0;
if (isLoading) {
return <Loading icon="dots" position="block" />;
}
return (
<div>
{hasData && <WebsitesTable data={data} />}
{!hasData && formatMessage(messages.noData)}
</div>
);
}

View file

@ -1,13 +1,18 @@
import { useState } from 'react';
import { Button, Icon, Modal, useToast } from 'react-basics';
import { Button, Icon, Modal, useToast, Icons, Text } from 'react-basics';
import { useIntl } from 'react-intl';
import useApi from 'hooks/useApi';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import TeamAddForm from 'components/pages/settings/teams/TeamAddForm';
import PageHeader from 'components/layout/PageHeader';
import TeamsTable from 'components/pages/settings/teams/TeamsTable';
import Page from 'components/layout/Page';
import { labels, messages } from 'components/messages';
const { Plus } = Icons;
export default function TeamsList() {
const { formatMessage } = useIntl();
const [edit, setEdit] = useState(false);
const [update, setUpdate] = useState(0);
const { get, useQuery } = useApi();
@ -22,7 +27,7 @@ export default function TeamsList() {
const handleSave = () => {
setEdit(false);
setUpdate(state => state + 1);
showToast({ message: 'Team saved.', variant: 'success' });
showToast({ message: formatMessage(messages.saved), variant: 'success' });
};
const handleClose = () => {
@ -32,21 +37,27 @@ export default function TeamsList() {
return (
<Page loading={isLoading} error={error}>
{toast}
<PageHeader title="Teams">
<Button onClick={handleAdd}>
<Icon icon="plus" /> Create team
<PageHeader title={formatMessage(labels.team)}>
<Button variant="primary" onClick={handleAdd}>
<Icon>
<Plus />
</Icon>
<Text>{formatMessage(labels.createTeam)}</Text>
</Button>
</PageHeader>
{hasData && <TeamsTable data={data} />}
{!hasData && (
<EmptyPlaceholder message="You don't have any teams configured.">
<EmptyPlaceholder message={formatMessage(messages.noTeams)}>
<Button variant="primary" onClick={handleAdd}>
<Icon icon="plus" /> Create team
<Icon>
<Plus />
</Icon>
<Text>{formatMessage(labels.createTeam)}</Text>
</Button>
</EmptyPlaceholder>
)}
{edit && (
<Modal title="Create team" onClose={handleClose}>
<Modal title={formatMessage(labels.createTeam)} onClose={handleClose}>
{close => <TeamAddForm onSave={handleSave} onClose={close} />}
</Modal>
)}

View file

@ -8,17 +8,25 @@ import {
TableColumn,
Button,
Icon,
Flexbox,
Icons,
Text,
} from 'react-basics';
import styles from './TeamsTable.module.css';
import { useIntl } from 'react-intl';
import { labels } from 'components/messages';
const columns = [
{ name: 'name', label: 'Name', style: { flex: 2 } },
{ name: 'action', label: ' ' },
];
const { ArrowRight } = Icons;
export default function TeamsTable({ data = [] }) {
const { formatMessage } = useIntl();
const columns = [
{ name: 'name', label: formatMessage(labels.name), style: { flex: 2 } },
{ name: 'action', label: ' ' },
];
return (
<Table className={styles.table} columns={columns} rows={data}>
<Table columns={columns} rows={data}>
<TableHeader>
{(column, index) => {
return (
@ -33,28 +41,28 @@ export default function TeamsTable({ data = [] }) {
const { id } = row;
row.action = (
<div className={styles.actions}>
<Flexbox flex={1} justifyContent="end">
<Link href={`/settings/teams/${id}`}>
<a>
<Button>
<Icon icon="arrow-right" />
Settings
<Icon>
<ArrowRight />
</Icon>
<Text>{formatMessage(labels.settings)}</Text>
</Button>
</a>
</Link>
</div>
</Flexbox>
);
return (
<TableRow key={rowIndex} data={row} keys={keys}>
{(data, key, colIndex) => {
return (
<TableCell
key={colIndex}
className={styles.cell}
style={{ ...columns[colIndex]?.style }}
>
{data[key]}
<TableCell key={colIndex} style={{ ...columns[colIndex]?.style }}>
<Flexbox flex={1} alignItems="center">
{data[key]}
</Flexbox>
</TableCell>
);
}}

View file

@ -1,18 +0,0 @@
.table th,
.table td {
flex: 2;
}
.cell {
display: flex;
align-items: center;
}
.cell:last-child {
justify-content: flex-end;
}
.actions {
display: flex;
gap: 12px;
}