Updated edit forms and icons.
Some checks failed
Create docker images / Build, push, and deploy (push) Has been cancelled
Node.js CI / build (postgresql, 18.18, 10) (push) Has been cancelled

This commit is contained in:
Mike Cao 2025-09-26 01:46:36 -07:00
parent 554054d3a1
commit 257050f749
20 changed files with 120 additions and 123 deletions

View file

@ -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 />}

View file

@ -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>;

View file

@ -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>

View file

@ -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>
</>
); );
} }

View file

@ -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>

View file

@ -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>

View file

@ -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}>

View file

@ -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',

View file

@ -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} />;
}} }}

View file

@ -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'),
}, },
], ],

View file

@ -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>

View file

@ -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 }) {

View file

@ -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)

View file

@ -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>

View file

@ -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>

View file

@ -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>`;

View file

@ -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)}>

View file

@ -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>

View file

@ -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';

View file

@ -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">