mirror of
https://github.com/umami-software/umami.git
synced 2026-02-22 13:35:35 +01:00
Compare commits
4 commits
6eefb4173c
...
f073fb1996
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f073fb1996 | ||
|
|
72fba187db | ||
|
|
ef55b63a3b | ||
|
|
c81b1c16c8 |
15 changed files with 176 additions and 135 deletions
|
|
@ -1,15 +1,19 @@
|
||||||
import { Row, NavMenu, NavMenuItem, IconLabel, Text, Grid } from '@umami/react-zen';
|
|
||||||
import { Globe, Grid2x2, LinkIcon } from '@/components/icons';
|
|
||||||
import { useMessages, useNavigation } from '@/components/hooks';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
||||||
import { Logo } from '@/components/svg';
|
import { useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { NavButton } from '@/components/input/NavButton';
|
import { Globe, Grid2x2, LinkIcon } from '@/components/icons';
|
||||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||||
|
import { NavButton } from '@/components/input/NavButton';
|
||||||
|
import { Logo } from '@/components/svg';
|
||||||
|
import { Grid, IconLabel, NavMenu, NavMenuItem, Row, Text } from '@umami/react-zen';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { AdminNav } from './admin/AdminNav';
|
||||||
|
import { SettingsNav } from './settings/SettingsNav';
|
||||||
|
|
||||||
export function MobileNav() {
|
export function MobileNav() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { websiteId, renderUrl } = useNavigation();
|
const { pathname, websiteId, renderUrl } = useNavigation();
|
||||||
|
const isAdmin = pathname.includes('/admin');
|
||||||
|
const isSettings = pathname.includes('/settings');
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
|
|
@ -51,6 +55,8 @@ export function MobileNav() {
|
||||||
})}
|
})}
|
||||||
</NavMenu>
|
</NavMenu>
|
||||||
{websiteId && <WebsiteNav websiteId={websiteId} onItemClick={close} />}
|
{websiteId && <WebsiteNav websiteId={websiteId} onItemClick={close} />}
|
||||||
|
{isAdmin && <AdminNav onItemClick={close} />}
|
||||||
|
{isSettings && <SettingsNav onItemClick={close} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,32 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { ReactNode } from 'react';
|
|
||||||
import { Grid, Column } from '@umami/react-zen';
|
|
||||||
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
|
||||||
import { User, Users, Globe } from '@/components/icons';
|
|
||||||
import { SideMenu } from '@/components/common/SideMenu';
|
|
||||||
import { PageBody } from '@/components/common/PageBody';
|
import { PageBody } from '@/components/common/PageBody';
|
||||||
|
import { useLoginQuery } from '@/components/hooks';
|
||||||
|
import { Column, Grid } from '@umami/react-zen';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { AdminNav } from './AdminNav';
|
||||||
|
|
||||||
export function AdminLayout({ children }: { children: ReactNode }) {
|
export function AdminLayout({ children }: { children: ReactNode }) {
|
||||||
const { user } = useLoginQuery();
|
const { user } = useLoginQuery();
|
||||||
const { formatMessage, labels } = useMessages();
|
|
||||||
const { pathname } = useNavigation();
|
|
||||||
|
|
||||||
if (!user.isAdmin || process.env.cloudMode) {
|
if (!user.isAdmin || process.env.cloudMode) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
label: formatMessage(labels.manage),
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 'users',
|
|
||||||
label: formatMessage(labels.users),
|
|
||||||
path: '/admin/users',
|
|
||||||
icon: <User />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'websites',
|
|
||||||
label: formatMessage(labels.websites),
|
|
||||||
path: '/admin/websites',
|
|
||||||
icon: <Globe />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'teams',
|
|
||||||
label: formatMessage(labels.teams),
|
|
||||||
path: '/admin/teams',
|
|
||||||
icon: <Users />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const selectedKey = items
|
|
||||||
.flatMap(e => e.items)
|
|
||||||
?.find(({ path }) => path && pathname.startsWith(path))?.id;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid columns="auto 1fr" width="100%" height="100%">
|
<Grid columns={{ xs: '1fr', lg: 'auto 1fr' }} width="100%" height="100%">
|
||||||
<Column height="100%" border="right" backgroundColor>
|
<Column
|
||||||
<SideMenu
|
display={{ xs: 'none', lg: 'flex' }}
|
||||||
items={items}
|
width="240px"
|
||||||
title={formatMessage(labels.admin)}
|
height="100%"
|
||||||
selectedKey={selectedKey}
|
border="right"
|
||||||
allowMinimize={false}
|
backgroundColor
|
||||||
/>
|
marginRight="2"
|
||||||
|
>
|
||||||
|
<AdminNav />
|
||||||
|
</Column>
|
||||||
|
<Column gap="6" margin="2">
|
||||||
|
<PageBody>{children}</PageBody>
|
||||||
</Column>
|
</Column>
|
||||||
<PageBody>{children}</PageBody>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
src/app/(main)/admin/AdminNav.tsx
Normal file
48
src/app/(main)/admin/AdminNav.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { SideMenu } from '@/components/common/SideMenu';
|
||||||
|
import { useMessages, useNavigation } from '@/components/hooks';
|
||||||
|
import { Globe, User, Users } from '@/components/icons';
|
||||||
|
|
||||||
|
export function AdminNav({ onItemClick }: { onItemClick?: () => void }) {
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { pathname } = useNavigation();
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
label: formatMessage(labels.manage),
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'users',
|
||||||
|
label: formatMessage(labels.users),
|
||||||
|
path: '/admin/users',
|
||||||
|
icon: <User />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'websites',
|
||||||
|
label: formatMessage(labels.websites),
|
||||||
|
path: '/admin/websites',
|
||||||
|
icon: <Globe />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'teams',
|
||||||
|
label: formatMessage(labels.teams),
|
||||||
|
path: '/admin/teams',
|
||||||
|
icon: <Users />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedKey = items
|
||||||
|
.flatMap(e => e.items)
|
||||||
|
?.find(({ path }) => path && pathname.startsWith(path))?.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SideMenu
|
||||||
|
items={items}
|
||||||
|
title={formatMessage(labels.admin)}
|
||||||
|
selectedKey={selectedKey}
|
||||||
|
allowMinimize={false}
|
||||||
|
onItemClick={onItemClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { useState } from 'react';
|
|
||||||
import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { Trash } from '@/components/icons';
|
|
||||||
import { useMessages } from '@/components/hooks';
|
|
||||||
import { Edit } from '@/components/icons';
|
|
||||||
import { MenuButton } from '@/components/input/MenuButton';
|
|
||||||
import { DateDistance } from '@/components/common/DateDistance';
|
import { DateDistance } from '@/components/common/DateDistance';
|
||||||
|
import { useMessages } from '@/components/hooks';
|
||||||
|
import { Edit, Trash } from '@/components/icons';
|
||||||
|
import { MenuButton } from '@/components/input/MenuButton';
|
||||||
|
import { DataColumn, DataTable, Dialog, Icon, MenuItem, Modal, Row, Text } from '@umami/react-zen';
|
||||||
|
import { TeamDeleteForm } from '../../teams/[teamId]/TeamDeleteForm';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
export function AdminTeamsTable({
|
export function AdminTeamsTable({
|
||||||
data = [],
|
data = [],
|
||||||
|
|
@ -15,7 +15,7 @@ export function AdminTeamsTable({
|
||||||
showActions?: boolean;
|
showActions?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const [deleteUser, setDeleteUser] = useState(null);
|
const [deleteTeam, setDeleteTeam] = useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -60,7 +60,7 @@ export function AdminTeamsTable({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
id="delete"
|
id="delete"
|
||||||
onAction={() => setDeleteUser(row)}
|
onAction={() => setDeleteTeam(id)}
|
||||||
data-test="link-button-delete"
|
data-test="link-button-delete"
|
||||||
>
|
>
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
|
|
@ -76,7 +76,11 @@ export function AdminTeamsTable({
|
||||||
</DataColumn>
|
</DataColumn>
|
||||||
)}
|
)}
|
||||||
</DataTable>
|
</DataTable>
|
||||||
<Modal isOpen={!!deleteUser}></Modal>
|
<Modal isOpen={!!deleteTeam}>
|
||||||
|
<Dialog style={{ width: 400 }}>
|
||||||
|
<TeamDeleteForm teamId={deleteTeam} onClose={() => setDeleteTeam(null)} />
|
||||||
|
</Dialog>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () =
|
||||||
await mutateAsync(data, {
|
await mutateAsync(data, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast(formatMessage(messages.saved));
|
toast(formatMessage(messages.saved));
|
||||||
|
touch('users');
|
||||||
touch(`user:${user.id}`);
|
touch(`user:${user.id}`);
|
||||||
onSave?.();
|
onSave?.();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { PageBody } from '@/components/common/PageBody';
|
import { PageBody } from '@/components/common/PageBody';
|
||||||
import { SideMenu } from '@/components/common/SideMenu';
|
|
||||||
import { useMessages, useNavigation } from '@/components/hooks';
|
|
||||||
import { Settings2, UserCircle, Users } from '@/components/icons';
|
|
||||||
import { Column, Grid } from '@umami/react-zen';
|
import { Column, Grid } from '@umami/react-zen';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import { SettingsNav } from './SettingsNav';
|
||||||
|
|
||||||
export function SettingsLayout({ children }: { children: ReactNode }) {
|
export function SettingsLayout({ children }: { children: ReactNode }) {
|
||||||
const { formatMessage, labels } = useMessages();
|
|
||||||
const { renderUrl, pathname } = useNavigation();
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
label: formatMessage(labels.application),
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 'preferences',
|
|
||||||
label: formatMessage(labels.preferences),
|
|
||||||
path: renderUrl('/settings/preferences'),
|
|
||||||
icon: <Settings2 />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: formatMessage(labels.account),
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 'profile',
|
|
||||||
label: formatMessage(labels.profile),
|
|
||||||
path: renderUrl('/settings/profile'),
|
|
||||||
icon: <UserCircle />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'teams',
|
|
||||||
label: formatMessage(labels.teams),
|
|
||||||
path: renderUrl('/settings/teams'),
|
|
||||||
icon: <Users />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const selectedKey = items
|
|
||||||
.flatMap(e => e.items)
|
|
||||||
.find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid columns={{ xs: '1fr', lg: 'auto 1fr' }} width="100%" height="100%">
|
<Grid columns={{ xs: '1fr', lg: 'auto 1fr' }} width="100%" height="100%">
|
||||||
<Column
|
<Column
|
||||||
|
|
@ -55,12 +15,7 @@ export function SettingsLayout({ children }: { children: ReactNode }) {
|
||||||
backgroundColor
|
backgroundColor
|
||||||
marginRight="2"
|
marginRight="2"
|
||||||
>
|
>
|
||||||
<SideMenu
|
<SettingsNav />
|
||||||
items={items}
|
|
||||||
title={formatMessage(labels.settings)}
|
|
||||||
selectedKey={selectedKey}
|
|
||||||
allowMinimize={false}
|
|
||||||
/>
|
|
||||||
</Column>
|
</Column>
|
||||||
<Column gap="6" margin="2">
|
<Column gap="6" margin="2">
|
||||||
<PageBody>{children}</PageBody>
|
<PageBody>{children}</PageBody>
|
||||||
|
|
|
||||||
53
src/app/(main)/settings/SettingsNav.tsx
Normal file
53
src/app/(main)/settings/SettingsNav.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { SideMenu } from '@/components/common/SideMenu';
|
||||||
|
import { useMessages, useNavigation } from '@/components/hooks';
|
||||||
|
import { Settings2, UserCircle, Users } from '@/components/icons';
|
||||||
|
|
||||||
|
export function SettingsNav({ onItemClick }: { onItemClick?: () => void }) {
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { renderUrl, pathname } = useNavigation();
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
label: formatMessage(labels.application),
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'preferences',
|
||||||
|
label: formatMessage(labels.preferences),
|
||||||
|
path: renderUrl('/settings/preferences'),
|
||||||
|
icon: <Settings2 />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: formatMessage(labels.account),
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'profile',
|
||||||
|
label: formatMessage(labels.profile),
|
||||||
|
path: renderUrl('/settings/profile'),
|
||||||
|
icon: <UserCircle />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'teams',
|
||||||
|
label: formatMessage(labels.teams),
|
||||||
|
path: renderUrl('/settings/teams'),
|
||||||
|
icon: <Users />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedKey = items
|
||||||
|
.flatMap(e => e.items)
|
||||||
|
.find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SideMenu
|
||||||
|
items={items}
|
||||||
|
title={formatMessage(labels.settings)}
|
||||||
|
selectedKey={selectedKey}
|
||||||
|
allowMinimize={false}
|
||||||
|
onItemClick={onItemClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ export function TeamDeleteForm({
|
||||||
await mutateAsync(null, {
|
await mutateAsync(null, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
touch('teams');
|
touch('teams');
|
||||||
|
touch(`teams:${teamId}`);
|
||||||
onSave?.();
|
onSave?.();
|
||||||
onClose?.();
|
onClose?.();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,27 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { useState } from 'react';
|
|
||||||
import { Button, Column, Box, DialogTrigger, Popover, Dialog, IconLabel } from '@umami/react-zen';
|
|
||||||
import { useDateRange, useMessages } from '@/components/hooks';
|
|
||||||
import { ListCheck } from '@/components/icons';
|
|
||||||
import { Panel } from '@/components/common/Panel';
|
|
||||||
import { Breakdown } from './Breakdown';
|
|
||||||
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
|
|
||||||
import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/FieldSelectForm';
|
import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/FieldSelectForm';
|
||||||
|
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
|
||||||
|
import { Panel } from '@/components/common/Panel';
|
||||||
|
import { useDateRange, useMessages, useMobile } from '@/components/hooks';
|
||||||
|
import { ListCheck } from '@/components/icons';
|
||||||
|
import { DialogButton } from '@/components/input/DialogButton';
|
||||||
|
import { Column, Row } from '@umami/react-zen';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Breakdown } from './Breakdown';
|
||||||
|
|
||||||
export function BreakdownPage({ websiteId }: { websiteId: string }) {
|
export function BreakdownPage({ websiteId }: { websiteId: string }) {
|
||||||
const {
|
const {
|
||||||
dateRange: { startDate, endDate },
|
dateRange: { startDate, endDate },
|
||||||
} = useDateRange();
|
} = useDateRange();
|
||||||
const [fields, setFields] = useState(['path']);
|
const [fields, setFields] = useState(['path']);
|
||||||
|
const { isMobile } = useMobile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap>
|
<Column gap>
|
||||||
<WebsiteControls websiteId={websiteId} />
|
<WebsiteControls websiteId={websiteId} />
|
||||||
<FieldsButton value={fields} onChange={setFields} />
|
<Row alignItems="center" justifyContent={isMobile ? 'flex-end' : 'flex-start'}>
|
||||||
|
<FieldsButton value={fields} onChange={setFields} />
|
||||||
|
</Row>
|
||||||
<Panel height="900px" overflow="auto" allowFullscreen>
|
<Panel height="900px" overflow="auto" allowFullscreen>
|
||||||
<Breakdown
|
<Breakdown
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
|
|
@ -34,19 +38,15 @@ const FieldsButton = ({ value, onChange }) => {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<DialogButton
|
||||||
<DialogTrigger>
|
icon={<ListCheck />}
|
||||||
<Button>
|
label={formatMessage(labels.fields)}
|
||||||
<IconLabel icon={<ListCheck />}>{formatMessage(labels.fields)}</IconLabel>
|
width="800px"
|
||||||
</Button>
|
minHeight="300px"
|
||||||
<Popover>
|
>
|
||||||
<Dialog title={formatMessage(labels.fields)} style={{ width: 400 }}>
|
{({ close }) => {
|
||||||
{({ close }) => (
|
return <FieldSelectForm selectedFields={value} onChange={onChange} onClose={close} />;
|
||||||
<FieldSelectForm selectedFields={value} onChange={onChange} onClose={close} />
|
}}
|
||||||
)}
|
</DialogButton>
|
||||||
</Dialog>
|
|
||||||
</Popover>
|
|
||||||
</DialogTrigger>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function CohortAddButton({ websiteId }: { websiteId: string }) {
|
||||||
label={formatMessage(labels.cohort)}
|
label={formatMessage(labels.cohort)}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
width="800px"
|
width="800px"
|
||||||
minHeight="300px"
|
height="calc(100dvh - 40px)"
|
||||||
>
|
>
|
||||||
{({ close }) => {
|
{({ close }) => {
|
||||||
return <CohortEditForm websiteId={websiteId} onClose={close} />;
|
return <CohortEditForm websiteId={websiteId} onClose={close} />;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export function CohortEditButton({
|
||||||
variant="quiet"
|
variant="quiet"
|
||||||
title={formatMessage(labels.cohort)}
|
title={formatMessage(labels.cohort)}
|
||||||
width="800px"
|
width="800px"
|
||||||
minHeight="300px"
|
height="calc(100dvh - 40px)"
|
||||||
>
|
>
|
||||||
{({ close }) => {
|
{({ close }) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export function SegmentAddButton({ websiteId }: { websiteId: string }) {
|
||||||
label={formatMessage(labels.segment)}
|
label={formatMessage(labels.segment)}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
width="800px"
|
width="800px"
|
||||||
|
height="calc(100dvh - 40px)"
|
||||||
>
|
>
|
||||||
{({ close }) => {
|
{({ close }) => {
|
||||||
return <SegmentEditForm websiteId={websiteId} onClose={close} />;
|
return <SegmentEditForm websiteId={websiteId} onClose={close} />;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ export function SegmentEditButton({
|
||||||
title={formatMessage(labels.segment)}
|
title={formatMessage(labels.segment)}
|
||||||
variant="quiet"
|
variant="quiet"
|
||||||
width="800px"
|
width="800px"
|
||||||
|
height="calc(100dvh - 40px)"
|
||||||
>
|
>
|
||||||
{({ close }) => {
|
{({ close }) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export function WebsiteEditForm({ websiteId, onSave }: { websiteId: string; onSa
|
||||||
await mutateAsync(data, {
|
await mutateAsync(data, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast(formatMessage(messages.saved));
|
toast(formatMessage(messages.saved));
|
||||||
|
touch('websites');
|
||||||
touch(`website:${website.id}`);
|
touch(`website:${website.id}`);
|
||||||
onSave?.();
|
onSave?.();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ async function relationalQuery(
|
||||||
where event_data.website_id = {{websiteId::uuid}}
|
where event_data.website_id = {{websiteId::uuid}}
|
||||||
and event_data.created_at between {{startDate}} and {{endDate}}
|
and event_data.created_at between {{startDate}} and {{endDate}}
|
||||||
and event_data.data_key = {{propertyName}}
|
and event_data.data_key = {{propertyName}}
|
||||||
and website_event.event_name = {{eventName}}
|
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by value
|
group by value
|
||||||
order by 2 desc
|
order by 2 desc
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue