Merge pull request #1955 from umami-software/dev

v2.1.0
This commit is contained in:
Mike Cao 2023-04-21 18:20:48 -07:00 committed by GitHub
commit 36b767af75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
246 changed files with 6517 additions and 4209 deletions

View File

@ -4,3 +4,4 @@ Dockerfile
.gitignore
.DS_Store
node_modules
.idea

View File

@ -46,6 +46,7 @@
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"import/no-anonymous-default-export": "off",
"import/no-named-as-default": "off",
"@next/next/no-img-element": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",

View File

@ -18,7 +18,7 @@ jobs:
db-type: [postgresql, mysql]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & push Docker image for ${{ matrix.db-type }}

View File

@ -3,7 +3,6 @@ name: Create docker images
on: [create]
jobs:
build:
name: Build, push, and deploy
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
@ -14,7 +13,7 @@ jobs:
db-type: [postgresql, mysql]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ node_modules
/build
/public/script.js
/geo
/dist
# misc
.DS_Store

View File

@ -1 +1 @@
/public/
/public/script.js

View File

@ -1,7 +1,7 @@
import { Icon, Text, Flexbox } from 'react-basics';
import Logo from 'assets/logo.svg';
function EmptyPlaceholder({ message, children }) {
export function EmptyPlaceholder({ message, children }) {
return (
<Flexbox direction="column" alignItems="center" justifyContent="center" gap={60} height={600}>
<Icon size="xl">

View File

@ -2,7 +2,7 @@ import { Icon, Icons, Text } from 'react-basics';
import styles from './ErrorMessage.module.css';
import useMessages from 'hooks/useMessages';
export default function ErrorMessage() {
export function ErrorMessage() {
const { formatMessage, messages } = useMessages();
return (
@ -14,3 +14,5 @@ export default function ErrorMessage() {
</div>
);
}
export default ErrorMessage;

View File

@ -5,7 +5,7 @@ function getHostName(url) {
return match && match.length > 1 ? match[1] : null;
}
function Favicon({ domain, ...props }) {
export function Favicon({ domain, ...props }) {
const hostName = domain ? getHostName(domain) : null;
return hostName ? (

View File

@ -1,3 +1,3 @@
.favicon {
margin-right: 8px;
margin-inline-end: 8px;
}

View File

@ -1,6 +1,6 @@
import { ButtonGroup, Button, Flexbox } from 'react-basics';
export default function FilterButtons({ items, selectedKey, onSelect }) {
export function FilterButtons({ items, selectedKey, onSelect }) {
return (
<Flexbox justifyContent="center">
<ButtonGroup items={items} selectedKey={selectedKey} onSelect={onSelect}>
@ -9,3 +9,5 @@ export default function FilterButtons({ items, selectedKey, onSelect }) {
</Flexbox>
);
}
export default FilterButtons;

View File

@ -6,7 +6,7 @@ import usePageQuery from 'hooks/usePageQuery';
import useMessages from 'hooks/useMessages';
import styles from './FilterLink.module.css';
export default function FilterLink({ id, value, label, externalUrl }) {
export function FilterLink({ id, value, label, externalUrl }) {
const { formatMessage, labels } = useMessages();
const { resolveUrl, query } = usePageQuery();
const active = query[id] !== undefined;
@ -37,3 +37,5 @@ export default function FilterLink({ id, value, label, externalUrl }) {
</div>
);
}
export default FilterLink;

View File

@ -14,7 +14,7 @@
.row .link {
display: none;
margin-left: 20px;
margin-inline-start: 20px;
}
.row .label {

View File

@ -5,7 +5,7 @@ import Icons from 'components/icons';
import useMessages from 'hooks/useMessages';
import useConfig from 'hooks/useConfig';
export default function HamburgerButton() {
export function HamburgerButton() {
const { formatMessage, labels } = useMessages();
const [active, setActive] = useState(false);
const { cloudMode } = useConfig();
@ -57,3 +57,5 @@ export default function HamburgerButton() {
</>
);
}
export default HamburgerButton;

View File

@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { Tooltip } from 'react-basics';
import styles from './HoverTooltip.module.css';
export default function HoverTooltip({ tooltip }) {
export function HoverTooltip({ tooltip }) {
const [position, setPosition] = useState({ x: -1000, y: -1000 });
useEffect(() => {
@ -23,3 +23,5 @@ export default function HoverTooltip({ tooltip }) {
</div>
);
}
export default HoverTooltip;

View File

@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import Link from 'next/link';
import styles from './MobileMenu.module.css';
export default function MobileMenu({ items = [], onClose }) {
export function MobileMenu({ items = [], onClose }) {
const { pathname } = useRouter();
const Items = ({ items, className }) => (
@ -34,3 +34,5 @@ export default function MobileMenu({ items = [], onClose }) {
</div>
);
}
export default MobileMenu;

View File

@ -2,7 +2,7 @@ import classNames from 'classnames';
import styles from './NoData.module.css';
import useMessages from 'hooks/useMessages';
function NoData({ className }) {
export function NoData({ className }) {
const { formatMessage, messages } = useMessages();
return (

View File

@ -1,7 +1,7 @@
import { Table, TableHeader, TableBody, TableRow, TableCell, TableColumn } from 'react-basics';
import styles from './SettingsTable.module.css';
export default function SettingsTable({ columns = [], data = [], children, cellRender }) {
export function SettingsTable({ columns = [], data = [], children, cellRender }) {
return (
<Table columns={columns} rows={data}>
<TableHeader className={styles.header}>
@ -34,3 +34,5 @@ export default function SettingsTable({ columns = [], data = [], children, cellR
</Table>
);
}
export default SettingsTable;

View File

@ -6,7 +6,7 @@ import { REPO_URL, VERSION_CHECK } from 'lib/constants';
import styles from './UpdateNotice.module.css';
import useMessages from 'hooks/useMessages';
export default function UpdateNotice() {
export function UpdateNotice() {
const { formatMessage, labels, messages } = useMessages();
const { latest, checked, hasUpdate, releaseUrl } = useStore();
const [dismissed, setDismissed] = useState(false);
@ -50,3 +50,5 @@ export default function UpdateNotice() {
</Row>
);
}
export default UpdateNotice;

View File

@ -3,16 +3,16 @@ import { useRouter } from 'next/router';
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
import classNames from 'classnames';
import { colord } from 'colord';
import useTheme from 'hooks/useTheme';
import HoverTooltip from 'components/common/HoverTooltip';
import { ISO_COUNTRIES, THEME_COLORS, MAP_FILE } from 'lib/constants';
import styles from './WorldMap.module.css';
import useTheme from 'hooks/useTheme';
import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale';
import HoverTooltip from './HoverTooltip';
import { formatLongNumber } from 'lib/format';
import { percentFilter } from 'lib/filters';
import styles from './WorldMap.module.css';
function WorldMap({ data, className }) {
export function WorldMap({ data, className }) {
const { basePath } = useRouter();
const [tooltip, setTooltip] = useState();
const [theme] = useTheme();

View File

@ -9,7 +9,7 @@ import useApi from 'hooks/useApi';
import useDateRange from 'hooks/useDateRange';
import useMessages from 'hooks/useMessages';
function DateFilter({ websiteId, value, className }) {
export function DateFilter({ websiteId, value, className }) {
const { formatMessage, labels } = useMessages();
const { get } = useApi();
const [dateRange, setDateRange] = useDateRange(websiteId);

View File

@ -5,8 +5,8 @@ import useLocale from 'hooks/useLocale';
import Icons from 'components/icons';
import styles from './LanguageButton.module.css';
export default function LanguageButton() {
const { locale, saveLocale } = useLocale();
export function LanguageButton() {
const { locale, saveLocale, dir } = useLocale();
const items = Object.keys(languages).map(key => ({ ...languages[key], value: key }));
function handleSelect(value) {
@ -20,7 +20,7 @@ export default function LanguageButton() {
<Icons.Globe />
</Icon>
</Button>
<Popup position="bottom" alignment="end">
<Popup position="bottom" alignment={dir === 'rtl' ? 'start' : 'end'}>
<div className={styles.menu}>
{items.map(({ value, label }) => {
return (
@ -43,3 +43,5 @@ export default function LanguageButton() {
</PopupTrigger>
);
}
export default LanguageButton;

View File

@ -2,7 +2,7 @@ import { Button, Icon, Icons, Tooltip } from 'react-basics';
import Link from 'next/link';
import useMessages from 'hooks/useMessages';
export default function LogoutButton({ tooltipPosition = 'top' }) {
export function LogoutButton({ tooltipPosition = 'top' }) {
const { formatMessage, labels } = useMessages();
return (
<Link href="/logout">
@ -16,3 +16,5 @@ export default function LogoutButton({ tooltipPosition = 'top' }) {
</Link>
);
}
export default LogoutButton;

View File

@ -5,12 +5,14 @@ import useMessages from 'hooks/useMessages';
import useUser from 'hooks/useUser';
import useConfig from 'hooks/useConfig';
import styles from './ProfileButton.module.css';
import useLocale from 'hooks/useLocale';
export default function ProfileButton() {
export function ProfileButton() {
const { formatMessage, labels } = useMessages();
const { user } = useUser();
const { cloudMode } = useConfig();
const router = useRouter();
const { dir } = useLocale();
const handleSelect = key => {
if (key === 'profile') {
@ -31,7 +33,7 @@ export default function ProfileButton() {
<Icons.ChevronDown />
</Icon>
</Button>
<Popup position="bottom" alignment="end">
<Popup position="bottom" alignment={dir === 'rtl' ? 'start' : 'end'}>
<Menu variant="popup" onSelect={handleSelect} className={styles.menu}>
<Item key="user" className={styles.item}>
<Text>{user.username}</Text>
@ -55,3 +57,5 @@ export default function ProfileButton() {
</PopupTrigger>
);
}
export default ProfileButton;

View File

@ -4,7 +4,7 @@ import useDateRange from 'hooks/useDateRange';
import Icons from 'components/icons';
import useMessages from 'hooks/useMessages';
function RefreshButton({ websiteId, isLoading }) {
export function RefreshButton({ websiteId, isLoading }) {
const { formatMessage, labels } = useMessages();
const [dateRange] = useDateRange(websiteId);

View File

@ -5,7 +5,7 @@ import Icons from 'components/icons';
import useMessages from 'hooks/useMessages';
import styles from './SettingsButton.module.css';
export default function SettingsButton() {
export function SettingsButton() {
const { formatMessage, labels } = useMessages();
return (
@ -33,3 +33,5 @@ export default function SettingsButton() {
</PopupTrigger>
);
}
export default SettingsButton;

View File

@ -4,7 +4,7 @@ import useTheme from 'hooks/useTheme';
import Icons from 'components/icons';
import styles from './ThemeButton.module.css';
export default function ThemeButton() {
export function ThemeButton() {
const [theme, setTheme] = useTheme();
const transitions = useTransition(theme, {
@ -34,3 +34,5 @@ export default function ThemeButton() {
</Button>
);
}
export default ThemeButton;

View File

@ -2,7 +2,7 @@ import { Dropdown, Item } from 'react-basics';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function WebsiteSelect({ websiteId, onSelect }) {
export function WebsiteSelect({ websiteId, onSelect }) {
const { formatMessage, labels } = useMessages();
const { get, useQuery } = useApi();
const { data } = useQuery(['websites:me'], () => get('/me/websites'));
@ -25,3 +25,5 @@ export default function WebsiteSelect({ websiteId, onSelect }) {
</Dropdown>
);
}
export default WebsiteSelect;

View File

@ -5,9 +5,10 @@ import NavBar from 'components/layout/NavBar';
import UpdateNotice from 'components/common/UpdateNotice';
import useRequireLogin from 'hooks/useRequireLogin';
import useConfig from 'hooks/useConfig';
import { CURRENT_VERSION } from 'lib/constants';
import styles from './AppLayout.module.css';
export default function AppLayout({ title, children }) {
export function AppLayout({ title, children }) {
const { user } = useRequireLogin();
const config = useConfig();
const { pathname } = useRouter();
@ -19,7 +20,7 @@ export default function AppLayout({ title, children }) {
const allowUpdate = user?.isAdmin && !config?.updatesDisabled && !pathname.includes('/share/');
return (
<div className={styles.layout}>
<div className={styles.layout} data-app-version={CURRENT_VERSION}>
{allowUpdate && <UpdateNotice />}
<Head>
<title>{title ? `${title} | umami` : 'umami'}</title>
@ -33,3 +34,5 @@ export default function AppLayout({ title, children }) {
</div>
);
}
export default AppLayout;

View File

@ -4,7 +4,7 @@ import { CURRENT_VERSION, HOMEPAGE_URL, REPO_URL } from 'lib/constants';
import { labels } from 'components/messages';
import styles from './Footer.module.css';
export default function Footer() {
export function Footer() {
return (
<footer className={styles.footer}>
<Row>
@ -29,3 +29,5 @@ export default function Footer() {
</footer>
);
}
export default Footer;

View File

@ -10,16 +10,16 @@
}
.row > .col {
border-left: 1px solid var(--base300);
border-inline-start: 1px solid var(--base300);
}
.row > .col:first-child {
border-left: 0;
padding-left: 0;
border-inline-start: 0;
padding-inline-start: 0;
}
.row > .col:last-child {
padding-right: 0;
padding-inline-end: 0;
}
@media only screen and (max-width: 992px) {
@ -29,7 +29,7 @@
.row > .col {
border-top: 1px solid var(--base300);
border-left: 0;
border-inline-end: 0;
padding: 20px 0;
}
}

View File

@ -6,7 +6,7 @@ import SettingsButton from 'components/input/SettingsButton';
import Icons from 'components/icons';
import styles from './Header.module.css';
export default function Header() {
export function Header() {
return (
<header className={styles.header}>
<Row>
@ -27,3 +27,5 @@ export default function Header() {
</header>
);
}
export default Header;

View File

@ -1,4 +1,3 @@
import { useState } from 'react';
import { Icon, Text, Row, Column } from 'react-basics';
import Link from 'next/link';
import classNames from 'classnames';
@ -12,7 +11,7 @@ import useMessages from 'hooks/useMessages';
import { useRouter } from 'next/router';
import HamburgerButton from '../common/HamburgerButton';
export default function NavBar() {
export function NavBar() {
const { pathname } = useRouter();
const { cloudMode } = useConfig();
const { formatMessage, labels } = useMessages();
@ -61,3 +60,5 @@ export default function NavBar() {
</div>
);
}
export default NavBar;

View File

@ -6,7 +6,7 @@ import Link from 'next/link';
import Icons from 'components/icons';
import styles from './NavGroup.module.css';
export default function NavGroup({
export function NavGroup({
title,
items,
defaultExpanded = true,
@ -54,3 +54,5 @@ export default function NavGroup({
</div>
);
}
export default NavGroup;

View File

@ -3,7 +3,7 @@ import { Banner, Loading } from 'react-basics';
import useMessages from 'hooks/useMessages';
import styles from './Page.module.css';
export default function Page({ className, error, loading, children }) {
export function Page({ className, error, loading, children }) {
const { formatMessage, messages } = useMessages();
if (error) {
@ -16,3 +16,5 @@ export default function Page({ className, error, loading, children }) {
return <div className={classNames(styles.page, className)}>{children}</div>;
}
export default Page;

View File

@ -1,7 +1,7 @@
import React from 'react';
import styles from './PageHeader.module.css';
export default function PageHeader({ title, children }) {
export function PageHeader({ title, children }) {
return (
<div className={styles.header}>
<div className={styles.title}>{title}</div>
@ -9,3 +9,5 @@ export default function PageHeader({ title, children }) {
</div>
);
}
export default PageHeader;

View File

@ -1,5 +1,4 @@
import { Row, Column } from 'react-basics';
import classNames from 'classnames';
import { useRouter } from 'next/router';
import SideNav from './SideNav';
import useUser from 'hooks/useUser';
@ -7,7 +6,7 @@ import useMessages from 'hooks/useMessages';
import useConfig from 'hooks/useConfig';
import styles from './SettingsLayout.module.css';
export default function SettingsLayout({ children }) {
export function SettingsLayout({ children }) {
const { user } = useUser();
const { pathname } = useRouter();
const { formatMessage, labels } = useMessages();
@ -35,3 +34,5 @@ export default function SettingsLayout({ children }) {
</Row>
);
}
export default SettingsLayout;

View File

@ -2,7 +2,7 @@ import { Container } from 'react-basics';
import Header from './Header';
import Footer from './Footer';
export default function ShareLayout({ children }) {
export function ShareLayout({ children }) {
return (
<Container>
<Header />
@ -11,3 +11,5 @@ export default function ShareLayout({ children }) {
</Container>
);
}
export default ShareLayout;

View File

@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
import Link from 'next/link';
import styles from './SideNav.module.css';
export default function SideNav({ selectedKey, items, shallow, onSelect = () => {} }) {
export function SideNav({ selectedKey, items, shallow, onSelect = () => {} }) {
const { asPath } = useRouter();
return (
<Menu items={items} selectedKey={selectedKey} className={styles.menu} onSelect={onSelect}>
@ -21,3 +21,5 @@ export default function SideNav({ selectedKey, items, shallow, onSelect = () =>
</Menu>
);
}
export default SideNav;

View File

@ -62,6 +62,7 @@ export const labels = defineMessages({
queries: { id: 'label.queries', defaultMessage: 'Queries' },
teams: { id: 'label.teams', defaultMessage: 'Teams' },
analytics: { id: 'label.analytics', defaultMessage: 'Analytics' },
login: { id: 'label.login', defaultMessage: 'Login' },
logout: { id: 'label.logout', defaultMessage: 'Logout' },
singleDay: { id: 'label.single-day', defaultMessage: 'Single day' },
dateRange: { id: 'label.date-range', defaultMessage: 'Date range' },
@ -94,12 +95,12 @@ export const labels = defineMessages({
thisMonth: { id: 'label.this-month', defaultMessage: 'This month' },
thisYear: { id: 'label.this-year', defaultMessage: 'This year' },
allTime: { id: 'label.all-time', defaultMessage: 'All time' },
customRange: { id: 'label.custom-range', defaultMessage: 'Custom-range' },
customRange: { id: 'label.custom-range', defaultMessage: 'Custom range' },
selectWebsite: { id: 'label.select-website', defaultMessage: 'Select website' },
all: { id: 'label.all', defaultMessage: 'All' },
sessions: { id: 'label.sessions', defaultMessage: 'Sessions' },
pageNotFound: { id: 'message.page-not-found', defaultMessage: 'Page not found' },
logs: { id: 'label.activity-log', defaultMessage: 'Activity log' },
activityLog: { id: 'label.activity-log', defaultMessage: 'Activity log' },
dismiss: { id: 'label.dismiss', defaultMessage: 'Dismiss' },
poweredBy: { id: 'label.powered-by', defaultMessage: 'Powered by {name}' },
pageViews: { id: 'label.page-views', defaultMessage: 'Page views' },

View File

@ -4,7 +4,7 @@ import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import styles from './ActiveUsers.module.css';
export default function ActiveUsers({ websiteId, value, refetchInterval = 60000 }) {
export function ActiveUsers({ websiteId, value, refetchInterval = 60000 }) {
const { formatMessage, messages } = useMessages();
const { get, useQuery } = useApi();
const { data } = useQuery(
@ -34,3 +34,5 @@ export default function ActiveUsers({ websiteId, value, refetchInterval = 60000
</StatusLight>
);
}
export default ActiveUsers;

View File

@ -11,7 +11,7 @@ import useTheme from 'hooks/useTheme';
import { DEFAULT_ANIMATION_DURATION, THEME_COLORS } from 'lib/constants';
import styles from './BarChart.module.css';
export default function BarChart({
export function BarChart({
datasets,
unit,
animationDuration = DEFAULT_ANIMATION_DURATION,
@ -45,7 +45,7 @@ export default function BarChart({
switch (unit) {
case 'minute':
return dateFormat(d, 'H:mm', locale);
return dateFormat(d, 'h:mm', locale);
case 'hour':
return dateFormat(d, 'p', locale);
case 'day':
@ -177,15 +177,16 @@ export default function BarChart({
const updateChart = () => {
setTooltip(null);
datasets.forEach((dataset, index) => {
chart.current.data.datasets[index].data = dataset.data;
chart.current.data.datasets[index].label = dataset.label;
});
chart.current.options = getOptions();
if (datasets.length) {
chart.current.data.datasets = datasets;
}
onUpdate(chart.current);
chart.current.update();
onUpdate(chart.current);
};
useEffect(() => {
@ -209,3 +210,5 @@ export default function BarChart({
</>
);
}
export default BarChart;

View File

@ -3,7 +3,7 @@ import MetricsTable from 'components/metrics/MetricsTable';
import { BROWSERS } from 'lib/constants';
import useMessages from 'hooks/useMessages';
export default function BrowsersTable({ websiteId, ...props }) {
export function BrowsersTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
function renderLink({ x: browser }) {
@ -21,3 +21,5 @@ export default function BrowsersTable({ websiteId, ...props }) {
/>
);
}
export default BrowsersTable;

View File

@ -4,7 +4,7 @@ import FilterLink from 'components/common/FilterLink';
import useLocale from 'hooks/useLocale';
import useMessages from 'hooks/useMessages';
export default function CitiesTable({ websiteId, ...props }) {
export function CitiesTable({ websiteId, ...props }) {
const { locale } = useLocale();
const { formatMessage, labels } = useMessages();
@ -28,3 +28,5 @@ export default function CitiesTable({ websiteId, ...props }) {
/>
);
}
export default CitiesTable;

View File

@ -4,7 +4,7 @@ import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale';
import useMessages from 'hooks/useMessages';
export default function CountriesTable({ websiteId, ...props }) {
export function CountriesTable({ websiteId, ...props }) {
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const { formatMessage, labels } = useMessages();
@ -28,3 +28,5 @@ export default function CountriesTable({ websiteId, ...props }) {
/>
);
}
export default CountriesTable;

View File

@ -3,13 +3,12 @@ import useMeasure from 'react-use-measure';
import { FixedSizeList } from 'react-window';
import { useSpring, animated, config } from 'react-spring';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import NoData from 'components/common/NoData';
import { formatNumber, formatLongNumber } from 'lib/format';
import styles from './DataTable.module.css';
import useMessages from '../../hooks/useMessages';
export default function DataTable({
export function DataTable({
data = [],
title,
metric,
@ -102,3 +101,5 @@ const AnimatedRow = ({
</div>
);
};
export default DataTable;

View File

@ -68,8 +68,8 @@
.value {
width: 50px;
text-align: right;
margin-right: 10px;
text-align: end;
margin-inline-end: 10px;
font-weight: 600;
cursor: pointer;
}
@ -79,7 +79,7 @@
width: 50px;
color: var(--base600);
border-left: 1px solid var(--base600);
padding-left: 10px;
padding-inline-start: 10px;
z-index: 1;
}

View File

@ -8,7 +8,7 @@ import { FILTER_DAY, FILTER_RANGE } from 'lib/constants';
import useMessages from 'hooks/useMessages';
import styles from './DatePickerForm.module.css';
export default function DatePickerForm({
export function DatePickerForm({
startDate: defaultStartDate,
endDate: defaultEndDate,
minDate,
@ -78,3 +78,5 @@ export default function DatePickerForm({
</div>
);
}
export default DatePickerForm;

View File

@ -2,7 +2,7 @@ import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
import useMessages from 'hooks/useMessages';
export default function DevicesTable({ websiteId, ...props }) {
export function DevicesTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
function renderLink({ x: device }) {
@ -26,3 +26,5 @@ export default function DevicesTable({ websiteId, ...props }) {
/>
);
}
export default DevicesTable;

View File

@ -9,7 +9,7 @@ import useTimezone from 'hooks/useTimezone';
import usePageQuery from 'hooks/usePageQuery';
import { EVENT_COLORS } from 'lib/constants';
export default function EventsChart({ websiteId, className, token }) {
export function EventsChart({ websiteId, className, token }) {
const { get, useQuery } = useApi();
const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId);
const [timezone] = useTimezone();
@ -76,3 +76,5 @@ export default function EventsChart({ websiteId, className, token }) {
/>
);
}
export default EventsChart;

View File

@ -1,7 +1,7 @@
import MetricsTable from './MetricsTable';
import useMessages from 'hooks/useMessages';
export default function EventsTable({ websiteId, ...props }) {
export function EventsTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
function handleDataLoad(data) {
@ -19,3 +19,5 @@ export default function EventsTable({ websiteId, ...props }) {
/>
);
}
export default EventsTable;

View File

@ -4,7 +4,7 @@ import usePageQuery from 'hooks/usePageQuery';
import styles from './FilterTags.module.css';
import useMessages from 'hooks/useMessages';
export default function FilterTags({ params }) {
export function FilterTags({ params }) {
const { formatMessage, labels } = useMessages();
const {
router,
@ -50,3 +50,5 @@ export default function FilterTags({ params }) {
</div>
);
}
export default FilterTags;

View File

@ -4,7 +4,7 @@ import useLanguageNames from 'hooks/useLanguageNames';
import useLocale from 'hooks/useLocale';
import useMessages from 'hooks/useMessages';
export default function LanguagesTable({ websiteId, onDataLoad, ...props }) {
export function LanguagesTable({ websiteId, onDataLoad, ...props }) {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const languageNames = useLanguageNames(locale);
@ -25,3 +25,5 @@ export default function LanguagesTable({ websiteId, onDataLoad, ...props }) {
/>
);
}
export default LanguagesTable;

View File

@ -6,7 +6,7 @@ import useLocale from 'hooks/useLocale';
import useForceUpdate from 'hooks/useForceUpdate';
import styles from './Legend.module.css';
export default function Legend({ chart }) {
export function Legend({ chart }) {
const { locale } = useLocale();
const forceUpdate = useForceUpdate();
@ -48,3 +48,5 @@ export default function Legend({ chart }) {
</div>
);
}
export default Legend;

View File

@ -13,7 +13,7 @@
}
.label + .label {
margin-left: 20px;
margin-inline-start: 20px;
}
.hidden {

View File

@ -3,7 +3,7 @@ import { useSpring, animated } from 'react-spring';
import { formatNumber } from 'lib/format';
import styles from './MetricCard.module.css';
const MetricCard = ({
export const MetricCard = ({
value = 0,
change = 0,
label,

View File

@ -9,7 +9,7 @@ import MetricCard from './MetricCard';
import useMessages from 'hooks/useMessages';
import styles from './MetricsBar.module.css';
export default function MetricsBar({ websiteId }) {
export function MetricsBar({ websiteId }) {
const { formatMessage, labels } = useMessages();
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
@ -111,3 +111,5 @@ export default function MetricsBar({ websiteId }) {
</div>
);
}
export default MetricsBar;

View File

@ -13,8 +13,9 @@ import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
import Icons from 'components/icons';
import useMessages from 'hooks/useMessages';
import styles from './MetricsTable.module.css';
import useLocale from 'hooks/useLocale';
export default function MetricsTable({
export function MetricsTable({
websiteId,
type,
className,
@ -69,6 +70,7 @@ export default function MetricsTable({
}
return [];
}, [data, error, dataFilter, filterOptions]);
const { dir } = useLocale();
return (
<div className={classNames(styles.container, className)}>
@ -80,7 +82,7 @@ export default function MetricsTable({
<Link href={router.pathname} as={resolveUrl({ view: type })}>
<Button variant="quiet">
<Text>{formatMessage(labels.more)}</Text>
<Icon size="sm">
<Icon size="sm" rotate={dir === 'rtl' ? 180 : 0}>
<Icons.ArrowRight />
</Icon>
</Button>
@ -90,3 +92,5 @@ export default function MetricsTable({
</div>
);
}
export default MetricsTable;

View File

@ -2,7 +2,7 @@ import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
import useMessages from 'hooks/useMessages';
export default function OSTable({ websiteId, ...props }) {
export function OSTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
function renderLink({ x: os }) {
@ -20,3 +20,5 @@ export default function OSTable({ websiteId, ...props }) {
/>
);
}
export default OSTable;

View File

@ -5,7 +5,7 @@ import useMessages from 'hooks/useMessages';
import usePageQuery from 'hooks/usePageQuery';
import { emptyFilter } from 'lib/filters';
export default function PagesTable({ websiteId, showFilters, ...props }) {
export function PagesTable({ websiteId, showFilters, ...props }) {
const {
router,
resolveUrl,
@ -47,3 +47,5 @@ export default function PagesTable({ websiteId, showFilters, ...props }) {
</>
);
}
export default PagesTable;

View File

@ -6,15 +6,7 @@ import useTheme from 'hooks/useTheme';
import useMessages from 'hooks/useMessages';
import useLocale from 'hooks/useLocale';
export default function PageviewsChart({
websiteId,
data,
unit,
records,
className,
loading,
...props
}) {
export function PageviewsChart({ websiteId, data, unit, records, className, loading, ...props }) {
const { formatMessage, labels } = useMessages();
const [theme] = useTheme();
const { locale } = useLocale();
@ -68,3 +60,5 @@ export default function PageviewsChart({
/>
);
}
export default PageviewsChart;

View File

@ -12,7 +12,7 @@ const filters = {
[FILTER_COMBINED]: paramFilter,
};
export default function QueryParametersTable({ websiteId, showFilters, ...props }) {
export function QueryParametersTable({ websiteId, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
const { formatMessage, labels } = useMessages();
@ -49,3 +49,5 @@ export default function QueryParametersTable({ websiteId, showFilters, ...props
</>
);
}
export default QueryParametersTable;

View File

@ -23,7 +23,7 @@ function mapData(data) {
return arr;
}
export default function RealtimeChart({ data, unit, ...props }) {
export function RealtimeChart({ data, unit, ...props }) {
const endDate = startOfMinute(new Date());
const startDate = subMinutes(endDate, REALTIME_RANGE);
const prevEndDate = useRef(endDate);
@ -58,3 +58,5 @@ export default function RealtimeChart({ data, unit, ...props }) {
/>
);
}
export default RealtimeChart;

View File

@ -2,7 +2,7 @@ import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
import useMessages from 'hooks/useMessages';
export default function ReferrersTable({ websiteId, ...props }) {
export function ReferrersTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
const renderLink = ({ x: referrer }) => {
@ -29,3 +29,5 @@ export default function ReferrersTable({ websiteId, ...props }) {
</>
);
}
export default ReferrersTable;

View File

@ -3,19 +3,25 @@ import { emptyFilter } from 'lib/filters';
import FilterLink from 'components/common/FilterLink';
import useLocale from 'hooks/useLocale';
import useMessages from 'hooks/useMessages';
import useCountryNames from 'hooks/useCountryNames';
import regions from 'public/iso-3166-2.json';
export default function RegionsTable({ websiteId, ...props }) {
export function RegionsTable({ websiteId, ...props }) {
const { locale } = useLocale();
const { formatMessage, labels } = useMessages();
const countryNames = useCountryNames(locale);
function renderLink({ x }) {
const renderLabel = x => {
return regions[x] ? `${regions[x]}, ${countryNames[x.split('-')[0]]}` : x;
};
const renderLink = ({ x }) => {
return (
<div className={locale}>
<FilterLink id="region" value={x} label={regions[x] || x} />
<FilterLink id="region" value={x} label={renderLabel(x)} />
</div>
);
}
};
return (
<MetricsTable
@ -29,3 +35,5 @@ export default function RegionsTable({ websiteId, ...props }) {
/>
);
}
export default RegionsTable;

View File

@ -1,7 +1,7 @@
import MetricsTable from './MetricsTable';
import useMessages from 'hooks/useMessages';
export default function ScreenTable({ websiteId, ...props }) {
export function ScreenTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
return (
@ -14,3 +14,5 @@ export default function ScreenTable({ websiteId, ...props }) {
/>
);
}
export default ScreenTable;

View File

@ -18,8 +18,9 @@ import Icons from 'components/icons';
import useSticky from 'hooks/useSticky';
import useMessages from 'hooks/useMessages';
import styles from './WebsiteChart.module.css';
import useLocale from 'hooks/useLocale';
export default function WebsiteChart({
export function WebsiteChart({
websiteId,
name,
domain,
@ -72,6 +73,7 @@ export default function WebsiteChart({
return { pageviews: [], sessions: [] };
}, [data, modified]);
const { dir } = useLocale();
return (
<>
<WebsiteHeader websiteId={websiteId} name={name} domain={domain}>
@ -80,7 +82,9 @@ export default function WebsiteChart({
<Button variant="primary">
<Text>{formatMessage(labels.viewDetails)}</Text>
<Icon>
<Icons.ArrowRight />
<Icon rotate={dir === 'rtl' ? 180 : 0}>
<Icons.ArrowRight />
</Icon>
</Icon>
</Button>
</Link>
@ -124,3 +128,5 @@ export default function WebsiteChart({
</>
);
}
export default WebsiteChart;

View File

@ -3,7 +3,7 @@ import Favicon from 'components/common/Favicon';
import ActiveUsers from './ActiveUsers';
import styles from './WebsiteHeader.module.css';
export default function WebsiteHeader({ websiteId, name, domain, children }) {
export function WebsiteHeader({ websiteId, name, domain, children }) {
return (
<Row className={styles.header} justifyContent="center">
<Column className={styles.title} variant="two">
@ -17,3 +17,5 @@ export default function WebsiteHeader({ websiteId, name, domain, children }) {
</Row>
);
}
export default WebsiteHeader;

View File

@ -11,7 +11,7 @@ import Script from 'next/script';
import { Button, Column, Row } from 'react-basics';
import styles from './TestConsole.module.css';
export default function TestConsole() {
export function TestConsole() {
const { get, useQuery } = useApi();
const { data, isLoading, error } = useQuery(['websites:me'], () => get('/me/websites'));
const router = useRouter();
@ -132,3 +132,5 @@ export default function TestConsole() {
</Page>
);
}
export default TestConsole;

View File

@ -10,8 +10,9 @@ import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import useApi from 'hooks/useApi';
import useDashboard from 'store/dashboard';
import useMessages from 'hooks/useMessages';
import useLocale from 'hooks/useLocale';
export default function Dashboard({ userId }) {
export function Dashboard({ userId }) {
const { formatMessage, labels, messages } = useMessages();
const dashboard = useDashboard();
const { showCharts, limit, editing } = dashboard;
@ -19,6 +20,7 @@ export default function Dashboard({ userId }) {
const { get, useQuery } = useApi();
const { data, isLoading, error } = useQuery(['websites'], () => get('/websites', { userId }));
const hasData = data && data.length !== 0;
const { dir } = useLocale();
function handleMore() {
setMax(max + limit);
@ -33,7 +35,7 @@ export default function Dashboard({ userId }) {
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)}>
<Link href="/settings/websites">
<Button>
<Icon>
<Icon rotate={dir === 'rtl' ? 180 : 0}>
<Icons.ArrowRight />
</Icon>
<Text>{formatMessage(messages.goToSettings)}</Text>
@ -48,7 +50,7 @@ export default function Dashboard({ userId }) {
{max < data.length && (
<Flexbox justifyContent="center">
<Button onClick={handleMore}>
<Icon>
<Icon rotate={dir === 'rtl' ? 180 : 0}>
<Icons.More />
</Icon>
<Text>{formatMessage(labels.more)}</Text>
@ -60,3 +62,5 @@ export default function Dashboard({ userId }) {
</Page>
);
}
export default Dashboard;

View File

@ -9,7 +9,7 @@ import styles from './DashboardEdit.module.css';
const dragId = 'dashboard-website-ordering';
export default function DashboardEdit({ websites }) {
export function DashboardEdit({ websites }) {
const settings = useDashboard();
const { websiteOrder } = settings;
const { formatMessage, labels } = useMessages();
@ -98,3 +98,5 @@ export default function DashboardEdit({ websites }) {
</>
);
}
export default DashboardEdit;

View File

@ -3,7 +3,7 @@ import Icons from 'components/icons';
import { saveDashboard } from 'store/dashboard';
import useMessages from 'hooks/useMessages';
export default function DashboardSettingsButton() {
export function DashboardSettingsButton() {
const { formatMessage, labels } = useMessages();
const menuOptions = [
@ -42,3 +42,5 @@ export default function DashboardSettingsButton() {
</PopupTrigger>
);
}
export default DashboardSettingsButton;

View File

@ -17,7 +17,7 @@ import useMessages from 'hooks/useMessages';
import Logo from 'assets/logo.svg';
import styles from './LoginForm.module.css';
export default function LoginForm() {
export function LoginForm() {
const { formatMessage, labels, getMessage } = useMessages();
const router = useRouter();
const { post } = useApi();
@ -53,10 +53,12 @@ export default function LoginForm() {
</FormRow>
<FormButtons>
<SubmitButton className={styles.button} variant="primary" disabled={isLoading}>
Log in
{formatMessage(labels.login)}
</SubmitButton>
</FormButtons>
</Form>
</div>
);
}
export default LoginForm;

View File

@ -2,7 +2,7 @@ import Head from 'next/head';
import useLocale from 'hooks/useLocale';
import styles from './LoginLayout.module.css';
export default function LoginLayout({ children }) {
export function LoginLayout({ children }) {
const { dir } = useLocale();
return (
@ -14,3 +14,5 @@ export default function LoginLayout({ children }) {
</div>
);
}
export default LoginLayout;

View File

@ -4,7 +4,7 @@ import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames';
import useMessages from 'hooks/useMessages';
export default function RealtimeCountries({ data }) {
export function RealtimeCountries({ data }) {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
@ -23,3 +23,5 @@ export default function RealtimeCountries({ data }) {
/>
);
}
export default RealtimeCountries;

View File

@ -25,7 +25,7 @@ function mergeData(state = [], data = [], time) {
.filter(({ timestamp }) => timestamp >= time);
}
export default function RealtimeDashboard({ websiteId }) {
export function RealtimeDashboard({ websiteId }) {
const { formatMessage, labels } = useMessages();
const router = useRouter();
const [currentData, setCurrentData] = useState();
@ -63,7 +63,7 @@ export default function RealtimeDashboard({ websiteId }) {
currentData.countries = percentFilter(
currentData.sessions
.reduce((arr, data) => {
if (!arr.find(({ sessionId }) => sessionId === data.sessionId)) {
if (!arr.find(({ id }) => id === data.id)) {
return arr.concat(data);
}
return arr;
@ -84,7 +84,7 @@ export default function RealtimeDashboard({ websiteId }) {
);
currentData.visitors = currentData.sessions.reduce((arr, val) => {
if (!arr.find(({ sessionId }) => sessionId === val.sessionId)) {
if (!arr.find(({ id }) => id === val.id)) {
return arr.concat(val);
}
return arr;
@ -125,3 +125,5 @@ export default function RealtimeDashboard({ websiteId }) {
</Page>
);
}
export default RealtimeDashboard;

View File

@ -2,7 +2,7 @@ import MetricCard from 'components/metrics/MetricCard';
import useMessages from 'hooks/useMessages';
import styles from './RealtimeHeader.module.css';
export default function RealtimeHeader({ data = {} }) {
export function RealtimeHeader({ data = {} }) {
const { formatMessage, labels } = useMessages();
const { pageviews, visitors, events, countries } = data;
@ -37,3 +37,5 @@ export default function RealtimeHeader({ data = {} }) {
</div>
);
}
export default RealtimeHeader;

View File

@ -6,7 +6,7 @@ import useApi from 'hooks/useApi';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import useMessages from 'hooks/useMessages';
export default function RealtimeHome() {
export function RealtimeHome() {
const { formatMessage, labels, messages } = useMessages();
const { get, useQuery } = useApi();
const router = useRouter();
@ -27,3 +27,5 @@ export default function RealtimeHome() {
</Page>
);
}
export default RealtimeHome;

View File

@ -25,7 +25,7 @@ const icons = {
[TYPE_EVENT]: <Icons.Bolt />,
};
export default function RealtimeLog({ data, websiteDomain }) {
export function RealtimeLog({ data, websiteDomain }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
@ -142,7 +142,7 @@ export default function RealtimeLog({ data, websiteDomain }) {
return (
<div className={styles.table}>
<FilterButtons items={buttons} selectedKey={filter} onSelect={setFilter} />
<div className={styles.header}>{formatMessage(labels.logs)}</div>
<div className={styles.header}>{formatMessage(labels.activityLog)}</div>
<div className={styles.body}>
{logs?.length === 0 && <NoData />}
{logs?.length > 0 && (
@ -154,3 +154,5 @@ export default function RealtimeLog({ data, websiteDomain }) {
</div>
);
}
export default RealtimeLog;

View File

@ -9,7 +9,7 @@
align-items: center;
justify-content: space-between;
font-size: var(--font-size-md);
line-height: 50px;
line-height: 40px;
font-weight: 700;
}
@ -42,6 +42,7 @@
.detail {
display: flex;
align-items: center;
flex: 1;
gap: 10px;
white-space: nowrap;

View File

@ -6,7 +6,7 @@ import DataTable from 'components/metrics/DataTable';
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
import useMessages from 'hooks/useMessages';
export default function RealtimeUrls({ websiteDomain, data = {} }) {
export function RealtimeUrls({ websiteDomain, data = {} }) {
const { formatMessage, labels } = useMessages();
const { pageviews } = data;
const [filter, setFilter] = useState(FILTER_REFERRERS);
@ -97,3 +97,5 @@ export default function RealtimeUrls({ websiteDomain, data = {} }) {
</>
);
}
export default RealtimeUrls;

View File

@ -4,7 +4,7 @@ import useDateRange from 'hooks/useDateRange';
import { DEFAULT_DATE_RANGE } from 'lib/constants';
import useMessages from 'hooks/useMessages';
export default function DateRangeSetting() {
export function DateRangeSetting() {
const { formatMessage, labels } = useMessages();
const [dateRange, setDateRange] = useDateRange();
const { startDate, endDate, value } = dateRange;
@ -18,3 +18,5 @@ export default function DateRangeSetting() {
</Flexbox>
);
}
export default DateRangeSetting;

View File

@ -4,7 +4,7 @@ import { DEFAULT_LOCALE } from 'lib/constants';
import { languages } from 'lib/lang';
import useMessages from 'hooks/useMessages';
export default function LanguageSetting() {
export function LanguageSetting() {
const { formatMessage, labels } = useMessages();
const { locale, saveLocale } = useLocale();
const options = Object.keys(languages);
@ -28,3 +28,5 @@ export default function LanguageSetting() {
</Flexbox>
);
}
export default LanguageSetting;

View File

@ -3,7 +3,7 @@ import PasswordEditForm from 'components/pages/settings/profile/PasswordEditForm
import Icons from 'components/icons';
import useMessages from 'hooks/useMessages';
export default function PasswordChangeButton() {
export function PasswordChangeButton() {
const { formatMessage, labels, messages } = useMessages();
const { toast, showToast } = useToast();
@ -28,3 +28,5 @@ export default function PasswordChangeButton() {
</>
);
}
export default PasswordChangeButton;

View File

@ -3,7 +3,7 @@ import { Form, FormRow, FormInput, FormButtons, PasswordField, Button } from 're
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function PasswordEditForm({ onSave, onClose }) {
export function PasswordEditForm({ onSave, onClose }) {
const { formatMessage, labels, messages } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error, isLoading } = useMutation(data => post('/me/password', data));
@ -64,3 +64,5 @@ export default function PasswordEditForm({ onSave, onClose }) {
</Form>
);
}
export default PasswordEditForm;

View File

@ -8,7 +8,7 @@ import useUser from 'hooks/useUser';
import useMessages from 'hooks/useMessages';
import useConfig from 'hooks/useConfig';
export default function ProfileDetails() {
export function ProfileDetails() {
const { user } = useUser();
const { formatMessage, labels } = useMessages();
const { cloudMode } = useConfig();
@ -45,3 +45,5 @@ export default function ProfileDetails() {
</Form>
);
}
export default ProfileDetails;

View File

@ -3,7 +3,7 @@ import PageHeader from 'components/layout/PageHeader';
import ProfileDetails from './ProfileDetails';
import useMessages from 'hooks/useMessages';
export default function ProfileSettings() {
export function ProfileSettings() {
const { formatMessage, labels } = useMessages();
return (
@ -13,3 +13,5 @@ export default function ProfileSettings() {
</Page>
);
}
export default ProfileSettings;

View File

@ -5,7 +5,7 @@ import Sun from 'assets/sun.svg';
import Moon from 'assets/moon.svg';
import styles from './ThemeSetting.module.css';
export default function ThemeSetting() {
export function ThemeSetting() {
const [theme, setTheme] = useTheme();
return (
@ -29,3 +29,5 @@ export default function ThemeSetting() {
</div>
);
}
export default ThemeSetting;

View File

@ -4,7 +4,7 @@ import useTimezone from 'hooks/useTimezone';
import useMessages from 'hooks/useMessages';
import { getTimezone } from 'lib/date';
export default function TimezoneSetting() {
export function TimezoneSetting() {
const { formatMessage, labels } = useMessages();
const [timezone, saveTimezone] = useTimezone();
const options = listTimeZones();
@ -26,3 +26,5 @@ export default function TimezoneSetting() {
</Flexbox>
);
}
export default TimezoneSetting;

View File

@ -11,7 +11,7 @@ import {
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function TeamAddForm({ onSave, onClose }) {
export function TeamAddForm({ onSave, onClose }) {
const { formatMessage, labels } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error, isLoading } = useMutation(data => post('/teams', data));
@ -44,3 +44,5 @@ export default function TeamAddForm({ onSave, onClose }) {
</Form>
);
}
export default TeamAddForm;

View File

@ -4,7 +4,7 @@ import { Button, Dropdown, Form, FormButtons, FormRow, Item, SubmitButton } from
import WebsiteTags from './WebsiteTags';
import useMessages from 'hooks/useMessages';
export default function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
export function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
const { formatMessage, labels } = useMessages();
const { get, post, useQuery, useMutation } = useApi();
const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data));
@ -44,7 +44,7 @@ export default function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
<>
<Form onSubmit={handleSubmit} error={error} ref={formRef}>
<FormRow label={formatMessage(labels.websites)}>
<Dropdown items={websites} onChange={handleAddWebsite}>
<Dropdown items={websites} onChange={handleAddWebsite} style={{ width: 300 }}>
{({ id, name }) => <Item key={id}>{name}</Item>}
</Dropdown>
</FormRow>
@ -59,3 +59,5 @@ export default function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
</>
);
}
export default TeamAddWebsiteForm;

View File

@ -2,7 +2,7 @@ import { Button, Form, FormButtons, SubmitButton } from 'react-basics';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function TeamDeleteForm({ teamId, teamName, onSave, onClose }) {
export function TeamDeleteForm({ teamId, teamName, onSave, onClose }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { del, useMutation } = useApi();
const { mutate, error, isLoading } = useMutation(data => del(`/teams/${teamId}`, data));
@ -30,3 +30,5 @@ export default function TeamDeleteForm({ teamId, teamName, onSave, onClose }) {
</Form>
);
}
export default TeamDeleteForm;

View File

@ -15,7 +15,7 @@ import useMessages from 'hooks/useMessages';
const generateId = () => getRandomChars(16);
export default function TeamEditForm({ teamId, data, onSave, readOnly }) {
export function TeamEditForm({ teamId, data, onSave, readOnly }) {
const { formatMessage, labels } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation(data => post(`/teams/${teamId}`, data));
@ -69,3 +69,5 @@ export default function TeamEditForm({ teamId, data, onSave, readOnly }) {
</Form>
);
}
export default TeamEditForm;

View File

@ -11,7 +11,7 @@ import {
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function TeamJoinForm({ onSave, onClose }) {
export function TeamJoinForm({ onSave, onClose }) {
const { formatMessage, labels, getMessage } = useMessages();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation(data => post('/teams/join', data));
@ -40,3 +40,5 @@ export default function TeamJoinForm({ onSave, onClose }) {
</Form>
);
}
export default TeamJoinForm;

View File

@ -2,7 +2,7 @@ import { Button, Form, FormButtons, SubmitButton } from 'react-basics';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function TeamLeaveForm({ teamId, userId, teamName, onSave, onClose }) {
export function TeamLeaveForm({ teamId, userId, teamName, onSave, onClose }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { del, useMutation } = useApi();
const { mutate, error, isLoading } = useMutation(() => del(`/team/${teamId}/users/${userId}`));
@ -33,3 +33,5 @@ export default function TeamLeaveForm({ teamId, userId, teamName, onSave, onClos
</Form>
);
}
export default TeamLeaveForm;

View File

@ -2,7 +2,7 @@ import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
export default function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
const { formatMessage, labels } = useMessages();
const { del, useMutation } = useApi();
const { mutate, isLoading } = useMutation(() => del(`/team/${teamId}/users/${userId}`));
@ -27,3 +27,5 @@ export default function TeamMemberRemoveButton({ teamId, userId, disabled, onSav
</LoadingButton>
);
}
export default TeamMemberRemoveButton;

View File

@ -3,7 +3,7 @@ import TeamMembersTable from 'components/pages/settings/teams/TeamMembersTable';
import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
export default function TeamMembers({ teamId, readOnly }) {
export function TeamMembers({ teamId, readOnly }) {
const { toast, showToast } = useToast();
const { get, useQuery } = useApi();
const { formatMessage, messages } = useMessages();
@ -27,3 +27,5 @@ export default function TeamMembers({ teamId, readOnly }) {
</>
);
}
export default TeamMembers;

View File

@ -4,7 +4,7 @@ import { ROLES } from 'lib/constants';
import TeamMemberRemoveButton from './TeamMemberRemoveButton';
import SettingsTable from 'components/common/SettingsTable';
export default function TeamMembersTable({ data = [], onSave, readOnly }) {
export function TeamMembersTable({ data = [], onSave, readOnly }) {
const { formatMessage, labels } = useMessages();
const { user } = useUser();
@ -43,3 +43,5 @@ export default function TeamMembersTable({ data = [], onSave, readOnly }) {
</SettingsTable>
);
}
export default TeamMembersTable;

View File

@ -11,7 +11,7 @@ import TeamEditForm from './TeamEditForm';
import TeamMembers from './TeamMembers';
import TeamWebsites from './TeamWebsites';
export default function TeamSettings({ teamId }) {
export function TeamSettings({ teamId }) {
const { formatMessage, labels, messages } = useMessages();
const { user } = useUser();
const [values, setValues] = useState(null);
@ -68,3 +68,5 @@ export default function TeamSettings({ teamId }) {
</Page>
);
}
export default TeamSettings;

View File

@ -2,7 +2,7 @@ import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
export default function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
export function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
const { formatMessage, labels } = useMessages();
const { del, useMutation } = useApi();
const { mutate, isLoading } = useMutation(() => del(`/teams/${teamId}/websites/${websiteId}`));
@ -27,3 +27,5 @@ export default function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
</LoadingButton>
);
}
export default TeamWebsiteRemoveButton;

Some files were not shown because too many files have changed in this diff Show More