mirror of
https://github.com/umami-software/umami.git
synced 2026-02-14 09:35:36 +01:00
Updated edit forms and icons.
This commit is contained in:
parent
554054d3a1
commit
257050f749
20 changed files with 120 additions and 123 deletions
|
|
@ -9,7 +9,8 @@ import {
|
||||||
SidebarProps,
|
SidebarProps,
|
||||||
ThemeButton,
|
ThemeButton,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { Globe, LinkIcon, LogoSvg, Grid2x2, PanelLeft } from '@/components/icons';
|
import { Globe, LinkIcon, Grid2x2, PanelLeft } from '@/components/icons';
|
||||||
|
import { Logo } from '@/components/svg';
|
||||||
import { useMessages, useNavigation, useGlobalState } from '@/components/hooks';
|
import { useMessages, useNavigation, useGlobalState } from '@/components/hooks';
|
||||||
import { NavButton } from '@/components/input/NavButton';
|
import { NavButton } from '@/components/input/NavButton';
|
||||||
import { PanelButton } from '@/components/input/PanelButton';
|
import { PanelButton } from '@/components/input/PanelButton';
|
||||||
|
|
@ -53,7 +54,7 @@ export function SideNav(props: SidebarProps) {
|
||||||
<SidebarSection onClick={() => setIsCollapsed(false)}>
|
<SidebarSection onClick={() => setIsCollapsed(false)}>
|
||||||
<SidebarHeader
|
<SidebarHeader
|
||||||
label="umami"
|
label="umami"
|
||||||
icon={isCollapsed && !hasNav ? <PanelLeft /> : <LogoSvg />}
|
icon={isCollapsed && !hasNav ? <PanelLeft /> : <Logo />}
|
||||||
style={{ maxHeight: 40 }}
|
style={{ maxHeight: 40 }}
|
||||||
>
|
>
|
||||||
{!isCollapsed && !hasNav && <PanelButton />}
|
{!isCollapsed && !hasNav && <PanelButton />}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { SettingsLayout } from './SettingsLayout';
|
||||||
|
|
||||||
export default function ({ children }) {
|
export default function ({ children }) {
|
||||||
if (process.env.cloudMode) {
|
if (process.env.cloudMode) {
|
||||||
return null;
|
//return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <SettingsLayout>{children}</SettingsLayout>;
|
return <SettingsLayout>{children}</SettingsLayout>;
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ import {
|
||||||
FormSubmitButton,
|
FormSubmitButton,
|
||||||
TextField,
|
TextField,
|
||||||
Button,
|
Button,
|
||||||
|
IconLabel,
|
||||||
|
Row,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { getRandomChars } from '@/lib/generate';
|
import { getRandomChars } from '@/lib/generate';
|
||||||
import { useMessages, useTeam, useUpdateQuery } from '@/components/hooks';
|
import { useMessages, useTeam, useUpdateQuery } from '@/components/hooks';
|
||||||
|
import { RefreshCw } from '@/components/icons';
|
||||||
|
|
||||||
const generateId = () => `team_${getRandomChars(16)}`;
|
const generateId = () => `team_${getRandomChars(16)}`;
|
||||||
|
|
||||||
|
|
@ -54,15 +57,25 @@ export function TeamEditForm({
|
||||||
<TextField isReadOnly={!allowEdit} />
|
<TextField isReadOnly={!allowEdit} />
|
||||||
</FormField>
|
</FormField>
|
||||||
{showAccessCode && (
|
{showAccessCode && (
|
||||||
<FormField name="accessCode" label={formatMessage(labels.accessCode)}>
|
<Row alignItems="flex-end" gap>
|
||||||
<TextField isReadOnly allowCopy />
|
<FormField
|
||||||
</FormField>
|
name="accessCode"
|
||||||
|
label={formatMessage(labels.accessCode)}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
>
|
||||||
|
<TextField isReadOnly allowCopy />
|
||||||
|
</FormField>
|
||||||
|
{allowEdit && (
|
||||||
|
<Button
|
||||||
|
onPress={() => setValue('accessCode', generateId(), { shouldDirty: true })}
|
||||||
|
>
|
||||||
|
<IconLabel icon={<RefreshCw />} label={formatMessage(labels.regenerate)} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
)}
|
)}
|
||||||
{allowEdit && (
|
{allowEdit && (
|
||||||
<FormButtons justifyContent="space-between">
|
<FormButtons justifyContent="flex-end">
|
||||||
<Button onPress={() => setValue('accessCode', generateId(), { shouldDirty: true })}>
|
|
||||||
{formatMessage(labels.regenerate)}
|
|
||||||
</Button>
|
|
||||||
<FormSubmitButton variant="primary" isPending={isPending}>
|
<FormSubmitButton variant="primary" isPending={isPending}>
|
||||||
{formatMessage(labels.save)}
|
{formatMessage(labels.save)}
|
||||||
</FormSubmitButton>
|
</FormSubmitButton>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import Link from 'next/link';
|
import { Column } from '@umami/react-zen';
|
||||||
import { Column, Icon, Text, Row } from '@umami/react-zen';
|
import { useLoginQuery, useNavigation, useTeam } from '@/components/hooks';
|
||||||
import { useLoginQuery, useMessages, useNavigation, useTeam } from '@/components/hooks';
|
|
||||||
import { ROLES } from '@/lib/constants';
|
import { ROLES } from '@/lib/constants';
|
||||||
import { Users, ArrowRight } from '@/components/icons';
|
import { Users } from '@/components/icons';
|
||||||
import { TeamLeaveButton } from '@/app/(main)/teams/TeamLeaveButton';
|
import { TeamLeaveButton } from '@/app/(main)/teams/TeamLeaveButton';
|
||||||
import { TeamManage } from './TeamManage';
|
import { TeamManage } from './TeamManage';
|
||||||
import { TeamEditForm } from './TeamEditForm';
|
import { TeamEditForm } from './TeamEditForm';
|
||||||
|
|
@ -13,7 +12,6 @@ import { Panel } from '@/components/common/Panel';
|
||||||
export function TeamSettings({ teamId }: { teamId: string }) {
|
export function TeamSettings({ teamId }: { teamId: string }) {
|
||||||
const team: any = useTeam();
|
const team: any = useTeam();
|
||||||
const { user } = useLoginQuery();
|
const { user } = useLoginQuery();
|
||||||
const { formatMessage, labels } = useMessages();
|
|
||||||
const { pathname } = useNavigation();
|
const { pathname } = useNavigation();
|
||||||
|
|
||||||
const isAdmin = pathname.includes('/admin');
|
const isAdmin = pathname.includes('/admin');
|
||||||
|
|
@ -31,32 +29,21 @@ export function TeamSettings({ teamId }: { teamId: string }) {
|
||||||
user.role !== ROLES.viewOnly);
|
user.role !== ROLES.viewOnly);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Column gap="6">
|
||||||
<Link href="/settings/teams">
|
<PageHeader title={team?.name} icon={<Users />}>
|
||||||
<Row marginTop="2" alignItems="center" gap>
|
{!isTeamOwner && !isAdmin && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
|
||||||
<Icon rotate={180}>
|
</PageHeader>
|
||||||
<ArrowRight />
|
<Panel>
|
||||||
</Icon>
|
<TeamEditForm teamId={teamId} allowEdit={canEdit} showAccessCode={canEdit} />
|
||||||
<Text>{formatMessage(labels.teams)}</Text>
|
</Panel>
|
||||||
</Row>
|
<Panel>
|
||||||
</Link>
|
<TeamMembersDataTable teamId={teamId} allowEdit={canEdit} />
|
||||||
|
</Panel>
|
||||||
<Column gap="6">
|
{isTeamOwner && (
|
||||||
<PageHeader title={team?.name} icon={<Users />}>
|
|
||||||
{!isTeamOwner && !isAdmin && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
|
|
||||||
</PageHeader>
|
|
||||||
<Panel>
|
<Panel>
|
||||||
<TeamEditForm teamId={teamId} allowEdit={canEdit} showAccessCode={canEdit} />
|
<TeamManage teamId={teamId} />
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel>
|
)}
|
||||||
<TeamMembersDataTable teamId={teamId} allowEdit={canEdit} />
|
</Column>
|
||||||
</Panel>
|
|
||||||
{isTeamOwner && (
|
|
||||||
<Panel>
|
|
||||||
<TeamManage teamId={teamId} />
|
|
||||||
</Panel>
|
|
||||||
)}
|
|
||||||
</Column>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { Grid, Column, Row, Text, Icon, ProgressBar, Dialog, Box } from '@umami/react-zen';
|
import { Grid, Column, Row, Text, Icon, ProgressBar, Dialog, Box } from '@umami/react-zen';
|
||||||
import { useMessages, useResultQuery } from '@/components/hooks';
|
import { useMessages, useResultQuery } from '@/components/hooks';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
import { File, LightningSvg, User } from '@/components/icons';
|
import { File, User } from '@/components/icons';
|
||||||
|
import { Lightning } from '@/components/svg';
|
||||||
import { formatLongNumber } from '@/lib/format';
|
import { formatLongNumber } from '@/lib/format';
|
||||||
import { ReportEditButton } from '@/components/input/ReportEditButton';
|
import { ReportEditButton } from '@/components/input/ReportEditButton';
|
||||||
import { FunnelEditForm } from './FunnelEditForm';
|
import { FunnelEditForm } from './FunnelEditForm';
|
||||||
|
|
@ -92,7 +93,7 @@ export function Funnel({ id, name, type, parameters, websiteId }) {
|
||||||
</Row>
|
</Row>
|
||||||
<Row alignItems="center" justifyContent="space-between" gap>
|
<Row alignItems="center" justifyContent="space-between" gap>
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
<Icon>{type === 'path' ? <File /> : <LightningSvg />}</Icon>
|
<Icon>{type === 'path' ? <File /> : <Lightning />}</Icon>
|
||||||
<Text>{value}</Text>
|
<Text>{value}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Grid, Row, Column, Text, Icon, ProgressBar, Dialog } from '@umami/react-zen';
|
import { Grid, Row, Column, Text, Icon, ProgressBar, Dialog } from '@umami/react-zen';
|
||||||
import { ReportEditButton } from '@/components/input/ReportEditButton';
|
import { ReportEditButton } from '@/components/input/ReportEditButton';
|
||||||
import { useMessages, useResultQuery } from '@/components/hooks';
|
import { useMessages, useResultQuery } from '@/components/hooks';
|
||||||
import { File, LightningSvg, User } from '@/components/icons';
|
import { File, Lightning, User } from '@/components/icons';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
import { formatLongNumber } from '@/lib/format';
|
import { formatLongNumber } from '@/lib/format';
|
||||||
import { GoalEditForm } from './GoalEditForm';
|
import { GoalEditForm } from './GoalEditForm';
|
||||||
|
|
@ -68,7 +68,7 @@ export function Goal({ id, name, type, parameters, websiteId, startDate, endDate
|
||||||
</Row>
|
</Row>
|
||||||
<Row alignItems="center" justifyContent="space-between" gap>
|
<Row alignItems="center" justifyContent="space-between" gap>
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
<Icon>{parameters.type === 'path' ? <File /> : <LightningSvg />}</Icon>
|
<Icon>{parameters.type === 'path' ? <File /> : <Lightning />}</Icon>
|
||||||
<Text>{parameters.value}</Text>
|
<Text>{parameters.value}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { TooltipTrigger, Tooltip, Focusable, Icon, Text, Row, Column } from '@um
|
||||||
import { firstBy } from 'thenby';
|
import { firstBy } from 'thenby';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useEscapeKey, useMessages, useResultQuery } from '@/components/hooks';
|
import { useEscapeKey, useMessages, useResultQuery } from '@/components/hooks';
|
||||||
import { File, LightningSvg } from '@/components/icons';
|
import { File, Lightning } from '@/components/icons';
|
||||||
import { objectToArray } from '@/lib/data';
|
import { objectToArray } from '@/lib/data';
|
||||||
import { formatLongNumber } from '@/lib/format';
|
import { formatLongNumber } from '@/lib/format';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
|
|
@ -215,7 +215,7 @@ export function Journey({ websiteId, steps, startStep, endStep }: JourneyProps)
|
||||||
onClick={() => handleClick(name, columnIndex, paths)}
|
onClick={() => handleClick(name, columnIndex, paths)}
|
||||||
>
|
>
|
||||||
<Row alignItems="center" className={styles.name} title={name} gap>
|
<Row alignItems="center" className={styles.name} title={name} gap>
|
||||||
<Icon>{name.startsWith('/') ? <File /> : <LightningSvg />}</Icon>
|
<Icon>{name.startsWith('/') ? <File /> : <Lightning />}</Icon>
|
||||||
<Text truncate>{name}</Text>
|
<Text truncate>{name}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
<div className={styles.count} title={nodeCount}>
|
<div className={styles.count} title={nodeCount}>
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ import {
|
||||||
Languages,
|
Languages,
|
||||||
Monitor,
|
Monitor,
|
||||||
Cpu,
|
Cpu,
|
||||||
LightningSvg,
|
|
||||||
Network,
|
Network,
|
||||||
Tag,
|
Tag,
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
|
import { Lightning } from '@/components/svg';
|
||||||
|
|
||||||
export function WebsiteExpandedView({
|
export function WebsiteExpandedView({
|
||||||
websiteId,
|
websiteId,
|
||||||
|
|
@ -161,7 +161,7 @@ export function WebsiteExpandedView({
|
||||||
id: 'event',
|
id: 'event',
|
||||||
label: formatMessage(labels.event),
|
label: formatMessage(labels.event),
|
||||||
path: updateParams({ view: 'event' }),
|
path: updateParams({ view: 'event' }),
|
||||||
icon: <LightningSvg />,
|
icon: <Lightning />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hostname',
|
id: 'hostname',
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const ShareButton = ({ websiteId, shareId }) => {
|
||||||
<Text>Share</Text>
|
<Text>Share</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Modal>
|
<Modal>
|
||||||
<Dialog title={formatMessage(labels.share)} style={{ width: 600 }}>
|
<Dialog title={formatMessage(labels.share)} style={{ width: 800 }}>
|
||||||
{({ close }) => {
|
{({ close }) => {
|
||||||
return <WebsiteShareForm websiteId={websiteId} shareId={shareId} onClose={close} />;
|
return <WebsiteShareForm websiteId={websiteId} shareId={shareId} onClose={close} />;
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,5 @@
|
||||||
import {
|
import { Eye, User, Clock, Sheet, Tag, ChartPie, UserPlus } from '@/components/icons';
|
||||||
Eye,
|
import { Lightning, Path, Money, Compare, Target, Funnel, Magnet, Network } from '@/components/svg';
|
||||||
LightningSvg,
|
|
||||||
User,
|
|
||||||
Clock,
|
|
||||||
Sheet,
|
|
||||||
TargetSvg,
|
|
||||||
FunnelSvg,
|
|
||||||
PathSvg,
|
|
||||||
MagnetSvg,
|
|
||||||
Tag,
|
|
||||||
MoneySvg,
|
|
||||||
NetworkSvg,
|
|
||||||
ChartPie,
|
|
||||||
UserPlus,
|
|
||||||
CompareSvg,
|
|
||||||
} from '@/components/icons';
|
|
||||||
import { useMessages, useNavigation } from '@/components/hooks';
|
import { useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { SideMenu } from '@/components/common/SideMenu';
|
import { SideMenu } from '@/components/common/SideMenu';
|
||||||
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
|
||||||
|
|
@ -44,7 +29,7 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
||||||
{
|
{
|
||||||
id: 'events',
|
id: 'events',
|
||||||
label: formatMessage(labels.events),
|
label: formatMessage(labels.events),
|
||||||
icon: <LightningSvg />,
|
icon: <Lightning />,
|
||||||
path: renderPath('/events'),
|
path: renderPath('/events'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -62,7 +47,7 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
||||||
{
|
{
|
||||||
id: 'compare',
|
id: 'compare',
|
||||||
label: formatMessage(labels.compare),
|
label: formatMessage(labels.compare),
|
||||||
icon: <CompareSvg />,
|
icon: <Compare />,
|
||||||
path: renderPath('/compare'),
|
path: renderPath('/compare'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -79,25 +64,25 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
||||||
{
|
{
|
||||||
id: 'goals',
|
id: 'goals',
|
||||||
label: formatMessage(labels.goals),
|
label: formatMessage(labels.goals),
|
||||||
icon: <TargetSvg />,
|
icon: <Target />,
|
||||||
path: renderPath('/goals'),
|
path: renderPath('/goals'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'funnel',
|
id: 'funnel',
|
||||||
label: formatMessage(labels.funnels),
|
label: formatMessage(labels.funnels),
|
||||||
icon: <FunnelSvg />,
|
icon: <Funnel />,
|
||||||
path: renderPath('/funnels'),
|
path: renderPath('/funnels'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'journeys',
|
id: 'journeys',
|
||||||
label: formatMessage(labels.journeys),
|
label: formatMessage(labels.journeys),
|
||||||
icon: <PathSvg />,
|
icon: <Path />,
|
||||||
path: renderPath('/journeys'),
|
path: renderPath('/journeys'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'retention',
|
id: 'retention',
|
||||||
label: formatMessage(labels.retention),
|
label: formatMessage(labels.retention),
|
||||||
icon: <MagnetSvg />,
|
icon: <Magnet />,
|
||||||
path: renderPath('/retention'),
|
path: renderPath('/retention'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -131,13 +116,13 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
|
||||||
{
|
{
|
||||||
id: 'revenue',
|
id: 'revenue',
|
||||||
label: formatMessage(labels.revenue),
|
label: formatMessage(labels.revenue),
|
||||||
icon: <MoneySvg />,
|
icon: <Money />,
|
||||||
path: renderPath('/revenue'),
|
path: renderPath('/revenue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'attribution',
|
id: 'attribution',
|
||||||
label: formatMessage(labels.attribution),
|
label: formatMessage(labels.attribution),
|
||||||
icon: <NetworkSvg />,
|
icon: <Network />,
|
||||||
path: renderPath('/attribution'),
|
path: renderPath('/attribution'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import { useFormat, useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { Empty } from '@/components/common/Empty';
|
import { Empty } from '@/components/common/Empty';
|
||||||
import { Avatar } from '@/components/common/Avatar';
|
import { Avatar } from '@/components/common/Avatar';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { LightningSvg, Eye } from '@/components/icons';
|
import { Eye } from '@/components/icons';
|
||||||
|
import { Lightning } from '@/components/svg';
|
||||||
import { DateDistance } from '@/components/common/DateDistance';
|
import { DateDistance } from '@/components/common/DateDistance';
|
||||||
import { TypeIcon } from '@/components/common/TypeIcon';
|
import { TypeIcon } from '@/components/common/TypeIcon';
|
||||||
|
|
||||||
|
|
@ -25,7 +26,7 @@ export function EventsTable({ data = [] }) {
|
||||||
<Link href={renderUrl(`/websites/${row.websiteId}/sessions/${row.sessionId}`)}>
|
<Link href={renderUrl(`/websites/${row.websiteId}/sessions/${row.sessionId}`)}>
|
||||||
<Avatar seed={row.sessionId} size={32} />
|
<Avatar seed={row.sessionId} size={32} />
|
||||||
</Link>
|
</Link>
|
||||||
<Icon>{row.eventName ? <LightningSvg /> : <Eye />}</Icon>
|
<Icon>{row.eventName ? <Lightning /> : <Eye />}</Icon>
|
||||||
<Text>
|
<Text>
|
||||||
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
useTimezone,
|
useTimezone,
|
||||||
useWebsite,
|
useWebsite,
|
||||||
} from '@/components/hooks';
|
} from '@/components/hooks';
|
||||||
import { Eye, User, LightningSvg } from '@/components/icons';
|
import { Eye, User, Lightning } from '@/components/icons';
|
||||||
import { BROWSERS, OS_NAMES } from '@/lib/constants';
|
import { BROWSERS, OS_NAMES } from '@/lib/constants';
|
||||||
import { stringToColor } from '@/lib/format';
|
import { stringToColor } from '@/lib/format';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
@ -24,7 +24,7 @@ const TYPE_EVENT = 'event';
|
||||||
const icons = {
|
const icons = {
|
||||||
[TYPE_PAGEVIEW]: <Eye />,
|
[TYPE_PAGEVIEW]: <Eye />,
|
||||||
[TYPE_SESSION]: <User />,
|
[TYPE_SESSION]: <User />,
|
||||||
[TYPE_EVENT]: <LightningSvg />,
|
[TYPE_EVENT]: <Lightning />,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RealtimeLog({ data }: { data: any }) {
|
export function RealtimeLog({ data }: { data: any }) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
import { LightningSvg, Eye, FileText } from '@/components/icons';
|
import { Lightning, Eye, FileText } from '@/components/icons';
|
||||||
import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks';
|
import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks';
|
||||||
import { EventData } from '@/components/metrics/EventData';
|
import { EventData } from '@/components/metrics/EventData';
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ export function SessionActivity({
|
||||||
{formatTimezoneDate(createdAt, 'pp')}
|
{formatTimezoneDate(createdAt, 'pp')}
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
<Row alignItems="center" gap="2">
|
<Row alignItems="center" gap="2">
|
||||||
<Icon>{eventName ? <LightningSvg /> : <Eye />}</Icon>
|
<Icon>{eventName ? <Lightning /> : <Eye />}</Icon>
|
||||||
<Text>
|
<Text>
|
||||||
{eventName
|
{eventName
|
||||||
? formatMessage(labels.triggeredEvent)
|
? formatMessage(labels.triggeredEvent)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import { ReactNode } from 'react';
|
||||||
import { Icon, TextField, Column, Row, Label, Text } from '@umami/react-zen';
|
import { Icon, TextField, Column, Row, Label, Text } from '@umami/react-zen';
|
||||||
import { useFormat, useLocale, useMessages, useRegionNames } from '@/components/hooks';
|
import { useFormat, useLocale, useMessages, useRegionNames } from '@/components/hooks';
|
||||||
import { TypeIcon } from '@/components/common/TypeIcon';
|
import { TypeIcon } from '@/components/common/TypeIcon';
|
||||||
import { LocationSvg, KeyRound, Calendar } from '@/components/icons';
|
import { KeyRound, Calendar } from '@/components/icons';
|
||||||
|
import { Location } from '@/components/svg';
|
||||||
import { DateDistance } from '@/components/common/DateDistance';
|
import { DateDistance } from '@/components/common/DateDistance';
|
||||||
|
|
||||||
export function SessionInfo({ data }) {
|
export function SessionInfo({ data }) {
|
||||||
|
|
@ -36,11 +37,11 @@ export function SessionInfo({ data }) {
|
||||||
{formatValue(data?.country, 'country')}
|
{formatValue(data?.country, 'country')}
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
<Info label={formatMessage(labels.region)} icon={<LocationSvg />}>
|
<Info label={formatMessage(labels.region)} icon={<Location />}>
|
||||||
{getRegionName(data?.region)}
|
{getRegionName(data?.region)}
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
<Info label={formatMessage(labels.city)} icon={<LocationSvg />}>
|
<Info label={formatMessage(labels.city)} icon={<Location />}>
|
||||||
{data?.city}
|
{data?.city}
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@ import {
|
||||||
Column,
|
Column,
|
||||||
Label,
|
Label,
|
||||||
Row,
|
Row,
|
||||||
|
IconLabel,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { getRandomChars } from '@/lib/generate';
|
import { getRandomChars } from '@/lib/generate';
|
||||||
import { useMessages, useUpdateQuery } from '@/components/hooks';
|
import { useMessages, useUpdateQuery, useConfig } from '@/components/hooks';
|
||||||
|
import { RefreshCcw } from 'lucide-react';
|
||||||
|
|
||||||
const generateId = () => getRandomChars(16);
|
const generateId = () => getRandomChars(16);
|
||||||
|
|
||||||
|
|
@ -24,22 +26,31 @@ export interface WebsiteShareFormProps {
|
||||||
|
|
||||||
export function WebsiteShareForm({ websiteId, shareId, onSave, onClose }: WebsiteShareFormProps) {
|
export function WebsiteShareForm({ websiteId, shareId, onSave, onClose }: WebsiteShareFormProps) {
|
||||||
const { formatMessage, labels, messages, getErrorMessage } = useMessages();
|
const { formatMessage, labels, messages, getErrorMessage } = useMessages();
|
||||||
const [id, setId] = useState(shareId);
|
const [currentId, setCurrentId] = useState(shareId);
|
||||||
const { mutateAsync, error, touch, toast } = useUpdateQuery(`/websites/${websiteId}`);
|
const { mutateAsync, error, touch, toast } = useUpdateQuery(`/websites/${websiteId}`);
|
||||||
|
const { cloudMode } = useConfig();
|
||||||
|
|
||||||
const url = `${window?.location.origin || ''}${process.env.basePath || ''}/share/${id}`;
|
const getUrl = (shareId: string) => {
|
||||||
|
if (cloudMode) {
|
||||||
|
return `${process.env.cloudUrl}/share/${shareId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${window?.location.origin}${process.env.basePath || ''}/share/${shareId}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = getUrl(currentId);
|
||||||
|
|
||||||
const handleGenerate = () => {
|
const handleGenerate = () => {
|
||||||
setId(generateId());
|
setCurrentId(generateId());
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSwitch = () => {
|
const handleSwitch = () => {
|
||||||
setId(id ? null : generateId());
|
setCurrentId(currentId ? null : generateId());
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
const data = {
|
const data = {
|
||||||
shareId: id,
|
shareId: currentId,
|
||||||
};
|
};
|
||||||
await mutateAsync(data, {
|
await mutateAsync(data, {
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
|
|
@ -54,19 +65,23 @@ export function WebsiteShareForm({ websiteId, shareId, onSave, onClose }: Websit
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSave} error={getErrorMessage(error)} values={{ url }}>
|
<Form onSubmit={handleSave} error={getErrorMessage(error)} values={{ url }}>
|
||||||
<Column gap>
|
<Column gap>
|
||||||
<Switch isSelected={!!id} onChange={handleSwitch}>
|
<Switch isSelected={!!currentId} onChange={handleSwitch}>
|
||||||
{formatMessage(labels.enableShareUrl)}
|
{formatMessage(labels.enableShareUrl)}
|
||||||
</Switch>
|
</Switch>
|
||||||
{id && (
|
{currentId && (
|
||||||
<Column>
|
<Row alignItems="flex-end" gap>
|
||||||
<Label>{formatMessage(labels.shareUrl)}</Label>
|
<Column flexGrow={1}>
|
||||||
<TextField value={url} isReadOnly allowCopy />
|
<Label>{formatMessage(labels.shareUrl)}</Label>
|
||||||
</Column>
|
<TextField value={url} isReadOnly allowCopy />
|
||||||
)}
|
</Column>
|
||||||
<FormButtons justifyContent="space-between">
|
<Column>
|
||||||
<Row>
|
<Button onPress={handleGenerate}>
|
||||||
{id && <Button onPress={handleGenerate}>{formatMessage(labels.regenerate)}</Button>}
|
<IconLabel icon={<RefreshCcw />} label={formatMessage(labels.regenerate)} />
|
||||||
|
</Button>
|
||||||
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
|
)}
|
||||||
|
<FormButtons justifyContent="flex-end">
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
{onClose && <Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>}
|
{onClose && <Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>}
|
||||||
<FormSubmitButton isDisabled={false}>{formatMessage(labels.save)}</FormSubmitButton>
|
<FormSubmitButton isDisabled={false}>{formatMessage(labels.save)}</FormSubmitButton>
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,17 @@ export function WebsiteTrackingCode({
|
||||||
const trackerScriptName =
|
const trackerScriptName =
|
||||||
config?.trackerScriptName?.split(',')?.map((n: string) => n.trim())?.[0] || SCRIPT_NAME;
|
config?.trackerScriptName?.split(',')?.map((n: string) => n.trim())?.[0] || SCRIPT_NAME;
|
||||||
|
|
||||||
const url = trackerScriptName?.startsWith('http')
|
const getUrl = () => {
|
||||||
? trackerScriptName
|
if (config?.cloudMode) {
|
||||||
: `${hostUrl || window?.location.origin || ''}${
|
return `${process.env.cloudUrl}/${trackerScriptName}`;
|
||||||
process.env.basePath || ''
|
}
|
||||||
}/${trackerScriptName}`;
|
|
||||||
|
return `${hostUrl || window?.location?.origin || ''}${
|
||||||
|
process.env.basePath || ''
|
||||||
|
}/${trackerScriptName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = trackerScriptName?.startsWith('http') ? trackerScriptName : getUrl();
|
||||||
|
|
||||||
const code = `<script defer src="${url}" data-website-id="${websiteId}"></script>`;
|
const code = `<script defer src="${url}" data-website-id="${websiteId}"></script>`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { useRouter } from 'next/navigation';
|
||||||
import { useMessages, useUpdateQuery } from '@/components/hooks';
|
import { useMessages, useUpdateQuery } from '@/components/hooks';
|
||||||
import { setUser } from '@/store/app';
|
import { setUser } from '@/store/app';
|
||||||
import { setClientAuthToken } from '@/lib/client';
|
import { setClientAuthToken } from '@/lib/client';
|
||||||
import { LogoSvg } from '@/components/icons';
|
import { Logo } from '@/components/svg';
|
||||||
|
|
||||||
export function LoginForm() {
|
export function LoginForm() {
|
||||||
const { formatMessage, labels, getErrorMessage } = useMessages();
|
const { formatMessage, labels, getErrorMessage } = useMessages();
|
||||||
|
|
@ -34,7 +34,7 @@ export function LoginForm() {
|
||||||
return (
|
return (
|
||||||
<Column justifyContent="center" alignItems="center" gap="6">
|
<Column justifyContent="center" alignItems="center" gap="6">
|
||||||
<Icon size="lg">
|
<Icon size="lg">
|
||||||
<LogoSvg />
|
<Logo />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Heading>umami</Heading>
|
<Heading>umami</Heading>
|
||||||
<Form onSubmit={handleSubmit} error={getErrorMessage(error)}>
|
<Form onSubmit={handleSubmit} error={getErrorMessage(error)}>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Row, Icon, Text, ThemeButton } from '@umami/react-zen';
|
import { Row, Icon, Text, ThemeButton } from '@umami/react-zen';
|
||||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
import { LanguageButton } from '@/components/input/LanguageButton';
|
||||||
import { PreferencesButton } from '@/components/input/PreferencesButton';
|
import { PreferencesButton } from '@/components/input/PreferencesButton';
|
||||||
import { LogoSvg } from '@/components/icons';
|
import { Logo } from '@/components/svg';
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -9,7 +9,7 @@ export function Header() {
|
||||||
<a href="https://umami.is" target="_blank">
|
<a href="https://umami.is" target="_blank">
|
||||||
<Row alignItems="center" gap>
|
<Row alignItems="center" gap>
|
||||||
<Icon>
|
<Icon>
|
||||||
<LogoSvg />
|
<Logo />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text weight="bold">umami</Text>
|
<Text weight="bold">umami</Text>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1 @@
|
||||||
export * from 'lucide-react';
|
export * from 'lucide-react';
|
||||||
export {
|
|
||||||
Compare as CompareSvg,
|
|
||||||
Funnel as FunnelSvg,
|
|
||||||
Lightning as LightningSvg,
|
|
||||||
Location as LocationSvg,
|
|
||||||
Logo as LogoSvg,
|
|
||||||
Magnet as MagnetSvg,
|
|
||||||
Money as MoneySvg,
|
|
||||||
Network as NetworkSvg,
|
|
||||||
Path as PathSvg,
|
|
||||||
Switch as SwitchSvg,
|
|
||||||
Target as TargetSvg,
|
|
||||||
} from '@/components/svg';
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ import {
|
||||||
Settings,
|
Settings,
|
||||||
User,
|
User,
|
||||||
Users,
|
Users,
|
||||||
SwitchSvg,
|
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
|
import { Switch } from '@/components/svg';
|
||||||
import { DOCS_URL } from '@/lib/constants';
|
import { DOCS_URL } from '@/lib/constants';
|
||||||
import { ArrowRight } from 'lucide-react';
|
import { ArrowRight } from 'lucide-react';
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ export function NavButton({ showText = true }: TeamsButtonProps) {
|
||||||
<Menu autoFocus="last" onAction={handleAction}>
|
<Menu autoFocus="last" onAction={handleAction}>
|
||||||
<SubmenuTrigger>
|
<SubmenuTrigger>
|
||||||
<MenuItem id="teams" showChecked={false} showSubMenuIcon>
|
<MenuItem id="teams" showChecked={false} showSubMenuIcon>
|
||||||
<IconLabel icon={<SwitchSvg />} label={formatMessage(labels.switchAccount)} />
|
<IconLabel icon={<Switch />} label={formatMessage(labels.switchAccount)} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Popover placement="right top">
|
<Popover placement="right top">
|
||||||
<Column minWidth="300px">
|
<Column minWidth="300px">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue