Use FormattedMessage. Updated icons. Fixed bugs.

This commit is contained in:
Mike Cao 2025-09-23 23:08:40 -07:00
parent 3afe843461
commit 83a014e884
20 changed files with 129 additions and 84 deletions

View file

@ -15,7 +15,7 @@ export function LinkDeleteButton({
name: string;
onSave?: () => void;
}) {
const { formatMessage, labels, getErrorMessage } = useMessages();
const { formatMessage, labels, getErrorMessage, FormattedMessage } = useMessages();
const { mutateAsync, isPending, error, touch } = useDeleteQuery(`/links/${linkId}`);
const handleConfirm = async (close: () => void) => {
@ -33,9 +33,14 @@ export function LinkDeleteButton({
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
{({ close }) => (
<ConfirmationForm
message={formatMessage(messages.confirmRemove, {
target: name,
})}
message={
<FormattedMessage
{...messages.confirmRemove}
values={{
target: <b>{name}</b>,
}}
/>
}
isLoading={isPending}
error={getErrorMessage(error)}
onConfirm={handleConfirm.bind(null, close)}

View file

@ -13,7 +13,7 @@ export function PixelDeleteButton({
name: string;
onSave?: () => void;
}) {
const { formatMessage, labels, getErrorMessage } = useMessages();
const { formatMessage, labels, getErrorMessage, FormattedMessage } = useMessages();
const { mutateAsync, isPending, error } = useDeleteQuery(`/pixels/${pixelId}`);
const { touch } = useModified();
@ -32,9 +32,14 @@ export function PixelDeleteButton({
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
{({ close }) => (
<ConfirmationForm
message={formatMessage(messages.confirmRemove, {
target: name,
})}
message={
<FormattedMessage
{...messages.confirmRemove}
values={{
target: <b>{name}</b>,
}}
/>
}
isLoading={isPending}
error={getErrorMessage(error)}
onConfirm={handleConfirm.bind(null, close)}

View file

@ -14,7 +14,7 @@ export function TeamLeaveForm({
onSave: () => void;
onClose: () => void;
}) {
const { formatMessage, labels, messages, getErrorMessage } = useMessages();
const { formatMessage, labels, messages, getErrorMessage, FormattedMessage } = useMessages();
const { mutateAsync, error, isPending } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
const { touch } = useModified();
@ -31,9 +31,14 @@ export function TeamLeaveForm({
return (
<ConfirmationForm
buttonLabel={formatMessage(labels.leave)}
message={formatMessage(messages.confirmLeave, {
target: teamName,
})}
message={
<FormattedMessage
{...messages.confirmLeave}
values={{
target: <b>{teamName}</b>,
}}
/>
}
onConfirm={handleConfirm}
onClose={onClose}
isLoading={isPending}

View file

@ -1,16 +1,9 @@
import { ReactNode } from 'react';
import Link from 'next/link';
import { DataGrid } from '@/components/common/DataGrid';
import { TeamsTable } from './TeamsTable';
import { useLoginQuery, useNavigation, useUserTeamsQuery } from '@/components/hooks';
export function TeamsDataTable({
showActions,
}: {
allowEdit?: boolean;
showActions?: boolean;
children?: ReactNode;
}) {
export function TeamsDataTable() {
const { user } = useLoginQuery();
const query = useUserTeamsQuery(user.id);
const { pathname } = useNavigation();
@ -27,7 +20,7 @@ export function TeamsDataTable({
return (
<DataGrid query={query}>
{({ data }) => {
return <TeamsTable data={data} showActions={showActions} renderLink={renderLink} />;
return <TeamsTable data={data} renderLink={renderLink} />;
}}
</DataGrid>
);

View file

@ -1,5 +1,5 @@
import { Button, Icon, Modal, DialogTrigger, Dialog, Text, useToast } from '@umami/react-zen';
import { AddUser } from '@/components/icons';
import { UserPlus } from '@/components/icons';
import { useMessages, useModified } from '@/components/hooks';
import { TeamJoinForm } from './TeamJoinForm';
@ -17,7 +17,7 @@ export function TeamsJoinButton() {
<DialogTrigger>
<Button>
<Icon>
<AddUser />
<UserPlus />
</Icon>
<Text>{formatMessage(labels.joinTeam)}</Text>
</Button>

View file

@ -17,7 +17,7 @@ export function TeamMemberRemoveButton({
disabled?: boolean;
onSave?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { formatMessage, labels, FormattedMessage } = useMessages();
const { mutateAsync, isPending, error } = useDeleteQuery(`/teams/${teamId}/users/${userId}`);
const { touch } = useModified();
@ -36,9 +36,14 @@ export function TeamMemberRemoveButton({
<Dialog title={formatMessage(labels.removeMember)} style={{ width: 400 }}>
{({ close }) => (
<ConfirmationForm
message={formatMessage(messages.confirmRemove, {
target: userName,
})}
message={
<FormattedMessage
{...messages.confirmRemove}
values={{
target: <b>{userName}</b>,
}}
/>
}
isLoading={isPending}
error={error}
onConfirm={handleConfirm.bind(null, close)}

View file

@ -2,7 +2,7 @@ import Link from 'next/link';
import { Column, Icon, Text, Row } from '@umami/react-zen';
import { useLoginQuery, useMessages, useNavigation, useTeam } from '@/components/hooks';
import { ROLES } from '@/lib/constants';
import { Users, Arrow } from '@/components/icons';
import { Users, ArrowRight } from '@/components/icons';
import { TeamLeaveButton } from '@/app/(main)/teams/TeamLeaveButton';
import { TeamManage } from './TeamManage';
import { TeamEditForm } from './TeamEditForm';
@ -35,7 +35,7 @@ export function TeamSettings({ teamId }: { teamId: string }) {
<Link href="/settings/teams">
<Row marginTop="2" alignItems="center" gap>
<Icon rotate={180}>
<Arrow />
<ArrowRight />
</Icon>
<Text>{formatMessage(labels.teams)}</Text>
</Row>

View file

@ -14,7 +14,7 @@ export function WebsitesPage() {
return (
<PageBody>
<Column gap="6" margin="2">
<PageHeader title={formatMessage(labels.websites)}>
<PageHeader title={formatMessage(labels.websites)} label={'back'} description={'Websites'}>
<WebsiteAddButton teamId={teamId} />
</PageHeader>
<Panel>

View file

@ -14,12 +14,12 @@ export function WebsiteFilterButton({
showText?: boolean;
}) {
const { formatMessage, labels } = useMessages();
const { replaceParams, router } = useNavigation();
const { updateParams, router } = useNavigation();
const handleChange = ({ filters, segment, cohort }: any) => {
const params = filtersArrayToObject(filters);
const url = replaceParams({ ...params, segment, cohort });
const url = updateParams({ ...params, segment, cohort });
router.push(url);
};

View file

@ -16,7 +16,7 @@ export function CohortDeleteButton({
name: string;
onSave?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { formatMessage, labels, FormattedMessage } = useMessages();
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
`/websites/${websiteId}/segments/${cohortId}`,
);
@ -36,9 +36,14 @@ export function CohortDeleteButton({
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
{({ close }) => (
<ConfirmationForm
message={formatMessage(messages.confirmRemove, {
target: name,
})}
message={
<FormattedMessage
{...messages.confirmRemove}
values={{
target: <b>{name}</b>,
}}
/>
}
isLoading={isPending}
error={error}
onConfirm={handleConfirm.bind(null, close)}

View file

@ -3,7 +3,7 @@ import { useFormat, useMessages, useNavigation } from '@/components/hooks';
import { Empty } from '@/components/common/Empty';
import { Avatar } from '@/components/common/Avatar';
import Link from 'next/link';
import { Bolt, Eye } from '@/components/icons';
import { LightningSvg, Eye } from '@/components/icons';
import { DateDistance } from '@/components/common/DateDistance';
import { TypeIcon } from '@/components/common/TypeIcon';
@ -25,7 +25,7 @@ export function EventsTable({ data = [] }) {
<Link href={renderUrl(`/websites/${row.websiteId}/sessions/${row.sessionId}`)}>
<Avatar seed={row.sessionId} size={32} />
</Link>
<Icon>{row.eventName ? <Bolt /> : <Eye />}</Icon>
<Icon>{row.eventName ? <LightningSvg /> : <Eye />}</Icon>
<Text>
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
</Text>

View file

@ -8,7 +8,7 @@ import {
useTimezone,
useWebsite,
} from '@/components/hooks';
import { Eye, Visitor, Bolt } from '@/components/icons';
import { Eye, User, LightningSvg } from '@/components/icons';
import { BROWSERS, OS_NAMES } from '@/lib/constants';
import { stringToColor } from '@/lib/format';
import { useMemo, useState } from 'react';
@ -23,14 +23,14 @@ const TYPE_EVENT = 'event';
const icons = {
[TYPE_PAGEVIEW]: <Eye />,
[TYPE_SESSION]: <Visitor />,
[TYPE_EVENT]: <Bolt />,
[TYPE_SESSION]: <User />,
[TYPE_EVENT]: <LightningSvg />,
};
export function RealtimeLog({ data }: { data: any }) {
const website = useWebsite();
const [search, setSearch] = useState('');
const { formatMessage, labels, messages } = useMessages();
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { formatValue } = useFormat();
const { locale } = useLocale();
const { formatTimezoneDate } = useTimezone();
@ -74,20 +74,25 @@ export function RealtimeLog({ data }: { data: any }) {
const { __type, eventName, urlPath, browser, os, country, device } = log;
if (__type === TYPE_EVENT) {
return formatMessage(messages.eventLog, {
event: <b key="b">{eventName || formatMessage(labels.unknown)}</b>,
url: (
<a
key="a"
href={`//${website?.domain}${urlPath}`}
className={styles.link}
target="_blank"
rel="noreferrer noopener"
>
{urlPath}
</a>
),
});
return (
<FormattedMessage
{...messages.eventLog}
values={{
event: <b key="b">{eventName || formatMessage(labels.unknown)}</b>,
url: (
<a
key="a"
href={`//${website?.domain}${urlPath}`}
className={styles.link}
target="_blank"
rel="noreferrer noopener"
>
{urlPath}
</a>
),
}}
/>
);
}
if (__type === TYPE_PAGEVIEW) {
@ -104,12 +109,17 @@ export function RealtimeLog({ data }: { data: any }) {
}
if (__type === TYPE_SESSION) {
return formatMessage(messages.visitorLog, {
country: <b key="country">{countryNames[country] || formatMessage(labels.unknown)}</b>,
browser: <b key="browser">{BROWSERS[browser]}</b>,
os: <b key="os">{OS_NAMES[os] || os}</b>,
device: <b key="device">{formatMessage(labels[device] || labels.unknown)}</b>,
});
return (
<FormattedMessage
{...messages.visitorLog}
values={{
country: <b key="country">{countryNames[country] || formatMessage(labels.unknown)}</b>,
browser: <b key="browser">{BROWSERS[browser]}</b>,
os: <b key="os">{OS_NAMES[os] || os}</b>,
device: <b key="device">{formatMessage(labels[device] || labels.unknown)}</b>,
}}
/>
);
}
};

View file

@ -16,7 +16,7 @@ export function SegmentDeleteButton({
name: string;
onSave?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { formatMessage, labels, FormattedMessage } = useMessages();
const { mutateAsync, isPending, error, touch } = useDeleteQuery(
`/websites/${websiteId}/segments/${segmentId}`,
);
@ -36,9 +36,14 @@ export function SegmentDeleteButton({
<Dialog title={formatMessage(labels.confirm)} style={{ width: 400 }}>
{({ close }) => (
<ConfirmationForm
message={formatMessage(messages.confirmRemove, {
target: name,
})}
message={
<FormattedMessage
{...messages.confirmRemove}
values={{
target: <b>{name}</b>,
}}
/>
}
isLoading={isPending}
error={error}
onConfirm={handleConfirm.bind(null, close)}

View file

@ -12,7 +12,7 @@ import {
Dialog,
} from '@umami/react-zen';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { Bolt, Eye, FileText } from '@/components/icons';
import { LightningSvg, Eye, FileText } from '@/components/icons';
import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks';
import { EventData } from '@/components/metrics/EventData';
@ -52,7 +52,7 @@ export function SessionActivity({
{formatTimezoneDate(createdAt, 'pp')}
</StatusLight>
<Row alignItems="center" gap="2">
<Icon>{eventName ? <Bolt /> : <Eye />}</Icon>
<Icon>{eventName ? <LightningSvg /> : <Eye />}</Icon>
<Text>
{eventName
? formatMessage(labels.triggeredEvent)