Add Search Api/Components.

This commit is contained in:
Brian Cao 2023-08-10 13:26:33 -07:00
parent 45888fabe6
commit dcf8b2edaa
37 changed files with 1069 additions and 287 deletions

View file

@ -12,16 +12,17 @@ import useDashboard from 'store/dashboard';
import useMessages from 'hooks/useMessages';
import useLocale from 'hooks/useLocale';
export function Dashboard({ userId }) {
export function Dashboard() {
const { formatMessage, labels, messages } = useMessages();
const dashboard = useDashboard();
const { showCharts, limit, editing } = dashboard;
const [max, setMax] = useState(limit);
const { get, useQuery } = useApi();
const { data, isLoading, error } = useQuery(['websites'], () =>
get('/websites', { userId, includeTeams: 1 }),
get('/websites', { includeTeams: 1 }),
);
const hasData = data && data.length !== 0;
const hasData = data && data?.data.length !== 0;
const { dir } = useLocale();
function handleMore() {
@ -47,8 +48,10 @@ export function Dashboard({ userId }) {
)}
{hasData && (
<>
{editing && <DashboardEdit websites={data} />}
{!editing && <WebsiteChartList websites={data} showCharts={showCharts} limit={max} />}
{editing && <DashboardEdit websites={data?.data} />}
{!editing && (
<WebsiteChartList websites={data?.data} showCharts={showCharts} limit={max} />
)}
{max < data.length && (
<Flexbox justifyContent="center">
<Button onClick={handleMore}>

View file

@ -5,7 +5,14 @@ import SettingsTable from 'components/common/SettingsTable';
import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
import { useMessages } from 'hooks';
export function ReportsTable({ data = [], onDelete = () => {} }) {
export function ReportsTable({
data = [],
onDelete = () => {},
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const [report, setReport] = useState(null);
const { formatMessage, labels } = useMessages();
@ -22,7 +29,16 @@ export function ReportsTable({ data = [], onDelete = () => {} }) {
return (
<>
<SettingsTable columns={columns} data={data}>
<SettingsTable
columns={columns}
data={data}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{row => {
const { id } = row;

View file

@ -12,6 +12,8 @@ export function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
const [newWebsites, setNewWebsites] = useState([]);
const formRef = useRef();
const hasData = websites && websites.data.length > 0;
const handleSubmit = () => {
mutate(
{ websiteIds: newWebsites },
@ -42,20 +44,22 @@ export function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
return (
<>
<Form onSubmit={handleSubmit} error={error} ref={formRef}>
<FormRow label={formatMessage(labels.websites)}>
<Dropdown items={websites} onChange={handleAddWebsite} style={{ width: 300 }}>
{({ id, name }) => <Item key={id}>{name}</Item>}
</Dropdown>
</FormRow>
<WebsiteTags items={websites} websites={newWebsites} onClick={handleRemoveWebsite} />
<FormButtons flex>
<SubmitButton disabled={newWebsites && newWebsites.length === 0}>
{formatMessage(labels.addWebsite)}
</SubmitButton>
<Button onClick={onClose}>{formatMessage(labels.cancel)}</Button>
</FormButtons>
</Form>
{hasData && (
<Form onSubmit={handleSubmit} error={error} ref={formRef}>
<FormRow label={formatMessage(labels.websites)}>
<Dropdown items={websites.data} onChange={handleAddWebsite} style={{ width: 300 }}>
{({ id, name }) => <Item key={id}>{name}</Item>}
</Dropdown>
</FormRow>
<WebsiteTags items={websites.data} websites={newWebsites} onClick={handleRemoveWebsite} />
<FormButtons flex>
<SubmitButton disabled={newWebsites && newWebsites.length === 0}>
{formatMessage(labels.addWebsite)}
</SubmitButton>
<Button onClick={onClose}>{formatMessage(labels.cancel)}</Button>
</FormButtons>
</Form>
)}
</>
);
}

View file

@ -2,13 +2,22 @@ import { Loading, useToasts } from 'react-basics';
import TeamMembersTable from 'components/pages/settings/teams/TeamMembersTable';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import useApiFilter from 'hooks/useApiFilter';
export function TeamMembers({ teamId, readOnly }) {
const { showToast } = useToasts();
const { get, useQuery } = useApi();
const { formatMessage, messages } = useMessages();
const { data, isLoading, refetch } = useQuery(['teams:users', teamId], () =>
get(`/teams/${teamId}/users`),
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
useApiFilter();
const { get, useQuery } = useApi();
const { data, isLoading, refetch } = useQuery(
['teams:users', teamId, filter, page, pageSize],
() =>
get(`/teams/${teamId}/users`, {
filter,
page,
pageSize,
}),
);
if (isLoading) {
@ -22,7 +31,15 @@ export function TeamMembers({ teamId, readOnly }) {
return (
<>
<TeamMembersTable onSave={handleSave} data={data} readOnly={readOnly} />
<TeamMembersTable
onSave={handleSave}
data={data}
readOnly={readOnly}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
</>
);
}

View file

@ -4,7 +4,15 @@ import { ROLES } from 'lib/constants';
import TeamMemberRemoveButton from './TeamMemberRemoveButton';
import SettingsTable from 'components/common/SettingsTable';
export function TeamMembersTable({ data = [], onSave, readOnly }) {
export function TeamMembersTable({
data = [],
onSave,
readOnly,
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const { formatMessage, labels } = useMessages();
const { user } = useUser();
@ -16,7 +24,7 @@ export function TeamMembersTable({ data = [], onSave, readOnly }) {
const cellRender = (row, data, key) => {
if (key === 'username') {
return row?.user?.username;
return row?.username;
}
if (key === 'role') {
return formatMessage(
@ -27,13 +35,23 @@ export function TeamMembersTable({ data = [], onSave, readOnly }) {
};
return (
<SettingsTable data={data} columns={columns} cellRender={cellRender}>
<SettingsTable
data={data}
columns={columns}
cellRender={cellRender}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{row => {
return (
!readOnly && (
<TeamMemberRemoveButton
teamId={row.teamId}
userId={row.userId}
userId={row.id}
disabled={user.id === row?.user?.id || row.role === ROLES.teamOwner}
onSave={onSave}
/>

View file

@ -13,13 +13,22 @@ import TeamWebsitesTable from 'components/pages/settings/teams/TeamWebsitesTable
import TeamAddWebsiteForm from 'components/pages/settings/teams/TeamAddWebsiteForm';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import useApiFilter from 'hooks/useApiFilter';
export function TeamWebsites({ teamId }) {
const { showToast } = useToasts();
const { formatMessage, labels, messages } = useMessages();
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
useApiFilter();
const { get, useQuery } = useApi();
const { data, isLoading, refetch } = useQuery(['teams:websites', teamId], () =>
get(`/teams/${teamId}/websites`),
const { data, isLoading, refetch } = useQuery(
['teams:websites', teamId, filter, page, pageSize],
() =>
get(`/teams/${teamId}/websites`, {
filter,
page,
pageSize,
}),
);
const hasData = data && data.length !== 0;
@ -49,7 +58,17 @@ export function TeamWebsites({ teamId }) {
return (
<div>
<ActionForm description={formatMessage(messages.teamWebsitesInfo)}>{addButton}</ActionForm>
{hasData && <TeamWebsitesTable teamId={teamId} data={data} onSave={handleSave} />}
{hasData && (
<TeamWebsitesTable
teamId={teamId}
data={data}
onSave={handleSave}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
)}
</div>
);
}

View file

@ -6,9 +6,17 @@ import TeamWebsiteRemoveButton from './TeamWebsiteRemoveButton';
import SettingsTable from 'components/common/SettingsTable';
import useConfig from 'hooks/useConfig';
export function TeamWebsitesTable({ data = [], onSave }) {
export function TeamWebsitesTable({
data = [],
onSave,
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const { formatMessage, labels } = useMessages();
const { openExternal } = useConfig();
const { user } = useUser();
const columns = [
{ name: 'name', label: formatMessage(labels.name) },
@ -17,11 +25,19 @@ export function TeamWebsitesTable({ data = [], onSave }) {
];
return (
<SettingsTable columns={columns} data={data}>
<SettingsTable
columns={columns}
data={data}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{row => {
const { teamId } = row;
const { id: websiteId, name, domain, userId } = row.website;
const { teamUser } = row.team;
const { id: teamId, teamUser } = row.teamWebsite[0].team;
const { id: websiteId, name, domain, userId } = row;
const owner = teamUser[0];
const canRemove = user.id === userId || user.id === owner.userId;

View file

@ -1,24 +1,37 @@
import { useState } from 'react';
import { Button, Icon, Modal, ModalTrigger, useToasts, Text, Flexbox } from 'react-basics';
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 Icons from 'components/icons';
import TeamJoinForm from './TeamJoinForm';
import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import TeamAddForm from 'components/pages/settings/teams/TeamAddForm';
import TeamsTable from 'components/pages/settings/teams/TeamsTable';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import { ROLES } from 'lib/constants';
import useUser from 'hooks/useUser';
import { ROLES } from 'lib/constants';
import { useState } from 'react';
import { Button, Flexbox, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
import TeamJoinForm from './TeamJoinForm';
import useApiFilter from 'hooks/useApiFilter';
export default function TeamsList() {
const { user } = useUser();
const { formatMessage, labels, messages } = useMessages();
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
useApiFilter();
const [update, setUpdate] = useState(0);
const { get, useQuery } = useApi();
const { data, isLoading, error } = useQuery(['teams', update], () => get(`/teams`));
const hasData = data && data.length !== 0;
const { data, isLoading, error } = useQuery(['teams', update, filter, page, pageSize], () => {
return get(`/teams`, {
filter,
page,
pageSize,
});
});
const hasData = data && data?.data.length !== 0;
const isFiltered = filter;
const { showToast } = useToasts();
const handleSave = () => {
@ -71,15 +84,26 @@ export default function TeamsList() {
return (
<Page loading={isLoading} error={error}>
<PageHeader title={formatMessage(labels.teams)}>
{hasData && (
{(hasData || isFiltered) && (
<Flexbox gap={10}>
{joinButton}
{createButton}
</Flexbox>
)}
</PageHeader>
{hasData && <TeamsTable data={data} onDelete={handleDelete} />}
{!hasData && (
{(hasData || isFiltered) && (
<TeamsTable
data={data}
onDelete={handleDelete}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
)}
{!hasData && !isFiltered && (
<EmptyPlaceholder message={formatMessage(messages.noTeams)}>
<Flexbox gap={10}>
{joinButton}

View file

@ -1,14 +1,21 @@
import SettingsTable from 'components/common/SettingsTable';
import useLocale from 'hooks/useLocale';
import useMessages from 'hooks/useMessages';
import useUser from 'hooks/useUser';
import { ROLES } from 'lib/constants';
import Link from 'next/link';
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
import TeamDeleteForm from './TeamDeleteForm';
import TeamLeaveForm from './TeamLeaveForm';
import useMessages from 'hooks/useMessages';
import useUser from 'hooks/useUser';
import { ROLES } from 'lib/constants';
import SettingsTable from 'components/common/SettingsTable';
import useLocale from 'hooks/useLocale';
export function TeamsTable({ data = [], onDelete }) {
export function TeamsTable({
data = { data: [] },
onDelete,
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const { formatMessage, labels } = useMessages();
const { user } = useUser();
const { dir } = useLocale();
@ -27,7 +34,17 @@ export function TeamsTable({ data = [], onDelete }) {
};
return (
<SettingsTable data={data} columns={columns} cellRender={cellRender}>
<SettingsTable
data={data}
columns={columns}
cellRender={cellRender}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{row => {
const { id, teamUser } = row;
const owner = teamUser.find(({ role }) => role === ROLES.teamOwner);

View file

@ -7,14 +7,27 @@ import UserAddButton from './UserAddButton';
import useApi from 'hooks/useApi';
import useUser from 'hooks/useUser';
import useMessages from 'hooks/useMessages';
import useApiFilter from 'hooks/useApiFilter';
export function UsersList() {
const { formatMessage, labels, messages } = useMessages();
const { user } = useUser();
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
useApiFilter();
const { get, useQuery } = useApi();
const { data, isLoading, error, refetch } = useQuery(['user'], () => get(`/users`), {
enabled: !!user,
});
const { data, isLoading, error, refetch } = useQuery(
['user', filter, page, pageSize],
() =>
get(`/users`, {
filter,
page,
pageSize,
}),
{
enabled: !!user,
},
);
const { showToast } = useToasts();
const hasData = data && data.length !== 0;
@ -33,8 +46,17 @@ export function UsersList() {
<PageHeader title={formatMessage(labels.users)}>
<UserAddButton onSave={handleSave} />
</PageHeader>
{hasData && <UsersTable data={data} onDelete={handleDelete} />}
{!hasData && (
{(hasData || filter) && (
<UsersTable
data={data}
onDelete={handleDelete}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
)}
{!hasData && !filter && (
<EmptyPlaceholder message={formatMessage(messages.noUsers)}>
<UserAddButton onSave={handleSave} />
</EmptyPlaceholder>

View file

@ -8,7 +8,14 @@ import useMessages from 'hooks/useMessages';
import SettingsTable from 'components/common/SettingsTable';
import useLocale from 'hooks/useLocale';
export function UsersTable({ data = [], onDelete }) {
export function UsersTable({
data = { data: [] },
onDelete,
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const { formatMessage, labels } = useMessages();
const { user } = useUser();
const { dateLocale } = useLocale();
@ -36,7 +43,17 @@ export function UsersTable({ data = [], onDelete }) {
};
return (
<SettingsTable data={data} columns={columns} cellRender={cellRender}>
<SettingsTable
data={data}
columns={columns}
cellRender={cellRender}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{(row, keys, rowIndex) => {
return (
<>

View file

@ -8,14 +8,22 @@ import useApi from 'hooks/useApi';
import useUser from 'hooks/useUser';
import useMessages from 'hooks/useMessages';
import { ROLES } from 'lib/constants';
import useApiFilter from 'hooks/useApiFilter';
export function WebsitesList() {
const { formatMessage, labels, messages } = useMessages();
const { user } = useUser();
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
useApiFilter();
const { get, useQuery } = useApi();
const { data, isLoading, error, refetch } = useQuery(
['websites', user?.id],
() => get(`/users/${user?.id}/websites`),
['websites', user?.id, filter, page, pageSize],
() =>
get(`/users/${user?.id}/websites`, {
filter,
page,
pageSize,
}),
{ enabled: !!user },
);
const { showToast } = useToasts();
@ -47,7 +55,15 @@ export function WebsitesList() {
return (
<Page loading={isLoading} error={error}>
<PageHeader title={formatMessage(labels.websites)}>{addButton}</PageHeader>
{hasData && <WebsitesTable data={data} />}
{hasData && (
<WebsitesTable
data={data}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
)}
{!hasData && (
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)}>
{addButton}

View file

@ -4,7 +4,13 @@ import SettingsTable from 'components/common/SettingsTable';
import useMessages from 'hooks/useMessages';
import useConfig from 'hooks/useConfig';
export function WebsitesTable({ data = [] }) {
export function WebsitesTable({
data = [],
filterValue,
onFilterChange,
onPageChange,
onPageSizeChange,
}) {
const { formatMessage, labels } = useMessages();
const { openExternal } = useConfig();
@ -15,7 +21,16 @@ export function WebsitesTable({ data = [] }) {
];
return (
<SettingsTable columns={columns} data={data}>
<SettingsTable
columns={columns}
data={data}
showSearch={true}
showPaging={true}
onFilterChange={onFilterChange}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
{row => {
const { id } = row;

View file

@ -7,7 +7,16 @@ import WebsiteHeader from './WebsiteHeader';
export function WebsiteReportsPage({ websiteId }) {
const { formatMessage, labels } = useMessages();
const { reports, error, isLoading, deleteReport } = useReports(websiteId);
const {
reports,
error,
isLoading,
deleteReport,
filter,
handleFilterChange,
handlePageChange,
handlePageSizeChange,
} = useReports(websiteId);
const handleDelete = async id => {
await deleteReport(id);
@ -26,7 +35,14 @@ export function WebsiteReportsPage({ websiteId }) {
</Button>
</Link>
</Flexbox>
<ReportsTable data={reports} onDelete={handleDelete} />
<ReportsTable
data={reports}
onDelete={handleDelete}
onFilterChange={handleFilterChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
filterValue={filter}
/>
</Page>
);
}