mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 05:37:20 +01:00
Reworked settings screens.
This commit is contained in:
parent
c1d301ffdc
commit
0a16ab38e4
58 changed files with 362 additions and 365 deletions
|
|
@ -1,10 +1,11 @@
|
|||
import { Column, Heading } from '@umami/react-zen';
|
||||
import { Column } from '@umami/react-zen';
|
||||
import Link from 'next/link';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
|
||||
export function BoardsPage() {
|
||||
return (
|
||||
<Column>
|
||||
<Heading>My Boards</Heading>
|
||||
<PageHeader title="My Boards" />
|
||||
<Link href="/teams/3a97e34a-7f9d-4de2-8754-ed81714b528d/boards/86d4095c-a2a8-4fc8-9521-103e858e2b41">
|
||||
Board 1
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Link from 'next/link';
|
|||
import Script from 'next/script';
|
||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||
import { Page } from '@/components/common/Page';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { EventsChart } from '@/components/metrics/EventsChart';
|
||||
import { WebsiteChart } from '../websites/[websiteId]/WebsiteChart';
|
||||
import { useApi, useNavigation } from '@/components/hooks';
|
||||
|
|
@ -118,9 +118,9 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
|||
|
||||
return (
|
||||
<Page isLoading={isLoading} error={error}>
|
||||
<PageHeader title="Test console">
|
||||
<SectionHeader title="Test console">
|
||||
<WebsiteSelect websiteId={website?.id} onSelect={handleChange} />
|
||||
</PageHeader>
|
||||
</SectionHeader>
|
||||
{website && (
|
||||
<div className={styles.container}>
|
||||
<Script
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
import { Icon, Icons, Loading, Text } from '@umami/react-zen';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { Pager } from '@/components/common/Pager';
|
||||
import { WebsiteChartList } from '../websites/[websiteId]/WebsiteChartList';
|
||||
import { DashboardSettingsButton } from '@/app/(main)/dashboard/DashboardSettingsButton';
|
||||
|
|
@ -30,9 +30,9 @@ export function DashboardPage() {
|
|||
|
||||
return (
|
||||
<section style={{ marginBottom: 60 }}>
|
||||
<PageHeader title={formatMessage(labels.dashboard)}>
|
||||
<SectionHeader title={formatMessage(labels.dashboard)}>
|
||||
{!editing && hasData && <DashboardSettingsButton />}
|
||||
</PageHeader>
|
||||
</SectionHeader>
|
||||
{!hasData && (
|
||||
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)}>
|
||||
<LinkButton href={renderTeamUrl('/settings')}>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
.field {
|
||||
width: 200px;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
export function ProfileHeader() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return <PageHeader title={formatMessage(labels.profile)}></PageHeader>;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
.container {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
'use client';
|
||||
import { ProfileHeader } from './ProfileHeader';
|
||||
import { ProfileSettings } from './ProfileSettings';
|
||||
import styles from './ProfilePage.module.css';
|
||||
|
||||
export function ProfilePage() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<ProfileHeader />
|
||||
<ProfileSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 2px solid var(--primary-color);
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import { Button, Icon, useTheme } from '@umami/react-zen';
|
||||
import { Icons } from '@/components/icons';
|
||||
import styles from './ThemeSetting.module.css';
|
||||
|
||||
export function ThemeSetting() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<div className={styles.buttons}>
|
||||
<Button
|
||||
className={classNames({ [styles.active]: theme === 'light' })}
|
||||
onPress={() => setTheme('light')}
|
||||
>
|
||||
<Icon>
|
||||
<Icons.Sun />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames({ [styles.active]: theme === 'dark' })}
|
||||
onPress={() => setTheme('dark')}
|
||||
>
|
||||
<Icon>
|
||||
<Icons.Moon />
|
||||
</Icon>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
div.menu {
|
||||
max-height: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { Icon, Icons, Text } from '@umami/react-zen';
|
||||
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
|
|
@ -11,7 +11,7 @@ export function ReportsHeader() {
|
|||
const canEdit = user.role !== ROLES.viewOnly;
|
||||
|
||||
return (
|
||||
<PageHeader title={formatMessage(labels.reports)}>
|
||||
<SectionHeader title={formatMessage(labels.reports)}>
|
||||
{canEdit && (
|
||||
<LinkButton href={renderTeamUrl('/reports/create')} variant="primary">
|
||||
<Icon>
|
||||
|
|
@ -20,6 +20,6 @@ export function ReportsHeader() {
|
|||
<Text>{formatMessage(labels.createReport)}</Text>
|
||||
</LinkButton>
|
||||
)}
|
||||
</PageHeader>
|
||||
</SectionHeader>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Icon, Text, Row, Column, Grid } from '@umami/react-zen';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
|
||||
export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) {
|
||||
|
|
@ -61,7 +61,7 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean })
|
|||
|
||||
return (
|
||||
<>
|
||||
{showHeader && <PageHeader title={formatMessage(labels.reports)} />}
|
||||
{showHeader && <SectionHeader title={formatMessage(labels.reports)} />}
|
||||
<Grid columns="repeat(3, minmax(200px, 1fr))" gap="3">
|
||||
{reports.map(({ title, description, url, icon }) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,31 +1,49 @@
|
|||
'use client';
|
||||
import { ReactNode } from 'react';
|
||||
import { Grid, Column } from '@umami/react-zen';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { SideMenu } from '@/components/common/SideMenu';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
|
||||
export function SettingsLayout({ children }: { children: ReactNode }) {
|
||||
const { user } = useLoginQuery();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { pathname } = useNavigation();
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'websites',
|
||||
id: 'profile',
|
||||
label: formatMessage(labels.profile),
|
||||
url: '/settings/profile',
|
||||
},
|
||||
{ id: 'teams', label: formatMessage(labels.teams), url: '/settings/teams' },
|
||||
user.isAdmin && {
|
||||
id: 'websites',
|
||||
label: formatMessage(labels.websites),
|
||||
url: '/settings/websites',
|
||||
},
|
||||
{ key: 'teams', label: formatMessage(labels.teams), url: '/settings/teams' },
|
||||
user.isAdmin && {
|
||||
key: 'users',
|
||||
id: 'users',
|
||||
label: formatMessage(labels.users),
|
||||
url: '/settings/users',
|
||||
},
|
||||
].filter(n => n);
|
||||
|
||||
const value = items.find(({ url }) => pathname.includes(url))?.id;
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<SideBar items={items} />
|
||||
<Column>{children}</Column>
|
||||
</Grid>
|
||||
<Column gap="6">
|
||||
<PageHeader title={formatMessage(labels.settings)} />
|
||||
|
||||
<Grid columns="160px 1fr" gap="6">
|
||||
<Column marginTop="6">
|
||||
<SideMenu items={items} selectedKey={value} />
|
||||
</Column>
|
||||
<Column>
|
||||
<Panel>{children}</Panel>
|
||||
</Column>
|
||||
</Grid>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { DateFilter } from '@/components/input/DateFilter';
|
||||
import { Button, Flexbox } from '@umami/react-zen';
|
||||
import { Button, Row } from '@umami/react-zen';
|
||||
import { useDateRange, useMessages } from '@/components/hooks';
|
||||
import { DEFAULT_DATE_RANGE } from '@/lib/constants';
|
||||
import { DateRange } from '@/lib/types';
|
||||
import styles from './DateRangeSetting.module.css';
|
||||
|
||||
export function DateRangeSetting() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
|
@ -14,15 +13,14 @@ export function DateRangeSetting() {
|
|||
const handleReset = () => saveDateRange(DEFAULT_DATE_RANGE);
|
||||
|
||||
return (
|
||||
<Flexbox gap="3" width="300px">
|
||||
<Row gap="3">
|
||||
<DateFilter
|
||||
className={styles.field}
|
||||
value={value}
|
||||
startDate={dateRange.startDate}
|
||||
endDate={dateRange.endDate}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Button onPress={handleReset}>{formatMessage(labels.reset)}</Button>
|
||||
</Flexbox>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Button, Icon, Text, useToast, DialogTrigger, Dialog, Modal } from '@umami/react-zen';
|
||||
import { PasswordEditForm } from '@/app/(main)/profile/PasswordEditForm';
|
||||
import { PasswordEditForm } from './PasswordEditForm';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ export function PasswordChangeButton() {
|
|||
return (
|
||||
<DialogTrigger>
|
||||
<Button>
|
||||
<Icon>
|
||||
<Icon fillColor="currentColor">
|
||||
<Icons.Lock />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.changePassword)}</Text>
|
||||
8
src/app/(main)/settings/profile/ProfileHeader.tsx
Normal file
8
src/app/(main)/settings/profile/ProfileHeader.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
export function ProfileHeader() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return <SectionHeader title={formatMessage(labels.profile)}></SectionHeader>;
|
||||
}
|
||||
16
src/app/(main)/settings/profile/ProfilePage.tsx
Normal file
16
src/app/(main)/settings/profile/ProfilePage.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
'use client';
|
||||
import { ProfileSettings } from './ProfileSettings';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
|
||||
export function ProfilePage() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionHeader title={formatMessage(labels.profile)} />
|
||||
|
||||
<ProfileSettings />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { Column, Label } from '@umami/react-zen';
|
||||
import { TimezoneSetting } from '@/app/(main)/profile/TimezoneSetting';
|
||||
import { DateRangeSetting } from '@/app/(main)/profile/DateRangeSetting';
|
||||
import { LanguageSetting } from '@/app/(main)/profile/LanguageSetting';
|
||||
import { ThemeSetting } from '@/app/(main)/profile/ThemeSetting';
|
||||
import { PasswordChangeButton } from './PasswordChangeButton';
|
||||
import { Row, Column, Label } from '@umami/react-zen';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { TimezoneSetting } from './TimezoneSetting';
|
||||
import { DateRangeSetting } from './DateRangeSetting';
|
||||
import { LanguageSetting } from './LanguageSetting';
|
||||
import { ThemeSetting } from './ThemeSetting';
|
||||
import { PasswordChangeButton } from './PasswordChangeButton';
|
||||
|
||||
export function ProfileSettings() {
|
||||
const { user } = useLoginQuery();
|
||||
|
|
@ -47,7 +47,9 @@ export function ProfileSettings() {
|
|||
{!cloudMode && (
|
||||
<Column>
|
||||
<Label>{formatMessage(labels.password)}</Label>
|
||||
<PasswordChangeButton />
|
||||
<Row>
|
||||
<PasswordChangeButton />
|
||||
</Row>
|
||||
</Column>
|
||||
)}
|
||||
|
||||
24
src/app/(main)/settings/profile/ThemeSetting.tsx
Normal file
24
src/app/(main)/settings/profile/ThemeSetting.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { Row, Button, Icon, useTheme } from '@umami/react-zen';
|
||||
import { Icons } from '@/components/icons';
|
||||
|
||||
export function ThemeSetting() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<Row gap>
|
||||
<Button
|
||||
variant={theme === 'light' ? 'primary' : 'secondary'}
|
||||
onPress={() => setTheme('light')}
|
||||
>
|
||||
<Icon fillColor="currentColor">
|
||||
<Icons.Sun />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Button variant={theme === 'dark' ? 'primary' : 'secondary'} onPress={() => setTheme('dark')}>
|
||||
<Icon fillColor="currentColor">
|
||||
<Icons.Moon />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { useState } from 'react';
|
|||
import { Row, Select, ListItem, Button } from '@umami/react-zen';
|
||||
import { useTimezone, useMessages } from '@/components/hooks';
|
||||
import { getTimezone } from '@/lib/date';
|
||||
import styles from './TimezoneSetting.module.css';
|
||||
|
||||
const timezones = Intl.supportedValuesOf('timeZone');
|
||||
|
||||
|
|
@ -25,7 +24,6 @@ export function TimezoneSetting() {
|
|||
return (
|
||||
<Row gap="3">
|
||||
<Select
|
||||
className={styles.dropdown}
|
||||
selectedKey={timezone}
|
||||
onChange={(value: any) => saveTimezone(value)}
|
||||
allowSearch={true}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Row } from '@umami/react-zen';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { TeamsJoinButton } from './TeamsJoinButton';
|
||||
|
|
@ -11,11 +11,11 @@ export function TeamsHeader({ allowCreate = true }: { allowCreate?: boolean }) {
|
|||
const cloudMode = !!process.env.cloudMode;
|
||||
|
||||
return (
|
||||
<PageHeader title={formatMessage(labels.teams)}>
|
||||
<SectionHeader title={formatMessage(labels.teams)}>
|
||||
<Row gap="3">
|
||||
{!cloudMode && <TeamsJoinButton />}
|
||||
{allowCreate && user.role !== ROLES.viewOnly && <TeamsAddButton />}
|
||||
</Row>
|
||||
</PageHeader>
|
||||
</SectionHeader>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export function TeamsJoinButton() {
|
|||
return (
|
||||
<DialogTrigger>
|
||||
<Button variant="secondary">
|
||||
<Icon>
|
||||
<Icon fillColor="currentColor">
|
||||
<Icons.AddUser />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.joinTeam)}</Text>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
'use client';
|
||||
import { TeamsDataTable } from './TeamsDataTable';
|
||||
import { TeamsHeader } from './TeamsHeader';
|
||||
import { Column } from '@umami/react-zen';
|
||||
|
||||
export function TeamsSettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<Column gap>
|
||||
<TeamsHeader />
|
||||
<TeamsDataTable />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { UserAddButton } from './UserAddButton';
|
||||
|
||||
export function UsersHeader({ onAdd }: { onAdd?: () => void }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<PageHeader title={formatMessage(labels.users)}>
|
||||
<UserAddButton onSave={onAdd} />
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,12 +1,21 @@
|
|||
'use client';
|
||||
import { UsersDataTable } from './UsersDataTable';
|
||||
import { UsersHeader } from './UsersHeader';
|
||||
import { Column } from '@umami/react-zen';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { UserAddButton } from '@/app/(main)/settings/users/UserAddButton';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
export function UsersSettingsPage() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const handleSave = () => {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<UsersHeader />
|
||||
<Column gap>
|
||||
<SectionHeader title={formatMessage(labels.users)}>
|
||||
<UserAddButton onSave={handleSave} />
|
||||
</SectionHeader>
|
||||
<UsersDataTable />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useContext } from 'react';
|
|||
import { Tabs, Tab, TabList, TabPanel } from '@umami/react-zen';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { UserEditForm } from './UserEditForm';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { UserWebsites } from './UserWebsites';
|
||||
import { UserContext } from './UserProvider';
|
||||
|
|
@ -13,7 +13,7 @@ export function UserSettings({ userId }: { userId: string }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={user?.username} icon={<Icons.User />} />
|
||||
<SectionHeader title={user?.username} icon={<Icons.User />} />
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab id="details">{formatMessage(labels.details)}</Tab>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { WebsiteAddButton } from './WebsiteAddButton';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
|
||||
export interface WebsitesHeaderProps {
|
||||
allowCreate?: boolean;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
'use client';
|
||||
import { useLoginQuery } from '@/components/hooks';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { WebsitesDataTable } from './WebsitesDataTable';
|
||||
import { WebsitesHeader } from './WebsitesHeader';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { WebsiteAddButton } from '@/app/(main)/settings/websites/WebsiteAddButton';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { Column } from '@umami/react-zen';
|
||||
|
||||
export function WebsitesSettingsPage({ teamId }: { teamId: string }) {
|
||||
const { user } = useLoginQuery();
|
||||
const canCreate = user.role !== ROLES.viewOnly;
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsitesHeader allowCreate={canCreate} />
|
||||
<Column gap>
|
||||
<SectionHeader title={formatMessage(labels.websites)}>
|
||||
{canCreate && <WebsiteAddButton teamId={teamId} />}
|
||||
</SectionHeader>
|
||||
<WebsitesDataTable teamId={teamId} />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { useContext } from 'react';
|
||||
import { Button, Icon, Tabs, TabList, Tab, TabPanel, Text } from '@umami/react-zen';
|
||||
import Link from 'next/link';
|
||||
import { Icon, Tabs, TabList, Tab, TabPanel, Text } from '@umami/react-zen';
|
||||
import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { ShareUrl } from './ShareUrl';
|
||||
import { TrackingCode } from './TrackingCode';
|
||||
import { WebsiteData } from './WebsiteData';
|
||||
import { WebsiteEditForm } from './WebsiteEditForm';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
|
||||
export function WebsiteSettings({
|
||||
websiteId,
|
||||
|
|
@ -22,16 +22,18 @@ export function WebsiteSettings({
|
|||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={website?.name} icon={<Icons.Globe />}>
|
||||
<Link href={`/websites/${websiteId}`} target={openExternal ? '_blank' : null}>
|
||||
<Button variant="primary">
|
||||
<Icon>
|
||||
<Icons.Arrow />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.view)}</Text>
|
||||
</Button>
|
||||
</Link>
|
||||
</PageHeader>
|
||||
<SectionHeader title={website?.name} icon={<Icons.Globe />}>
|
||||
<LinkButton
|
||||
variant="primary"
|
||||
href={`/websites/${websiteId}`}
|
||||
target={openExternal ? '_blank' : null}
|
||||
>
|
||||
<Icon>
|
||||
<Icons.Arrow />
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.view)}</Text>
|
||||
</LinkButton>
|
||||
</SectionHeader>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab id="details">{formatMessage(labels.details)}</Tab>
|
||||
|
|
|
|||
|
|
@ -2,34 +2,48 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { Grid, Column } from '@umami/react-zen';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { SideMenu } from '@/components/common/SideMenu';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
|
||||
export function TeamSettingsLayout({ children }: { children: ReactNode }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { teamId } = useNavigation();
|
||||
const { pathname, teamId } = useNavigation();
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'team',
|
||||
id: 'team',
|
||||
label: formatMessage(labels.team),
|
||||
url: `/teams/${teamId}/settings/team`,
|
||||
},
|
||||
{
|
||||
key: 'websites',
|
||||
id: 'websites',
|
||||
label: formatMessage(labels.websites),
|
||||
url: `/teams/${teamId}/settings/websites`,
|
||||
},
|
||||
{
|
||||
key: 'members',
|
||||
id: 'members',
|
||||
label: formatMessage(labels.members),
|
||||
url: `/teams/${teamId}/settings/members`,
|
||||
},
|
||||
].filter(n => n);
|
||||
|
||||
const value = items.find(({ url }) => pathname.endsWith(url))?.id;
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<SideBar items={items} />
|
||||
<Column>{children}</Column>
|
||||
</Grid>
|
||||
<Column gap="6">
|
||||
<PageHeader title={formatMessage(labels.teamSettings)} />
|
||||
|
||||
<Column gap="6">
|
||||
<Grid columns="200px 1fr">
|
||||
<Column marginTop="6">
|
||||
<SideMenu items={items} selectedKey={value} />
|
||||
</Column>
|
||||
<Column>
|
||||
<Panel>{children}</Panel>
|
||||
</Column>
|
||||
</Grid>
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
'use client';
|
||||
import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
|
||||
import { TeamMembersDataTable } from './TeamMembersDataTable';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { useContext } from 'react';
|
||||
import { Column } from '@umami/react-zen';
|
||||
|
||||
export function TeamMembersPage({ teamId }: { teamId: string }) {
|
||||
const team = useContext(TeamContext);
|
||||
|
|
@ -18,9 +19,9 @@ export function TeamMembersPage({ teamId }: { teamId: string }) {
|
|||
) && user.role !== ROLES.viewOnly;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={formatMessage(labels.members)} />
|
||||
<Column gap>
|
||||
<SectionHeader title={formatMessage(labels.members)} />
|
||||
<TeamMembersDataTable teamId={teamId} allowEdit={canEdit} />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { useContext, useState } from 'react';
|
||||
import { Column, Tabs, TabList, Tab, TabPanel } from '@umami/react-zen';
|
||||
import { TeamLeaveButton } from '@/app/(main)/settings/teams/TeamLeaveButton';
|
||||
import { TeamManage } from './TeamManage';
|
||||
import { TeamEditForm } from './TeamEditForm';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
|
||||
export function TeamDetails({ teamId }: { teamId: string }) {
|
||||
const team = useContext(TeamContext);
|
||||
|
|
@ -27,24 +25,22 @@ export function TeamDetails({ teamId }: { teamId: string }) {
|
|||
) && user.role !== ROLES.viewOnly;
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<PageHeader title={team?.name} icon={<Icons.Users />}>
|
||||
<Column gap>
|
||||
<SectionHeader title={team?.name}>
|
||||
{!isTeamOwner && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
|
||||
</PageHeader>
|
||||
<Panel>
|
||||
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
|
||||
<TabList>
|
||||
<Tab id="details">{formatMessage(labels.details)}</Tab>
|
||||
{isTeamOwner && <Tab id="manage">{formatMessage(labels.manage)}</Tab>}
|
||||
</TabList>
|
||||
<TabPanel id="details">
|
||||
<TeamEditForm teamId={teamId} allowEdit={canEdit} />
|
||||
</TabPanel>
|
||||
<TabPanel id="manage">
|
||||
<TeamManage teamId={teamId} />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>
|
||||
</SectionHeader>
|
||||
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
|
||||
<TabList>
|
||||
<Tab id="details">{formatMessage(labels.details)}</Tab>
|
||||
{isTeamOwner && <Tab id="manage">{formatMessage(labels.manage)}</Tab>}
|
||||
</TabList>
|
||||
<TabPanel id="details">
|
||||
<TeamEditForm teamId={teamId} allowEdit={canEdit} />
|
||||
</TabPanel>
|
||||
<TabPanel id="manage">
|
||||
<TeamManage teamId={teamId} />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
|
||||
import { WebsiteAddButton } from '@/app/(main)/settings/websites/WebsiteAddButton';
|
||||
import { useLoginQuery, useMessages } from '@/components/hooks';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { TeamWebsitesDataTable } from './TeamWebsitesDataTable';
|
||||
import { ROLES } from '@/lib/constants';
|
||||
import { useContext } from 'react';
|
||||
import { Column } from '@umami/react-zen';
|
||||
|
||||
export function TeamWebsitesPage({ teamId }: { teamId: string }) {
|
||||
const team = useContext(TeamContext);
|
||||
|
|
@ -18,11 +19,11 @@ export function TeamWebsitesPage({ teamId }: { teamId: string }) {
|
|||
) && user.role !== ROLES.viewOnly;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={formatMessage(labels.websites)}>
|
||||
<Column gap>
|
||||
<SectionHeader title={formatMessage(labels.websites)}>
|
||||
{canEdit && <WebsiteAddButton teamId={teamId} />}
|
||||
</PageHeader>
|
||||
</SectionHeader>
|
||||
<TeamWebsitesDataTable teamId={teamId} allowEdit={canEdit} />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
'use client';
|
||||
import { WebsitesHeader } from '@/app/(main)/settings/websites/WebsitesHeader';
|
||||
import { WebsitesDataTable } from '@/app/(main)/settings/websites/WebsitesDataTable';
|
||||
import { useNavigation } from '@/components/hooks';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { Column } from '@umami/react-zen';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { WebsiteAddButton } from '@/app/(main)/settings/websites/WebsiteAddButton';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
|
||||
export function WebsitesPage() {
|
||||
const { teamId } = useNavigation();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsitesHeader />
|
||||
<WebsitesDataTable teamId={teamId} allowEdit={false} />
|
||||
</>
|
||||
<Column gap="6">
|
||||
<PageHeader title={formatMessage(labels.websites)}>
|
||||
<WebsiteAddButton teamId={teamId} />
|
||||
</PageHeader>
|
||||
<Panel>
|
||||
<WebsitesDataTable teamId={teamId} allowEdit={false} />
|
||||
</Panel>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Grid, Heading, Column, Row } from '@umami/react-zen';
|
||||
import { useDateRange, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { SideMenu } from '@/components/common/SideMenu';
|
||||
import { BrowsersTable } from '@/components/metrics/BrowsersTable';
|
||||
import { ChangeLabel } from '@/components/metrics/ChangeLabel';
|
||||
import { CitiesTable } from '@/components/metrics/CitiesTable';
|
||||
|
|
@ -146,7 +146,7 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
|||
return (
|
||||
<Panel>
|
||||
<Grid columns={{ xs: '1fr', lg: '200px 1fr 1fr' }} gap="6">
|
||||
<SideBar items={items} selectedKey={view} />
|
||||
<SideMenu items={items} selectedKey={view} />
|
||||
<Column border="left" paddingLeft="6">
|
||||
<Row alignItems="center" justifyContent="space-between">
|
||||
<Heading size="1">{formatMessage(labels.previous)}</Heading>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Icon, Icons, Text, Grid, Column } from '@umami/react-zen';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { SideMenu } from '@/components/common/SideMenu';
|
||||
import { BrowsersTable } from '@/components/metrics/BrowsersTable';
|
||||
import { CitiesTable } from '@/components/metrics/CitiesTable';
|
||||
import { CountriesTable } from '@/components/metrics/CountriesTable';
|
||||
|
|
@ -17,7 +17,6 @@ import { RegionsTable } from '@/components/metrics/RegionsTable';
|
|||
import { ScreenTable } from '@/components/metrics/ScreenTable';
|
||||
import { TagsTable } from '@/components/metrics/TagsTable';
|
||||
import { ChannelsTable } from '@/components/metrics/ChannelsTable';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
|
||||
const views = {
|
||||
url: PagesTable,
|
||||
|
|
@ -56,77 +55,77 @@ export function WebsiteExpandedView({
|
|||
|
||||
const items = [
|
||||
{
|
||||
key: 'url',
|
||||
id: 'url',
|
||||
label: formatMessage(labels.pages),
|
||||
url: renderUrl({ view: 'url' }),
|
||||
},
|
||||
{
|
||||
key: 'referrer',
|
||||
id: 'referrer',
|
||||
label: formatMessage(labels.referrers),
|
||||
url: renderUrl({ view: 'referrer' }),
|
||||
},
|
||||
{
|
||||
key: 'channel',
|
||||
id: 'channel',
|
||||
label: formatMessage(labels.channels),
|
||||
url: renderUrl({ view: 'channel' }),
|
||||
},
|
||||
{
|
||||
key: 'browser',
|
||||
id: 'browser',
|
||||
label: formatMessage(labels.browsers),
|
||||
url: renderUrl({ view: 'browser' }),
|
||||
},
|
||||
{
|
||||
key: 'os',
|
||||
id: 'os',
|
||||
label: formatMessage(labels.os),
|
||||
url: renderUrl({ view: 'os' }),
|
||||
},
|
||||
{
|
||||
key: 'device',
|
||||
id: 'device',
|
||||
label: formatMessage(labels.devices),
|
||||
url: renderUrl({ view: 'device' }),
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
id: 'country',
|
||||
label: formatMessage(labels.countries),
|
||||
url: renderUrl({ view: 'country' }),
|
||||
},
|
||||
{
|
||||
key: 'region',
|
||||
id: 'region',
|
||||
label: formatMessage(labels.regions),
|
||||
url: renderUrl({ view: 'region' }),
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
id: 'city',
|
||||
label: formatMessage(labels.cities),
|
||||
url: renderUrl({ view: 'city' }),
|
||||
},
|
||||
{
|
||||
key: 'language',
|
||||
id: 'language',
|
||||
label: formatMessage(labels.languages),
|
||||
url: renderUrl({ view: 'language' }),
|
||||
},
|
||||
{
|
||||
key: 'screen',
|
||||
id: 'screen',
|
||||
label: formatMessage(labels.screens),
|
||||
url: renderUrl({ view: 'screen' }),
|
||||
},
|
||||
{
|
||||
key: 'event',
|
||||
id: 'event',
|
||||
label: formatMessage(labels.events),
|
||||
url: renderUrl({ view: 'event' }),
|
||||
},
|
||||
{
|
||||
key: 'query',
|
||||
id: 'query',
|
||||
label: formatMessage(labels.queryParameters),
|
||||
url: renderUrl({ view: 'query' }),
|
||||
},
|
||||
{
|
||||
key: 'host',
|
||||
id: 'host',
|
||||
label: formatMessage(labels.hosts),
|
||||
url: renderUrl({ view: 'host' }),
|
||||
},
|
||||
{
|
||||
key: 'tag',
|
||||
id: 'tag',
|
||||
label: formatMessage(labels.tags),
|
||||
url: renderUrl({ view: 'tag' }),
|
||||
},
|
||||
|
|
@ -143,20 +142,18 @@ export function WebsiteExpandedView({
|
|||
</Icon>
|
||||
<Text>{formatMessage(labels.back)}</Text>
|
||||
</LinkButton>
|
||||
<SideBar items={items} selectedKey={view} />
|
||||
<SideMenu items={items} selectedKey={view} />
|
||||
</Column>
|
||||
<Column>
|
||||
<Panel>
|
||||
<DetailsComponent
|
||||
websiteId={websiteId}
|
||||
domainName={domainName}
|
||||
animate={false}
|
||||
virtualize={true}
|
||||
itemCount={25}
|
||||
allowFilter={true}
|
||||
allowSearch={true}
|
||||
/>
|
||||
</Panel>
|
||||
<DetailsComponent
|
||||
websiteId={websiteId}
|
||||
domainName={domainName}
|
||||
animate={false}
|
||||
virtualize={true}
|
||||
itemCount={25}
|
||||
allowFilter={true}
|
||||
allowSearch={true}
|
||||
/>
|
||||
</Column>
|
||||
</Grid>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Page } from '@/components/common/Page';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { SectionHeader } from '@/components/common/SectionHeader';
|
||||
import { useApi, useMessages } from '@/components/hooks';
|
||||
import { EmptyPlaceholder } from '@/components/common/EmptyPlaceholder';
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ export function RealtimeHome() {
|
|||
|
||||
return (
|
||||
<Page isLoading={isLoading || data?.length > 0} error={error}>
|
||||
<PageHeader title={formatMessage(labels.realtime)} />
|
||||
<SectionHeader title={formatMessage(labels.realtime)} />
|
||||
{data?.length === 0 && (
|
||||
<EmptyPlaceholder message={formatMessage(messages.noWebsitesConfigured)} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -36,19 +36,19 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
|
|||
const buttons = [
|
||||
{
|
||||
label: formatMessage(labels.all),
|
||||
key: TYPE_ALL,
|
||||
id: TYPE_ALL,
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.views),
|
||||
key: TYPE_PAGEVIEW,
|
||||
id: TYPE_PAGEVIEW,
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.visitors),
|
||||
key: TYPE_SESSION,
|
||||
id: TYPE_SESSION,
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.events),
|
||||
key: TYPE_EVENT,
|
||||
id: TYPE_EVENT,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
|
|||
<div className={styles.table}>
|
||||
<div className={styles.actions}>
|
||||
<SearchField className={styles.search} value={search} onSearch={setSearch} />
|
||||
<FilterButtons items={buttons} selectedKey={filter} onSelect={setFilter} />
|
||||
<FilterButtons items={buttons} value={filter} onChange={setFilter} />
|
||||
</div>
|
||||
<div className={styles.header}>{formatMessage(labels.activity)}</div>
|
||||
<div className={styles.body}>
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ export function DataGrid({
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Column gap="4">
|
||||
{allowSearch && (hasData || search) && (
|
||||
<Row width="280px" alignItems="center" marginBottom="6">
|
||||
<Row width="280px" alignItems="center">
|
||||
<SearchField
|
||||
value={search}
|
||||
onSearch={handleSearch}
|
||||
|
|
@ -71,6 +71,6 @@ export function DataGrid({
|
|||
</Row>
|
||||
)}
|
||||
</LoadingPanel>
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useLocale } from '@/components/hooks';
|
|||
|
||||
export interface LinkButtonProps {
|
||||
href: string;
|
||||
target?: string;
|
||||
scroll?: boolean;
|
||||
variant?: any;
|
||||
children?: ReactNode;
|
||||
|
|
@ -12,8 +13,9 @@ export interface LinkButtonProps {
|
|||
|
||||
export function LinkButton({
|
||||
href,
|
||||
variant = 'quiet',
|
||||
variant,
|
||||
scroll = true,
|
||||
target,
|
||||
children,
|
||||
...props
|
||||
}: LinkButtonProps) {
|
||||
|
|
@ -21,7 +23,7 @@ export function LinkButton({
|
|||
|
||||
return (
|
||||
<Button {...props} variant={variant} asChild>
|
||||
<Link href={href} dir={dir} scroll={scroll}>
|
||||
<Link href={href} dir={dir} scroll={scroll} target={target}>
|
||||
{children}
|
||||
</Link>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
.page {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 1320px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
align-self: stretch;
|
||||
flex-wrap: wrap;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.header a {
|
||||
color: var(--base600);
|
||||
}
|
||||
|
||||
.header a:hover {
|
||||
color: var(--base900);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
gap: 20px;
|
||||
height: 60px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--base700);
|
||||
margin-inline-end: 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,10 @@ export function PageHeader({
|
|||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Row justifyContent="space-between" alignItems="center" marginY="6">
|
||||
<Row justifyContent="space-between" alignItems="center" paddingY="6" border="bottom">
|
||||
<Row gap="3">
|
||||
{icon && <Icon size="lg">{icon}</Icon>}
|
||||
{title && <Heading size="2">{title}</Heading>}
|
||||
{icon && <Icon>{icon}</Icon>}
|
||||
{title && <Heading size="4">{title}</Heading>}
|
||||
{description && <Text color="muted">{description}</Text>}
|
||||
</Row>
|
||||
<Row justifyContent="flex-end">{children}</Row>
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
.pager {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: var(--font-size-md);
|
||||
margin: 0 16px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: var(--base600);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.pager {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.nav {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
27
src/components/common/SectionHeader.tsx
Normal file
27
src/components/common/SectionHeader.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { Heading, Icon, Row, Text } from '@umami/react-zen';
|
||||
|
||||
export function SectionHeader({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: ReactNode;
|
||||
allowEdit?: boolean;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Row justifyContent="space-between" alignItems="center" height="60px">
|
||||
<Row gap="3" alignItems="center">
|
||||
{icon && <Icon>{icon}</Icon>}
|
||||
{title && <Heading size="3">{title}</Heading>}
|
||||
{description && <Text color="muted">{description}</Text>}
|
||||
</Row>
|
||||
<Row justifyContent="flex-end">{children}</Row>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import { Text, List, ListItem } from '@umami/react-zen';
|
||||
|
||||
export interface MenuNavProps {
|
||||
items: any[];
|
||||
selectedKey?: string;
|
||||
}
|
||||
|
||||
export function SideBar({ items, selectedKey }: MenuNavProps) {
|
||||
return (
|
||||
<List>
|
||||
{items.map(({ key, label, url }) => {
|
||||
return (
|
||||
<ListItem key={key} href={url}>
|
||||
<Text weight={key === selectedKey ? 'bold' : 'regular'}>{label}</Text>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
20
src/components/common/SideMenu.tsx
Normal file
20
src/components/common/SideMenu.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Text, List, ListItem } from '@umami/react-zen';
|
||||
|
||||
export interface MenuNavProps {
|
||||
items: { id: string; label: string; url: string }[];
|
||||
selectedKey?: string;
|
||||
}
|
||||
|
||||
export function SideMenu({ items, selectedKey }: MenuNavProps) {
|
||||
return (
|
||||
<List>
|
||||
{items.map(({ id, label, url }) => {
|
||||
return (
|
||||
<ListItem key={id} id={id} href={url}>
|
||||
<Text weight={id === selectedKey ? 'bold' : 'regular'}>{label}</Text>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
|
@ -106,7 +106,6 @@ export function DateFilter({
|
|||
placeholder={formatMessage(labels.selectDate)}
|
||||
onSelectionChange={handleChange}
|
||||
renderValue={renderValue}
|
||||
style={{ width: 'auto' }}
|
||||
>
|
||||
{options.map(({ label, value, divider }: any) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function ProfileButton() {
|
|||
|
||||
const handleSelect = (key: Key) => {
|
||||
if (key === 'profile') {
|
||||
router.push('/profile');
|
||||
router.push('/settings/profile');
|
||||
}
|
||||
if (key === 'logout') {
|
||||
router.push('/logout');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Button, Icon, DialogTrigger, Popover, Column, Label } from '@umami/react-zen';
|
||||
import { TimezoneSetting } from '@/app/(main)/profile/TimezoneSetting';
|
||||
import { DateRangeSetting } from '@/app/(main)/profile/DateRangeSetting';
|
||||
import { TimezoneSetting } from '@/app/(main)/settings/profile/TimezoneSetting';
|
||||
import { DateRangeSetting } from '@/app/(main)/settings/profile/DateRangeSetting';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export const labels = defineMessages({
|
|||
realtime: { id: 'label.realtime', defaultMessage: 'Realtime' },
|
||||
queries: { id: 'label.queries', defaultMessage: 'Queries' },
|
||||
teams: { id: 'label.teams', defaultMessage: 'Teams' },
|
||||
teamSettings: { id: 'label.team-settings', defaultMessage: 'Team settings' },
|
||||
analytics: { id: 'label.analytics', defaultMessage: 'Analytics' },
|
||||
login: { id: 'label.login', defaultMessage: 'Login' },
|
||||
logout: { id: 'label.logout', defaultMessage: 'Logout' },
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export { default as Profile } from './Profile';
|
|||
export { default as Pushpin } from './Pushpin';
|
||||
export { default as Redo } from './Redo';
|
||||
export { default as Reports } from './Reports';
|
||||
export { default as Security } from './Security';
|
||||
export { default as Speaker } from './Speaker';
|
||||
export { default as Sun } from './Sun';
|
||||
export { default as Tag } from './Tag';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue