commit
4047a7ec23
378 changed files with 29334 additions and 0 deletions
132
components/settings/AccountSettings.js
Normal file
132
components/settings/AccountSettings.js
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Link from 'next/link';
|
||||
import classNames from 'classnames';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import Button from 'components/common/Button';
|
||||
import Icon from 'components/common/Icon';
|
||||
import Table from 'components/common/Table';
|
||||
import Modal from 'components/common/Modal';
|
||||
import Toast from 'components/common/Toast';
|
||||
import AccountEditForm from 'components/forms/AccountEditForm';
|
||||
import ButtonLayout from 'components/layout/ButtonLayout';
|
||||
import DeleteForm from 'components/forms/DeleteForm';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import Pen from 'assets/pen.svg';
|
||||
import Plus from 'assets/plus.svg';
|
||||
import Trash from 'assets/trash.svg';
|
||||
import Check from 'assets/check.svg';
|
||||
import LinkIcon from 'assets/external-link.svg';
|
||||
import styles from './AccountSettings.module.css';
|
||||
|
||||
export default function AccountSettings() {
|
||||
const [addAccount, setAddAccount] = useState();
|
||||
const [editAccount, setEditAccount] = useState();
|
||||
const [deleteAccount, setDeleteAccount] = useState();
|
||||
const [saved, setSaved] = useState(0);
|
||||
const [message, setMessage] = useState();
|
||||
const { data } = useFetch(`/api/accounts`, {}, [saved]);
|
||||
|
||||
const Checkmark = ({ is_admin }) => (is_admin ? <Icon icon={<Check />} size="medium" /> : null);
|
||||
|
||||
const DashboardLink = row => (
|
||||
<Link href={`/dashboard/${row.user_id}/${row.username}`}>
|
||||
<a>
|
||||
<Icon icon={<LinkIcon />} />
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
|
||||
const Buttons = row =>
|
||||
row.username !== 'admin' ? (
|
||||
<ButtonLayout align="right">
|
||||
<Button icon={<Pen />} size="small" onClick={() => setEditAccount(row)}>
|
||||
<FormattedMessage id="label.edit" defaultMessage="Edit" />
|
||||
</Button>
|
||||
<Button icon={<Trash />} size="small" onClick={() => setDeleteAccount(row)}>
|
||||
<FormattedMessage id="label.delete" defaultMessage="Delete" />
|
||||
</Button>
|
||||
</ButtonLayout>
|
||||
) : null;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'username',
|
||||
label: <FormattedMessage id="label.username" defaultMessage="Username" />,
|
||||
className: 'col-4 col-md-3',
|
||||
},
|
||||
{
|
||||
key: 'is_admin',
|
||||
label: <FormattedMessage id="label.administrator" defaultMessage="Administrator" />,
|
||||
className: 'col-4 col-md-3',
|
||||
render: Checkmark,
|
||||
},
|
||||
{
|
||||
key: 'dashboard',
|
||||
label: <FormattedMessage id="label.dashboard" defaultMessage="Dashboard" />,
|
||||
className: 'col-4 col-md-3',
|
||||
render: DashboardLink,
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
className: classNames(styles.buttons, 'col-12 col-md-3 pt-2 pt-md-0'),
|
||||
render: Buttons,
|
||||
},
|
||||
];
|
||||
|
||||
function handleSave() {
|
||||
setSaved(state => state + 1);
|
||||
setMessage(<FormattedMessage id="message.save-success" defaultMessage="Saved successfully." />);
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
setEditAccount(null);
|
||||
setAddAccount(null);
|
||||
setDeleteAccount(null);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
<div>
|
||||
<FormattedMessage id="label.accounts" defaultMessage="Accounts" />
|
||||
</div>
|
||||
<Button icon={<Plus />} size="small" onClick={() => setAddAccount(true)}>
|
||||
<FormattedMessage id="label.add-account" defaultMessage="Add account" />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Table columns={columns} rows={data} />
|
||||
{editAccount && (
|
||||
<Modal title={<FormattedMessage id="label.edit-account" defaultMessage="Edit account" />}>
|
||||
<AccountEditForm
|
||||
values={{ ...editAccount, password: '' }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{addAccount && (
|
||||
<Modal title={<FormattedMessage id="label.add-account" defaultMessage="Add account" />}>
|
||||
<AccountEditForm onSave={handleSave} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{deleteAccount && (
|
||||
<Modal
|
||||
title={<FormattedMessage id="label.delete-account" defaultMessage="Delete account" />}
|
||||
>
|
||||
<DeleteForm
|
||||
values={{ type: 'account', id: deleteAccount.user_id, name: deleteAccount.username }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{message && <Toast message={message} onClose={() => setMessage(null)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
5
components/settings/AccountSettings.module.css
Normal file
5
components/settings/AccountSettings.module.css
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
}
|
||||
28
components/settings/DateRangeSetting.js
Normal file
28
components/settings/DateRangeSetting.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import DateFilter from 'components/common/DateFilter';
|
||||
import Button from 'components/common/Button';
|
||||
import useDateRange from 'hooks/useDateRange';
|
||||
import { DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||
import { getDateRange } from 'lib/date';
|
||||
import styles from './DateRangeSetting.module.css';
|
||||
import useLocale from 'hooks/useLocale';
|
||||
|
||||
export default function DateRangeSetting() {
|
||||
const { locale } = useLocale();
|
||||
const [dateRange, setDateRange] = useDateRange();
|
||||
const { startDate, endDate, value } = dateRange;
|
||||
|
||||
function handleReset() {
|
||||
setDateRange(getDateRange(DEFAULT_DATE_RANGE, locale));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DateFilter value={value} startDate={startDate} endDate={endDate} onChange={setDateRange} />
|
||||
<Button className={styles.button} size="small" onClick={handleReset}>
|
||||
<FormattedMessage id="label.reset" defaultMessage="Reset" />
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
components/settings/DateRangeSetting.module.css
Normal file
3
components/settings/DateRangeSetting.module.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
26
components/settings/LanguageButton.js
Normal file
26
components/settings/LanguageButton.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import { languages } from 'lib/lang';
|
||||
import useLocale from 'hooks/useLocale';
|
||||
import MenuButton from 'components/common/MenuButton';
|
||||
import Globe from 'assets/globe.svg';
|
||||
import styles from './LanguageButton.module.css';
|
||||
|
||||
export default function LanguageButton() {
|
||||
const { locale, saveLocale } = useLocale();
|
||||
const menuOptions = Object.keys(languages).map(key => ({ ...languages[key], value: key }));
|
||||
|
||||
function handleSelect(value) {
|
||||
saveLocale(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuButton
|
||||
icon={<Globe />}
|
||||
options={menuOptions}
|
||||
value={locale}
|
||||
menuClassName={styles.menu}
|
||||
onSelect={handleSelect}
|
||||
hideLabel
|
||||
/>
|
||||
);
|
||||
}
|
||||
25
components/settings/LanguageButton.module.css
Normal file
25
components/settings/LanguageButton.module.css
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
.menu {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
min-width: 560px;
|
||||
max-width: 100vw;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.menu div {
|
||||
border-radius: 5px;
|
||||
min-width: calc(100% / 3);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.menu {
|
||||
min-width: 90vw;
|
||||
transform: translateX(calc(40vw));
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.menu div {
|
||||
min-width: 50%;
|
||||
}
|
||||
}
|
||||
72
components/settings/ProfileSettings.js
Normal file
72
components/settings/ProfileSettings.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useSelector } from 'react-redux';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import Button from 'components/common/Button';
|
||||
import Modal from 'components/common/Modal';
|
||||
import Toast from 'components/common/Toast';
|
||||
import ChangePasswordForm from 'components/forms/ChangePasswordForm';
|
||||
import TimezoneSetting from 'components/settings/TimezoneSetting';
|
||||
import Dots from 'assets/ellipsis-h.svg';
|
||||
import styles from './ProfileSettings.module.css';
|
||||
import DateRangeSetting from './DateRangeSetting';
|
||||
import useEscapeKey from 'hooks/useEscapeKey';
|
||||
|
||||
export default function ProfileSettings() {
|
||||
const user = useSelector(state => state.user);
|
||||
const [changePassword, setChangePassword] = useState(false);
|
||||
const [message, setMessage] = useState();
|
||||
const { user_id } = user;
|
||||
|
||||
function handleSave() {
|
||||
setChangePassword(false);
|
||||
setMessage(<FormattedMessage id="message.save-success" defaultMessage="Saved successfully." />);
|
||||
}
|
||||
|
||||
useEscapeKey(() => {
|
||||
setChangePassword(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
<div>
|
||||
<FormattedMessage id="label.profile" defaultMessage="Profile" />
|
||||
</div>
|
||||
<Button icon={<Dots />} size="small" onClick={() => setChangePassword(true)}>
|
||||
<FormattedMessage id="label.change-password" defaultMessage="Change password" />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<dl className={styles.list}>
|
||||
<dt>
|
||||
<FormattedMessage id="label.username" defaultMessage="Username" />
|
||||
</dt>
|
||||
<dd>{user.username}</dd>
|
||||
<dt>
|
||||
<FormattedMessage id="label.timezone" defaultMessage="Timezone" />
|
||||
</dt>
|
||||
<dd>
|
||||
<TimezoneSetting />
|
||||
</dd>
|
||||
<dt>
|
||||
<FormattedMessage id="label.default-date-range" defaultMessage="Default date range" />
|
||||
</dt>
|
||||
<dd>
|
||||
<DateRangeSetting />
|
||||
</dd>
|
||||
</dl>
|
||||
{changePassword && (
|
||||
<Modal
|
||||
title={<FormattedMessage id="label.change-password" defaultMessage="Change password" />}
|
||||
>
|
||||
<ChangePasswordForm
|
||||
values={{ user_id }}
|
||||
onSave={handleSave}
|
||||
onClose={() => setChangePassword(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{message && <Toast message={message} onClose={() => setMessage(null)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
components/settings/ProfileSettings.module.css
Normal file
3
components/settings/ProfileSettings.module.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.list dd {
|
||||
display: flex;
|
||||
}
|
||||
43
components/settings/ThemeButton.js
Normal file
43
components/settings/ThemeButton.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { useTransition, animated } from 'react-spring';
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import Sun from 'assets/sun.svg';
|
||||
import Moon from 'assets/moon.svg';
|
||||
import styles from './ThemeButton.module.css';
|
||||
import Icon from '../common/Icon';
|
||||
|
||||
export default function ThemeButton() {
|
||||
const [theme, setTheme] = useTheme();
|
||||
|
||||
const transitions = useTransition(theme, theme => theme, {
|
||||
from: {
|
||||
opacity: 0,
|
||||
transform: `translateY(${theme === 'light' ? '20px' : '-20px'}) scale(0.5)`,
|
||||
},
|
||||
enter: { opacity: 1, transform: 'translateY(0px) scale(1)' },
|
||||
leave: {
|
||||
opacity: 0,
|
||||
transform: `translateY(${theme === 'light' ? '-20px' : '20px'}) scale(0.5)`,
|
||||
},
|
||||
});
|
||||
|
||||
function handleClick() {
|
||||
setTheme(theme === 'light' ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.button} onClick={handleClick}>
|
||||
{transitions.map(({ item, key, props }) =>
|
||||
item === 'light' ? (
|
||||
<animated.div key={key} style={props}>
|
||||
<Icon icon={<Sun />} />
|
||||
</animated.div>
|
||||
) : (
|
||||
<animated.div key={key} style={props}>
|
||||
<Icon icon={<Moon />} />
|
||||
</animated.div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
13
components/settings/ThemeButton.module.css
Normal file
13
components/settings/ThemeButton.module.css
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.button {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.button svg {
|
||||
position: absolute;
|
||||
}
|
||||
31
components/settings/TimezoneSetting.js
Normal file
31
components/settings/TimezoneSetting.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { listTimeZones } from 'timezone-support';
|
||||
import DropDown from 'components/common/DropDown';
|
||||
import Button from 'components/common/Button';
|
||||
import useTimezone from 'hooks/useTimezone';
|
||||
import { getTimezone } from 'lib/date';
|
||||
import styles from './TimezoneSetting.module.css';
|
||||
|
||||
export default function TimezoneSetting() {
|
||||
const [timezone, saveTimezone] = useTimezone();
|
||||
const options = listTimeZones().map(n => ({ label: n, value: n }));
|
||||
|
||||
function handleReset() {
|
||||
saveTimezone(getTimezone());
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropDown
|
||||
menuClassName={styles.menu}
|
||||
value={timezone}
|
||||
options={options}
|
||||
onChange={saveTimezone}
|
||||
/>
|
||||
<Button className={styles.button} size="small" onClick={handleReset}>
|
||||
<FormattedMessage id="label.reset" defaultMessage="Reset" />
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
8
components/settings/TimezoneSetting.module.css
Normal file
8
components/settings/TimezoneSetting.module.css
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.menu {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
47
components/settings/UserButton.js
Normal file
47
components/settings/UserButton.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useRouter } from 'next/router';
|
||||
import MenuButton from 'components/common/MenuButton';
|
||||
import Icon from 'components/common/Icon';
|
||||
import User from 'assets/user.svg';
|
||||
import Chevron from 'assets/chevron-down.svg';
|
||||
import styles from './UserButton.module.css';
|
||||
|
||||
export default function UserButton() {
|
||||
const user = useSelector(state => state.user);
|
||||
const router = useRouter();
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="label.logged-in-as"
|
||||
defaultMessage="Logged in as {username}"
|
||||
values={{ username: <b>{user.username}</b> }}
|
||||
/>
|
||||
),
|
||||
value: 'username',
|
||||
className: styles.username,
|
||||
},
|
||||
{ label: <FormattedMessage id="label.profile" defaultMessage="Profile" />, value: 'profile' },
|
||||
{ label: <FormattedMessage id="label.logout" defaultMessage="Logout" />, value: 'logout' },
|
||||
];
|
||||
|
||||
function handleSelect(value) {
|
||||
if (value === 'logout') {
|
||||
router.push('/logout');
|
||||
} else if (value === 'profile') {
|
||||
router.push('/settings/profile');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuButton
|
||||
icon={<Icon icon={<User />} size="large" />}
|
||||
value={<Icon icon={<Chevron />} size="small" />}
|
||||
options={menuOptions}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
7
components/settings/UserButton.module.css
Normal file
7
components/settings/UserButton.module.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.username {
|
||||
border-bottom: 1px solid var(--gray500);
|
||||
}
|
||||
|
||||
.username:hover {
|
||||
background: var(--gray50);
|
||||
}
|
||||
186
components/settings/WebsiteSettings.js
Normal file
186
components/settings/WebsiteSettings.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import Link from 'components/common/Link';
|
||||
import Table from 'components/common/Table';
|
||||
import Button from 'components/common/Button';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import Modal from 'components/common/Modal';
|
||||
import WebsiteEditForm from 'components/forms/WebsiteEditForm';
|
||||
import ResetForm from 'components/forms/ResetForm';
|
||||
import DeleteForm from 'components/forms/DeleteForm';
|
||||
import TrackingCodeForm from 'components/forms/TrackingCodeForm';
|
||||
import ShareUrlForm from 'components/forms/ShareUrlForm';
|
||||
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||
import ButtonLayout from 'components/layout/ButtonLayout';
|
||||
import Toast from 'components/common/Toast';
|
||||
import Favicon from 'components/common/Favicon';
|
||||
import Pen from 'assets/pen.svg';
|
||||
import Trash from 'assets/trash.svg';
|
||||
import Reset from 'assets/redo.svg';
|
||||
import Plus from 'assets/plus.svg';
|
||||
import Code from 'assets/code.svg';
|
||||
import LinkIcon from 'assets/link.svg';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import styles from './WebsiteSettings.module.css';
|
||||
|
||||
export default function WebsiteSettings() {
|
||||
const [editWebsite, setEditWebsite] = useState();
|
||||
const [resetWebsite, setResetWebsite] = useState();
|
||||
const [deleteWebsite, setDeleteWebsite] = useState();
|
||||
const [addWebsite, setAddWebsite] = useState();
|
||||
const [showCode, setShowCode] = useState();
|
||||
const [showUrl, setShowUrl] = useState();
|
||||
const [saved, setSaved] = useState(0);
|
||||
const [message, setMessage] = useState();
|
||||
const { data } = useFetch(`/api/websites`, {}, [saved]);
|
||||
|
||||
const Buttons = row => (
|
||||
<ButtonLayout align="right">
|
||||
{row.share_id && (
|
||||
<Button
|
||||
icon={<LinkIcon />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="message.get-share-url" defaultMessage="Get share URL" />}
|
||||
tooltipId={`button-share-${row.website_id}`}
|
||||
onClick={() => setShowUrl(row)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
icon={<Code />}
|
||||
size="small"
|
||||
tooltip={
|
||||
<FormattedMessage id="message.get-tracking-code" defaultMessage="Get tracking code" />
|
||||
}
|
||||
tooltipId={`button-code-${row.website_id}`}
|
||||
onClick={() => setShowCode(row)}
|
||||
/>
|
||||
<Button icon={<Pen />} size="small" onClick={() => setEditWebsite(row)}>
|
||||
<FormattedMessage id="label.edit" defaultMessage="Edit" />
|
||||
</Button>
|
||||
<Button icon={<Reset />} size="small" onClick={() => setResetWebsite(row)}>
|
||||
<FormattedMessage id="label.reset" defaultMessage="Reset" />
|
||||
</Button>
|
||||
<Button icon={<Trash />} size="small" onClick={() => setDeleteWebsite(row)}>
|
||||
<FormattedMessage id="label.delete" defaultMessage="Delete" />
|
||||
</Button>
|
||||
</ButtonLayout>
|
||||
);
|
||||
|
||||
const DetailsLink = ({ website_id, name, domain }) => (
|
||||
<Link href="/website/[...id]" as={`/website/${website_id}/${name}`}>
|
||||
<Favicon domain={domain} />
|
||||
{name}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'name',
|
||||
label: <FormattedMessage id="label.name" defaultMessage="Name" />,
|
||||
className: 'col-6 col-xl-4',
|
||||
render: DetailsLink,
|
||||
},
|
||||
{
|
||||
key: 'domain',
|
||||
label: <FormattedMessage id="label.domain" defaultMessage="Domain" />,
|
||||
className: 'col-6 col-xl-4',
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
className: classNames(styles.buttons, 'col-12 col-xl-4 pt-2 pt-xl-0'),
|
||||
render: Buttons,
|
||||
},
|
||||
];
|
||||
|
||||
function handleSave() {
|
||||
setSaved(state => state + 1);
|
||||
setMessage(<FormattedMessage id="message.save-success" defaultMessage="Saved successfully." />);
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
setAddWebsite(null);
|
||||
setEditWebsite(null);
|
||||
setResetWebsite(null);
|
||||
setDeleteWebsite(null);
|
||||
setShowCode(null);
|
||||
setShowUrl(null);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const empty = (
|
||||
<EmptyPlaceholder
|
||||
msg={
|
||||
<FormattedMessage
|
||||
id="message.no-websites-configured"
|
||||
defaultMessage="You don't have any websites configured."
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button icon={<Plus />} size="medium" onClick={() => setAddWebsite(true)}>
|
||||
<FormattedMessage id="label.add-website" defaultMessage="Add website" />
|
||||
</Button>
|
||||
</EmptyPlaceholder>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
<div>
|
||||
<FormattedMessage id="label.websites" defaultMessage="Websites" />
|
||||
</div>
|
||||
<Button icon={<Plus />} size="small" onClick={() => setAddWebsite(true)}>
|
||||
<FormattedMessage id="label.add-website" defaultMessage="Add website" />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Table columns={columns} rows={data} empty={empty} />
|
||||
{editWebsite && (
|
||||
<Modal title={<FormattedMessage id="label.edit-website" defaultMessage="Edit website" />}>
|
||||
<WebsiteEditForm values={editWebsite} onSave={handleSave} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{addWebsite && (
|
||||
<Modal title={<FormattedMessage id="label.add-website" defaultMessage="Add website" />}>
|
||||
<WebsiteEditForm onSave={handleSave} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{resetWebsite && (
|
||||
<Modal
|
||||
title={<FormattedMessage id="label.reset-website" defaultMessage="Reset statistics" />}
|
||||
>
|
||||
<ResetForm
|
||||
values={{ type: 'website', id: resetWebsite.website_id, name: resetWebsite.name }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{deleteWebsite && (
|
||||
<Modal
|
||||
title={<FormattedMessage id="label.delete-website" defaultMessage="Delete website" />}
|
||||
>
|
||||
<DeleteForm
|
||||
values={{ type: 'website', id: deleteWebsite.website_id, name: deleteWebsite.name }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{showCode && (
|
||||
<Modal title={<FormattedMessage id="label.tracking-code" defaultMessage="Tracking code" />}>
|
||||
<TrackingCodeForm values={showCode} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{showUrl && (
|
||||
<Modal title={<FormattedMessage id="label.share-url" defaultMessage="Share URL" />}>
|
||||
<ShareUrlForm values={showUrl} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{message && <Toast message={message} onClose={() => setMessage(null)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
components/settings/WebsiteSettings.module.css
Normal file
7
components/settings/WebsiteSettings.module.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.col {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue