mirror of
https://github.com/umami-software/umami.git
synced 2026-02-09 07:07:17 +01:00
Rewrite admin. (#1713)
* Rewrite admin. * Clean up password forms. * Fix naming issues. * CSS Naming.
This commit is contained in:
parent
f4db04c3c6
commit
e1f99a7d01
113 changed files with 2054 additions and 1872 deletions
|
|
@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import WebsiteList from 'components/pages/WebsiteList';
|
||||
import Button from 'components/common/Button';
|
||||
import { Button } from 'react-basics';
|
||||
import DashboardSettingsButton from 'components/settings/DashboardSettingsButton';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import useDashboard from 'store/dashboard';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useState, useMemo } from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
import classNames from 'classnames';
|
||||
import Button from 'components/common/Button';
|
||||
import { Button } from 'react-basics';
|
||||
import { firstBy } from 'thenby';
|
||||
import useDashboard, { saveDashboard } from 'store/dashboard';
|
||||
import styles from './DashboardEdit.module.css';
|
||||
|
|
|
|||
32
components/pages/ProfileSettings.js
Normal file
32
components/pages/ProfileSettings.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import ProfileDetails from 'components/settings/ProfileDetails';
|
||||
import { useState } from 'react';
|
||||
import { Breadcrumbs, Item, Tabs, useToast } from 'react-basics';
|
||||
import UserPasswordForm from 'components/forms/UserPasswordForm';
|
||||
|
||||
export default function ProfileSettings() {
|
||||
const [tab, setTab] = useState('general');
|
||||
const { toast, showToast } = useToast();
|
||||
|
||||
const handleSave = () => {
|
||||
showToast({ message: 'Saved successfully.', variant: 'success' });
|
||||
};
|
||||
|
||||
return (
|
||||
<Page>
|
||||
{toast}
|
||||
<PageHeader>
|
||||
<Breadcrumbs>
|
||||
<Item>Profile</Item>
|
||||
</Breadcrumbs>
|
||||
</PageHeader>
|
||||
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30, fontSize: 14 }}>
|
||||
<Item key="general">General</Item>
|
||||
<Item key="password">Password</Item>
|
||||
</Tabs>
|
||||
{tab === 'general' && <ProfileDetails />}
|
||||
{tab === 'password' && <UserPasswordForm onSave={handleSave} />}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,50 +1,23 @@
|
|||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useRouter } from 'next/router';
|
||||
import Page from 'components/layout/Page';
|
||||
import MenuLayout from 'components/layout/MenuLayout';
|
||||
import WebsiteSettings from 'components/settings/WebsiteSettings';
|
||||
import UserSettings from 'components/settings/UserSettings';
|
||||
import ProfileSettings from 'components/settings/ProfileSettings';
|
||||
import useUser from 'hooks/useUser';
|
||||
import Layout from 'components/layout/Layout';
|
||||
import Menu from 'components/nav/Nav';
|
||||
import useRequireLogin from 'hooks/useRequireLogin';
|
||||
import styles from './Settings.module.css';
|
||||
|
||||
const WEBSITES = '/settings';
|
||||
const ACCOUNTS = '/settings/users';
|
||||
const PROFILE = '/settings/profile';
|
||||
export default function Settings({ children }) {
|
||||
const { user: loggedIn } = useRequireLogin();
|
||||
|
||||
export default function Settings() {
|
||||
const { user } = useUser();
|
||||
const [option, setOption] = useState(WEBSITES);
|
||||
const router = useRouter();
|
||||
const { pathname } = router;
|
||||
|
||||
if (!user) {
|
||||
if (!loggedIn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
label: <FormattedMessage id="label.websites" defaultMessage="Websites" />,
|
||||
value: WEBSITES,
|
||||
},
|
||||
{
|
||||
label: <FormattedMessage id="label.users" defaultMessage="Users" />,
|
||||
value: ACCOUNTS,
|
||||
hidden: !user?.isAdmin,
|
||||
},
|
||||
{
|
||||
label: <FormattedMessage id="label.profile" defaultMessage="Profile" />,
|
||||
value: PROFILE,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<MenuLayout menu={menuOptions} selectedOption={option} onMenuSelect={setOption}>
|
||||
{pathname === WEBSITES && <WebsiteSettings />}
|
||||
{pathname === ACCOUNTS && <UserSettings />}
|
||||
{pathname === PROFILE && <ProfileSettings />}
|
||||
</MenuLayout>
|
||||
</Page>
|
||||
<Layout>
|
||||
<div className={styles.dashboard}>
|
||||
<div className={styles.nav}>
|
||||
<Menu />
|
||||
</div>
|
||||
<div className={styles.content}>{children}</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
16
components/pages/Settings.module.css
Normal file
16
components/pages/Settings.module.css
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.dashboard {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nav {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
background: var(--base50);
|
||||
flex: 1;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
58
components/pages/TeamDetails.js
Normal file
58
components/pages/TeamDetails.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Breadcrumbs, Item, Tabs, useToast } from 'react-basics';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApi } from 'next-basics';
|
||||
import Link from 'next/link';
|
||||
import Page from 'components/layout/Page';
|
||||
import TeamEditForm from 'components/forms/TeamEditForm';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import TeamMembersTable from '../tables/TeamMembersTable';
|
||||
|
||||
export default function TeamDetails({ teamId }) {
|
||||
const [values, setValues] = useState(null);
|
||||
const [tab, setTab] = useState('general');
|
||||
const { get } = useApi(getAuthToken());
|
||||
const { toast, showToast } = useToast();
|
||||
const { data, isLoading } = useQuery(
|
||||
['team', teamId],
|
||||
() => {
|
||||
if (teamId) {
|
||||
return get(`/teams/${teamId}`);
|
||||
}
|
||||
},
|
||||
{ cacheTime: 0 },
|
||||
);
|
||||
|
||||
const handleSave = data => {
|
||||
showToast({ message: 'Saved successfully.', variant: 'success' });
|
||||
setValues(state => ({ ...state, ...data }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Page loading={isLoading || !values}>
|
||||
{toast}
|
||||
<PageHeader>
|
||||
<Breadcrumbs>
|
||||
<Item>
|
||||
<Link href="/teams">Teams</Link>
|
||||
</Item>
|
||||
<Item>{values?.name}</Item>
|
||||
</Breadcrumbs>
|
||||
</PageHeader>
|
||||
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30, fontSize: 14 }}>
|
||||
<Item key="general">General</Item>
|
||||
<Item key="members">Members</Item>
|
||||
<Item key="websites">Websites</Item>
|
||||
</Tabs>
|
||||
{tab === 'general' && <TeamEditForm teamId={teamId} data={values} onSave={handleSave} />}
|
||||
{tab === 'members' && <TeamMembersTable teamId={teamId} />}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
65
components/pages/TeamsList.js
Normal file
65
components/pages/TeamsList.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { useState } from 'react';
|
||||
import { Button, Icon, Modal, useToast, Flexbox } from 'react-basics';
|
||||
import { useApi } from 'next-basics';
|
||||
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||
import TeamAddForm from 'components/forms/TeamAddForm';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import TeamsTable from 'components/tables/TeamsTable';
|
||||
import Page from 'components/layout/Page';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export default function TeamsList() {
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [update, setUpdate] = useState(0);
|
||||
const { get } = useApi(getAuthToken());
|
||||
const { data, isLoading, error } = useQuery(['teams', update], () => get(`/teams`));
|
||||
const hasData = data && data.length !== 0;
|
||||
const { toast, showToast } = useToast();
|
||||
|
||||
const columns = [
|
||||
{ name: 'name', label: 'Name', style: { flex: 2 } },
|
||||
{ name: 'action', label: ' ' },
|
||||
];
|
||||
|
||||
const handleAdd = () => {
|
||||
setEdit(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
setEdit(false);
|
||||
setUpdate(state => state + 1);
|
||||
showToast({ message: 'Team saved.', variant: 'success' });
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page loading={isLoading} error={error}>
|
||||
{toast}
|
||||
<PageHeader title="Teams">
|
||||
<Button onClick={handleAdd}>
|
||||
<Icon icon="plus" /> Create team
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
{hasData && <TeamsTable columns={columns} rows={data} />}
|
||||
{!hasData && (
|
||||
<EmptyPlaceholder msg="You don't have any teams configured.">
|
||||
<Flexbox justifyContent="center" alignItems="center">
|
||||
<Button variant="primary" onClick={handleAdd}>
|
||||
<Icon icon="plus" /> Create team
|
||||
</Button>
|
||||
</Flexbox>
|
||||
</EmptyPlaceholder>
|
||||
)}
|
||||
{edit && (
|
||||
<Modal title="Create team" onClose={handleClose}>
|
||||
{close => <TeamAddForm onSave={handleSave} onClose={close} />}
|
||||
</Modal>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
import { Row, Column } from 'react-basics';
|
||||
import DropDown from 'components/common/DropDown';
|
||||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import EventsChart from 'components/metrics/EventsChart';
|
||||
import WebsiteChart from 'components/metrics/WebsiteChart';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import DropDown from 'components/common/DropDown';
|
||||
import WebsiteChart from 'components/metrics/WebsiteChart';
|
||||
import EventsChart from 'components/metrics/EventsChart';
|
||||
import Button from 'components/common/Button';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import { Button, Column, Row } from 'react-basics';
|
||||
import styles from './TestConsole.module.css';
|
||||
|
||||
export default function TestConsole() {
|
||||
|
|
|
|||
30
components/pages/UserDelete.js
Normal file
30
components/pages/UserDelete.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import UserDeleteForm from 'components/forms/UserDeleteForm';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { Button, Form, FormRow, Modal } from 'react-basics';
|
||||
|
||||
export default function UserDelete({ userId, onSave }) {
|
||||
const [modal, setModal] = useState(null);
|
||||
const router = useRouter();
|
||||
|
||||
const handleDelete = async () => {
|
||||
onSave();
|
||||
await router.push('/users');
|
||||
};
|
||||
|
||||
const handleClose = () => setModal(null);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<FormRow label="Delete user">
|
||||
<p>All user data will be deleted.</p>
|
||||
<Button onClick={() => setModal('delete')}>Delete</Button>
|
||||
</FormRow>
|
||||
{modal === 'delete' && (
|
||||
<Modal title="Delete user" onClose={handleClose}>
|
||||
{close => <UserDeleteForm userId={userId} onSave={handleDelete} onClose={close} />}
|
||||
</Modal>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
69
components/pages/UserSettings.js
Normal file
69
components/pages/UserSettings.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import UserDelete from 'components/pages/UserDelete';
|
||||
import UserEditForm from 'components/forms/UserEditForm';
|
||||
import UserPasswordForm from 'components/forms/UserPasswordForm';
|
||||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import { useApi } from 'next-basics';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Breadcrumbs, Item, Tabs, useToast } from 'react-basics';
|
||||
|
||||
export default function UserSettings({ userId }) {
|
||||
const [values, setValues] = useState(null);
|
||||
const [tab, setTab] = useState('general');
|
||||
const { get } = useApi(getAuthToken());
|
||||
const { toast, showToast } = useToast();
|
||||
const router = useRouter();
|
||||
const { data, isLoading } = useQuery(
|
||||
['user', userId],
|
||||
() => {
|
||||
if (userId) {
|
||||
return get(`/users/${userId}`);
|
||||
}
|
||||
},
|
||||
{ cacheTime: 0 },
|
||||
);
|
||||
|
||||
const handleSave = data => {
|
||||
showToast({ message: 'Saved successfully.', variant: 'success' });
|
||||
if (data) {
|
||||
setValues(state => ({ ...state, ...data }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
showToast({ message: 'Deleted successfully.', variant: 'danger' });
|
||||
await router.push('/users');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Page loading={isLoading || !values}>
|
||||
{toast}
|
||||
<PageHeader>
|
||||
<Breadcrumbs>
|
||||
<Item>
|
||||
<Link href="/users">Users</Link>
|
||||
</Item>
|
||||
<Item>{values?.username}</Item>
|
||||
</Breadcrumbs>
|
||||
</PageHeader>
|
||||
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30, fontSize: 14 }}>
|
||||
<Item key="general">General</Item>
|
||||
<Item key="password">Password</Item>
|
||||
<Item key="delete">Danger Zone</Item>
|
||||
</Tabs>
|
||||
{tab === 'general' && <UserEditForm userId={userId} data={values} onSave={handleSave} />}
|
||||
{tab === 'password' && <UserPasswordForm userId={userId} data={values} onSave={handleSave} />}
|
||||
{tab === 'delete' && <UserDelete userId={userId} onSave={handleDelete} />}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
45
components/pages/UsersList.js
Normal file
45
components/pages/UsersList.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import Page from 'components/layout/Page';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import UsersTable from 'components/tables/UsersTable';
|
||||
import { useState } from 'react';
|
||||
import { Button, Icon, useToast } from 'react-basics';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useApi } from 'next-basics';
|
||||
|
||||
export default function UsersList() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState();
|
||||
const { toast, showToast } = useToast();
|
||||
const { post } = useApi(getAuthToken());
|
||||
const { mutate, isLoading } = useMutation(data => post('/api-key', data));
|
||||
|
||||
const handleSave = () => {
|
||||
mutate(
|
||||
{},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
showToast({ message: 'API key saved.', variant: 'success' });
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page loading={loading || isLoading} error={error}>
|
||||
{toast}
|
||||
<PageHeader title="Users">
|
||||
<Button onClick={handleSave}>
|
||||
<Icon icon="plus" /> Create user
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<UsersTable
|
||||
onLoading={({ isLoading, error }) => {
|
||||
setLoading(isLoading);
|
||||
setError(error);
|
||||
}}
|
||||
onAddKeyClick={handleSave}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ export default function WebsiteList({ websites, showCharts, limit }) {
|
|||
return (
|
||||
<Page>
|
||||
<EmptyPlaceholder msg={formatMessage(messages.noWebsites)}>
|
||||
<Link href="/settings" icon={<Arrow />} iconRight>
|
||||
<Link href="/websites" icon={<Arrow />} iconRight>
|
||||
{formatMessage(messages.goToSettngs)}
|
||||
</Link>
|
||||
</EmptyPlaceholder>
|
||||
|
|
|
|||
76
components/pages/WebsiteSettings.js
Normal file
76
components/pages/WebsiteSettings.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Breadcrumbs, Item, Tabs, useToast, Button, Icon } from 'react-basics';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApi } from 'next-basics';
|
||||
import Link from 'next/link';
|
||||
import Page from 'components/layout/Page';
|
||||
import WebsiteEditForm from 'components/forms/WebsiteEditForm';
|
||||
import WebsiteReset from 'components/forms/WebsiteReset';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import TrackingCodeForm from 'components/forms/TrackingCodeForm';
|
||||
import ShareUrlForm from 'components/forms/ShareUrlForm';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import ExternalLink from 'assets/external-link.svg';
|
||||
|
||||
export default function Websites({ websiteId }) {
|
||||
const [values, setValues] = useState(null);
|
||||
const [tab, setTab] = useState('general');
|
||||
const { get } = useApi(getAuthToken());
|
||||
const { toast, showToast } = useToast();
|
||||
const { data, isLoading } = useQuery(
|
||||
['website', websiteId],
|
||||
() => {
|
||||
if (websiteId) {
|
||||
return get(`/websites/${websiteId}`);
|
||||
}
|
||||
},
|
||||
{ cacheTime: 0 },
|
||||
);
|
||||
|
||||
const handleSave = data => {
|
||||
showToast({ message: 'Saved successfully.', variant: 'success' });
|
||||
setValues(state => ({ ...state, ...data }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Page loading={isLoading || !values}>
|
||||
{toast}
|
||||
<PageHeader>
|
||||
<Breadcrumbs>
|
||||
<Item>
|
||||
<Link href="/websites">Websites</Link>
|
||||
</Item>
|
||||
<Item>{values?.name}</Item>
|
||||
</Breadcrumbs>
|
||||
<Link href={`/analytics/websites/${websiteId}`}>
|
||||
<a target="_blank">
|
||||
<Button variant="primary">
|
||||
<Icon>
|
||||
<ExternalLink />
|
||||
</Icon>
|
||||
View
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</PageHeader>
|
||||
<Tabs selectedKey={tab} onSelect={setTab} style={{ marginBottom: 30, fontSize: 14 }}>
|
||||
<Item key="general">General</Item>
|
||||
<Item key="tracking">Tracking code</Item>
|
||||
<Item key="share">Share URL</Item>
|
||||
<Item key="danger">Danger zone</Item>
|
||||
</Tabs>
|
||||
{tab === 'general' && (
|
||||
<WebsiteEditForm websiteId={websiteId} data={values} onSave={handleSave} />
|
||||
)}
|
||||
{tab === 'tracking' && <TrackingCodeForm websiteId={websiteId} data={values} />}
|
||||
{tab === 'share' && <ShareUrlForm websiteId={websiteId} data={values} onSave={handleSave} />}
|
||||
{tab === 'danger' && <WebsiteReset websiteId={websiteId} onSave={handleSave} />}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
70
components/pages/WebsitesList.js
Normal file
70
components/pages/WebsitesList.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { useState } from 'react';
|
||||
import { Button, Icon, Modal, useToast, Flexbox } from 'react-basics';
|
||||
import { useApi } from 'next-basics';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||
import WebsiteAddForm from 'components/forms/WebsiteAddForm';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import WebsitesTable from 'components/tables/WebsitesTable';
|
||||
import Page from 'components/layout/Page';
|
||||
import { getAuthToken } from 'lib/client';
|
||||
import useUser from 'hooks/useUser';
|
||||
|
||||
export default function WebsitesList() {
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [update, setUpdate] = useState(0);
|
||||
const { get } = useApi(getAuthToken());
|
||||
const { user } = useUser();
|
||||
const { data, isLoading, error } = useQuery(['websites', update], () =>
|
||||
get(`/users/${user.id}/websites`),
|
||||
);
|
||||
const hasData = data && data.length !== 0;
|
||||
const { toast, showToast } = useToast();
|
||||
|
||||
const columns = [
|
||||
{ name: 'name', label: 'Name', style: { flex: 2 } },
|
||||
{ name: 'domain', label: 'Domain' },
|
||||
{ name: 'action', label: ' ' },
|
||||
];
|
||||
|
||||
const handleAdd = () => {
|
||||
setEdit(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
setEdit(false);
|
||||
setUpdate(state => state + 1);
|
||||
showToast({ message: 'Website saved.', variant: 'success' });
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page loading={isLoading} error={error}>
|
||||
{toast}
|
||||
<PageHeader title="Websites">
|
||||
<Button onClick={handleAdd}>
|
||||
<Icon icon="plus" /> Add website
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
{hasData && <WebsitesTable columns={columns} rows={data} />}
|
||||
{!hasData && (
|
||||
<EmptyPlaceholder msg="You don't have any websites configured.">
|
||||
<Flexbox justifyContent="center" alignItems="center">
|
||||
<Button variant="primary" onClick={handleAdd}>
|
||||
<Icon icon="plus" /> Add website
|
||||
</Button>
|
||||
</Flexbox>
|
||||
</EmptyPlaceholder>
|
||||
)}
|
||||
{edit && (
|
||||
<Modal title="Add website" onClose={handleClose}>
|
||||
{close => <WebsiteAddForm onSave={handleSave} onClose={close} />}
|
||||
</Modal>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue