mirror of
https://github.com/umami-software/umami.git
synced 2026-02-12 08:37:13 +01:00
Initial dev on DataTable component.
This commit is contained in:
parent
7107336b49
commit
d6a27b8e99
19 changed files with 223 additions and 53 deletions
|
|
@ -95,7 +95,7 @@
|
||||||
"node-fetch": "^3.2.8",
|
"node-fetch": "^3.2.8",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-basics": "^0.98.0",
|
"react-basics": "^0.100.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.4",
|
"react-error-boundary": "^4.0.4",
|
||||||
|
|
|
||||||
68
src/components/common/DataTable.js
Normal file
68
src/components/common/DataTable.js
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { createContext } from 'react';
|
||||||
|
import { SearchField } from 'react-basics';
|
||||||
|
import { useDataTable } from 'components/hooks/useDataTable';
|
||||||
|
import { useMessages } from 'components/hooks';
|
||||||
|
import Empty from 'components/common/Empty';
|
||||||
|
import Pager from 'components/common/Pager';
|
||||||
|
import styles from './DataTable.module.css';
|
||||||
|
|
||||||
|
const DEFAULT_SEARCH_DELAY = 1000;
|
||||||
|
|
||||||
|
export const DataTableStyles = styles;
|
||||||
|
|
||||||
|
export const DataTableContext = createContext(null);
|
||||||
|
|
||||||
|
export function DataTable({
|
||||||
|
searchDelay,
|
||||||
|
showSearch = true,
|
||||||
|
showPaging = true,
|
||||||
|
children,
|
||||||
|
onChange,
|
||||||
|
}) {
|
||||||
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
|
const dataTable = useDataTable();
|
||||||
|
const { query, setQuery, data, pageInfo, setPageInfo } = dataTable;
|
||||||
|
const { page, pageSize, count } = pageInfo || {};
|
||||||
|
const noResults = Boolean(query && data?.length === 0);
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
onChange?.({ query, page });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = value => {
|
||||||
|
setQuery(value);
|
||||||
|
handleChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = page => {
|
||||||
|
setPageInfo(state => ({ ...state, page }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTableContext.Provider value={dataTable}>
|
||||||
|
{showSearch && (
|
||||||
|
<SearchField
|
||||||
|
className={styles.search}
|
||||||
|
value={query}
|
||||||
|
onChange={handleSearch}
|
||||||
|
delay={searchDelay || DEFAULT_SEARCH_DELAY}
|
||||||
|
autoFocus={true}
|
||||||
|
placeholder={formatMessage(labels.search)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{noResults && <Empty message={formatMessage(messages.noResultsFound)} />}
|
||||||
|
<div className={styles.body}>{children}</div>
|
||||||
|
{showPaging && (
|
||||||
|
<Pager
|
||||||
|
className={styles.pager}
|
||||||
|
page={page}
|
||||||
|
pageSize={pageSize}
|
||||||
|
count={count}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DataTableContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTable;
|
||||||
17
src/components/common/DataTable.module.css
Normal file
17
src/components/common/DataTable.module.css
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.search {
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body td {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pager {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import styles from './Pager.module.css';
|
import classNames from 'classnames';
|
||||||
import { Button, Flexbox, Icon, Icons } from 'react-basics';
|
import { Button, Flexbox, Icon, Icons } from 'react-basics';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
import styles from './Pager.module.css';
|
||||||
|
|
||||||
export function Pager({ page, pageSize, count, onPageChange }) {
|
export function Pager({ page, pageSize, count, onPageChange, className }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const maxPage = Math.ceil(count / pageSize);
|
const maxPage = pageSize && count ? Math.ceil(count / pageSize) : 0;
|
||||||
const lastPage = page === maxPage;
|
const lastPage = page === maxPage;
|
||||||
const firstPage = page === 1;
|
const firstPage = page === 1;
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0 || !maxPage) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +25,7 @@ export function Pager({ page, pageSize, count, onPageChange }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flexbox justifyContent="center" className={styles.container}>
|
<Flexbox justifyContent="center" className={classNames(styles.container, className)}>
|
||||||
<Button onClick={() => handlePageChange(-1)} disabled={firstPage}>
|
<Button onClick={() => handlePageChange(-1)} disabled={firstPage}>
|
||||||
<Icon rotate={90}>
|
<Icon rotate={90}>
|
||||||
<Icons.ChevronDown />
|
<Icons.ChevronDown />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
.container {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
margin: 0 16px;
|
margin: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/components/hooks/useDataTable.js
Normal file
13
src/components/hooks/useDataTable.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { usePaging } from 'components/hooks/usePaging';
|
||||||
|
|
||||||
|
export function useDataTable(config = {}) {
|
||||||
|
const { initialData, initialQuery, initialPageInfo } = config;
|
||||||
|
const [data, setData] = useState(initialData ?? null);
|
||||||
|
const [query, setQuery] = useState(initialQuery ?? '');
|
||||||
|
const { pageInfo, setPageInfo } = usePaging(initialPageInfo);
|
||||||
|
|
||||||
|
return { data, setData, query, setQuery, pageInfo, setPageInfo };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useDataTable;
|
||||||
9
src/components/hooks/usePaging.js
Normal file
9
src/components/hooks/usePaging.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
const DEFAULT_PAGE_INFO = { page: 1, pageSize: 10, total: 0 };
|
||||||
|
|
||||||
|
export function usePaging(initialPageInfo) {
|
||||||
|
const [pageInfo, setPageInfo] = useState(initialPageInfo ?? { ...DEFAULT_PAGE_INFO });
|
||||||
|
|
||||||
|
return { pageInfo, setPageInfo };
|
||||||
|
}
|
||||||
|
|
@ -178,6 +178,7 @@ export const labels = defineMessages({
|
||||||
day: { id: 'label.day', defaultMessage: 'Day' },
|
day: { id: 'label.day', defaultMessage: 'Day' },
|
||||||
date: { id: 'label.date', defaultMessage: 'Date' },
|
date: { id: 'label.date', defaultMessage: 'Date' },
|
||||||
pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' },
|
pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' },
|
||||||
|
search: { id: 'label.search', defaultMessage: 'Search' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const messages = defineMessages({
|
export const messages = defineMessages({
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,17 @@ export function EventsChart({ websiteId, className, token }) {
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const [timezone] = useTimezone();
|
const [timezone] = useTimezone();
|
||||||
const {
|
const {
|
||||||
query: { url, eventName },
|
query: { url, event },
|
||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
|
|
||||||
const { data, isLoading } = useQuery(['events', websiteId, modified, eventName], () =>
|
const { data, isLoading } = useQuery(['events', websiteId, modified, event], () =>
|
||||||
get(`/websites/${websiteId}/events`, {
|
get(`/websites/${websiteId}/events`, {
|
||||||
startAt: +startDate,
|
startAt: +startDate,
|
||||||
endAt: +endDate,
|
endAt: +endDate,
|
||||||
unit,
|
unit,
|
||||||
timezone,
|
timezone,
|
||||||
url,
|
url,
|
||||||
eventName,
|
event,
|
||||||
token,
|
token,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ import classNames from 'classnames';
|
||||||
import Empty from 'components/common/Empty';
|
import Empty from 'components/common/Empty';
|
||||||
import { formatNumber, formatLongNumber } from 'lib/format';
|
import { formatNumber, formatLongNumber } from 'lib/format';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
import styles from './DataTable.module.css';
|
import styles from './ListTable.module.css';
|
||||||
|
|
||||||
export function DataTable({
|
export function ListTable({
|
||||||
data = [],
|
data = [],
|
||||||
title,
|
title,
|
||||||
metric,
|
metric,
|
||||||
|
|
@ -102,4 +102,4 @@ const AnimatedRow = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DataTable;
|
export default ListTable;
|
||||||
|
|
@ -8,7 +8,7 @@ import { percentFilter } from 'lib/filters';
|
||||||
import useDateRange from 'components/hooks/useDateRange';
|
import useDateRange from 'components/hooks/useDateRange';
|
||||||
import usePageQuery from 'components/hooks/usePageQuery';
|
import usePageQuery from 'components/hooks/usePageQuery';
|
||||||
import ErrorMessage from 'components/common/ErrorMessage';
|
import ErrorMessage from 'components/common/ErrorMessage';
|
||||||
import DataTable from './DataTable';
|
import ListTable from './ListTable';
|
||||||
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
|
@ -104,7 +104,7 @@ export function MetricsTable({
|
||||||
<div className={classNames(styles.container, className)}>
|
<div className={classNames(styles.container, className)}>
|
||||||
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
||||||
{error && <ErrorMessage />}
|
{error && <ErrorMessage />}
|
||||||
{data && !error && <DataTable {...props} data={filteredData} className={className} />}
|
{data && !error && <ListTable {...props} data={filteredData} className={className} />}
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
{data && !error && limit && (
|
{data && !error && limit && (
|
||||||
<Link href={router.pathname} as={resolveUrl({ view: type })}>
|
<Link href={router.pathname} as={resolveUrl({ view: type })}>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import useLocale from 'components/hooks/useLocale';
|
import useLocale from 'components/hooks/useLocale';
|
||||||
import useCountryNames from 'components/hooks/useCountryNames';
|
import useCountryNames from 'components/hooks/useCountryNames';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
|
@ -24,7 +24,7 @@ export function RealtimeCountries({ data }) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.countries)}
|
title={formatMessage(labels.countries)}
|
||||||
metric={formatMessage(labels.visitors)}
|
metric={formatMessage(labels.visitors)}
|
||||||
data={data}
|
data={data}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useMemo, useState } from 'react';
|
||||||
import { ButtonGroup, Button, Flexbox } from 'react-basics';
|
import { ButtonGroup, Button, Flexbox } from 'react-basics';
|
||||||
import firstBy from 'thenby';
|
import firstBy from 'thenby';
|
||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
|
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
|
||||||
|
|
@ -82,7 +82,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
{filter === FILTER_REFERRERS && (
|
{filter === FILTER_REFERRERS && (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.referrers)}
|
title={formatMessage(labels.referrers)}
|
||||||
metric={formatMessage(labels.views)}
|
metric={formatMessage(labels.views)}
|
||||||
renderLabel={renderLink}
|
renderLabel={renderLink}
|
||||||
|
|
@ -90,7 +90,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{filter === FILTER_PAGES && (
|
{filter === FILTER_PAGES && (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.pages)}
|
title={formatMessage(labels.pages)}
|
||||||
metric={formatMessage(labels.views)}
|
metric={formatMessage(labels.views)}
|
||||||
renderLabel={renderLink}
|
renderLabel={renderLink}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages } from 'components/hooks';
|
||||||
import { ReportContext } from '../Report';
|
import { ReportContext } from '../Report';
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ export function FunnelTable() {
|
||||||
const { report } = useContext(ReportContext);
|
const { report } = useContext(ReportContext);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<ListTable
|
||||||
data={report?.data}
|
data={report?.data}
|
||||||
title={formatMessage(labels.url)}
|
title={formatMessage(labels.url)}
|
||||||
metric={formatMessage(labels.visitors)}
|
metric={formatMessage(labels.visitors)}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ import PageHeader from 'components/layout/PageHeader';
|
||||||
import WebsiteAddForm from 'components/pages/settings/websites/WebsiteAddForm';
|
import WebsiteAddForm from 'components/pages/settings/websites/WebsiteAddForm';
|
||||||
import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
|
import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
|
||||||
import useApi from 'components/hooks/useApi';
|
import useApi from 'components/hooks/useApi';
|
||||||
import useApiFilter from 'components/hooks/useApiFilter';
|
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
import useUser from 'components/hooks/useUser';
|
import useUser from 'components/hooks/useUser';
|
||||||
import { ROLES } from 'lib/constants';
|
import { ROLES } from 'lib/constants';
|
||||||
import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
|
import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
export function WebsitesList({
|
export function WebsitesList({
|
||||||
showTeam,
|
showTeam,
|
||||||
|
|
@ -15,28 +15,27 @@ export function WebsitesList({
|
||||||
showHeader = true,
|
showHeader = true,
|
||||||
includeTeams,
|
includeTeams,
|
||||||
onlyTeams,
|
onlyTeams,
|
||||||
fetch,
|
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
const [params, setParams] = useState({});
|
||||||
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
|
|
||||||
useApiFilter();
|
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
const { data, isLoading, error, refetch } = useQuery(
|
const { data, isLoading, error, refetch } = useQuery(
|
||||||
['websites', fetch, user?.id, filter, page, pageSize, includeTeams, onlyTeams],
|
['websites', includeTeams, onlyTeams],
|
||||||
() =>
|
() =>
|
||||||
get(`/users/${user?.id}/websites`, {
|
get(`/users/${user?.id}/websites`, {
|
||||||
filter,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
includeTeams,
|
includeTeams,
|
||||||
onlyTeams,
|
onlyTeams,
|
||||||
|
...params,
|
||||||
}),
|
}),
|
||||||
{ enabled: !!user },
|
{ enabled: !!user },
|
||||||
);
|
);
|
||||||
const { showToast } = useToasts();
|
const { showToast } = useToasts();
|
||||||
|
|
||||||
|
const handleChange = params => {
|
||||||
|
setParams(params);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
await refetch();
|
await refetch();
|
||||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||||
|
|
@ -67,10 +66,7 @@ export function WebsitesList({
|
||||||
data={data}
|
data={data}
|
||||||
showTeam={showTeam}
|
showTeam={showTeam}
|
||||||
showEditButton={showEditButton}
|
showEditButton={showEditButton}
|
||||||
onFilterChange={handleFilterChange}
|
onChange={handleChange}
|
||||||
onPageChange={handlePageChange}
|
|
||||||
onPageSizeChange={handlePageSizeChange}
|
|
||||||
filterValue={filter}
|
|
||||||
/>
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,78 @@
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Button, Text, Icon, Icons } from 'react-basics';
|
import { Button, Text, Icon, Icons, GridTable, GridColumn } from 'react-basics';
|
||||||
import SettingsTable from 'components/common/SettingsTable';
|
import SettingsTable from 'components/common/SettingsTable';
|
||||||
import Empty from 'components/common/Empty';
|
import Empty from 'components/common/Empty';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
import useUser from 'components/hooks/useUser';
|
import useUser from 'components/hooks/useUser';
|
||||||
|
import DataTable, { DataTableStyles } from 'components/common/DataTable';
|
||||||
|
|
||||||
export function WebsitesTable({
|
export function WebsitesTable({
|
||||||
|
data = [],
|
||||||
|
filterValue,
|
||||||
|
showTeam,
|
||||||
|
showEditButton,
|
||||||
|
openExternal = false,
|
||||||
|
onChange,
|
||||||
|
}) {
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { user } = useUser();
|
||||||
|
|
||||||
|
const showTable = data && (filterValue || data?.data?.length !== 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataTable onChange={onChange}>
|
||||||
|
{showTable && (
|
||||||
|
<GridTable data={data?.data}>
|
||||||
|
<GridColumn name="name" label={formatMessage(labels.name)} />
|
||||||
|
<GridColumn name="domain" label={formatMessage(labels.domain)} />
|
||||||
|
{showTeam && (
|
||||||
|
<GridColumn name="teamName" label={formatMessage(labels.teamName)}>
|
||||||
|
{row => row.teamWebsite[0]?.team.name}
|
||||||
|
</GridColumn>
|
||||||
|
)}
|
||||||
|
{showTeam && (
|
||||||
|
<GridColumn name="owner" label={formatMessage(labels.owner)}>
|
||||||
|
{row => row.user.username}
|
||||||
|
</GridColumn>
|
||||||
|
)}
|
||||||
|
<GridColumn name="action" label=" " className={DataTableStyles.action}>
|
||||||
|
{row => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
user: { id: ownerId },
|
||||||
|
} = row;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showEditButton && (!showTeam || ownerId === user.id) && (
|
||||||
|
<Link href={`/settings/websites/${id}`}>
|
||||||
|
<Button>
|
||||||
|
<Icon>
|
||||||
|
<Icons.Edit />
|
||||||
|
</Icon>
|
||||||
|
<Text>{formatMessage(labels.edit)}</Text>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
<Link href={`/websites/${id}`} target={openExternal ? '_blank' : null}>
|
||||||
|
<Button>
|
||||||
|
<Icon>
|
||||||
|
<Icons.External />
|
||||||
|
</Icon>
|
||||||
|
<Text>{formatMessage(labels.view)}</Text>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</GridColumn>
|
||||||
|
</GridTable>
|
||||||
|
)}
|
||||||
|
</DataTable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WebsitesTable2({
|
||||||
data = [],
|
data = [],
|
||||||
filterValue,
|
filterValue,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,19 @@ import {
|
||||||
useToasts,
|
useToasts,
|
||||||
} from 'react-basics';
|
} from 'react-basics';
|
||||||
|
|
||||||
|
const TABS = {
|
||||||
|
myWebsites: 'my-websites',
|
||||||
|
teamWebsites: 'team-websites',
|
||||||
|
};
|
||||||
|
|
||||||
export function WebsitesPage() {
|
export function WebsitesPage() {
|
||||||
const { formatMessage, labels, messages } = useMessages();
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
const [tab, setTab] = useState('my-websites');
|
const [tab, setTab] = useState(TABS.myWebsites);
|
||||||
const [fetch, setFetch] = useState(1);
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { cloudMode } = useConfig();
|
const { cloudMode } = useConfig();
|
||||||
const { showToast } = useToasts();
|
const { showToast } = useToasts();
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = () => {
|
||||||
setFetch(fetch + 1);
|
|
||||||
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -54,18 +57,16 @@ export function WebsitesPage() {
|
||||||
<Page>
|
<Page>
|
||||||
<PageHeader title={formatMessage(labels.websites)}>{!cloudMode && addButton}</PageHeader>
|
<PageHeader title={formatMessage(labels.websites)}>{!cloudMode && addButton}</PageHeader>
|
||||||
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30 }}>
|
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30 }}>
|
||||||
<Item key="my-websites">{formatMessage(labels.myWebsites)}</Item>
|
<Item key={TABS.myWebsites}>{formatMessage(labels.myWebsites)}</Item>
|
||||||
<Item key="team-webaites">{formatMessage(labels.teamWebsites)}</Item>
|
<Item key={TABS.teamWebsites}>{formatMessage(labels.teamWebsites)}</Item>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
{tab === TABS.myWebsites && (
|
||||||
{tab === 'my-websites' && (
|
|
||||||
<WebsiteList showEditButton={!cloudMode} showHeader={false} fetch={fetch} />
|
<WebsiteList showEditButton={!cloudMode} showHeader={false} fetch={fetch} />
|
||||||
)}
|
)}
|
||||||
{tab === 'team-webaites' && (
|
{tab === TABS.teamWebsites && (
|
||||||
<WebsiteList
|
<WebsiteList
|
||||||
showEditButton={!cloudMode}
|
showEditButton={!cloudMode}
|
||||||
showHeader={false}
|
showHeader={false}
|
||||||
fetch={fetch}
|
|
||||||
showTeam={true}
|
showTeam={true}
|
||||||
onlyTeams={true}
|
onlyTeams={true}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -7642,10 +7642,10 @@ rc@^1.2.7:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
react-basics@^0.98.0:
|
react-basics@^0.100.0:
|
||||||
version "0.98.0"
|
version "0.100.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.98.0.tgz#b207bedbd9dac749d28ea6de2197a0efe648b78c"
|
resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.100.0.tgz#14a36769af89f3e01641997f897e4073f16f5035"
|
||||||
integrity sha512-ebUigu+s6Iusq14EZTFTTUzdDPYFQEZjeD4feeq3o7dE+ndOVnajEdQ2va/x6CsRBUsWgjLJipfQi0XIrxYupA==
|
integrity sha512-ET6DX/FYAcjGRauBE4jwqwVpd/hKmA2Nu/fi1dakwsv17hkyV5FEAhdWhQAxJX3VnaCH//QysN8+ae12KuNA9g==
|
||||||
dependencies:
|
dependencies:
|
||||||
classnames "^2.3.1"
|
classnames "^2.3.1"
|
||||||
date-fns "^2.29.3"
|
date-fns "^2.29.3"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue