From 83a014e8846f8e04e93788c0c9e4263f26cd9be8 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 23 Sep 2025 23:08:40 -0700 Subject: [PATCH] Use FormattedMessage. Updated icons. Fixed bugs. --- src/app/(main)/links/LinkDeleteButton.tsx | 13 +++-- src/app/(main)/pixels/PixelDeleteButton.tsx | 13 +++-- src/app/(main)/teams/TeamLeaveForm.tsx | 13 +++-- src/app/(main)/teams/TeamsDataTable.tsx | 11 +--- src/app/(main)/teams/TeamsJoinButton.tsx | 4 +- .../teams/[teamId]/TeamMemberRemoveButton.tsx | 13 +++-- .../(main)/teams/[teamId]/TeamSettings.tsx | 4 +- src/app/(main)/websites/WebsitesPage.tsx | 2 +- .../[websiteId]/WebsiteFilterButton.tsx | 4 +- .../cohorts/CohortDeleteButton.tsx | 13 +++-- .../[websiteId]/events/EventsTable.tsx | 4 +- .../[websiteId]/realtime/RealtimeLog.tsx | 58 +++++++++++-------- .../segments/SegmentDeleteButton.tsx | 13 +++-- .../sessions/[sessionId]/SessionActivity.tsx | 4 +- src/components/common/PageHeader.tsx | 5 +- src/components/hooks/useMessages.ts | 28 ++++++--- src/components/icons.ts | 4 -- src/components/input/WebsiteDateFilter.tsx | 3 +- src/components/metrics/WeeklyTraffic.tsx | 2 +- src/lib/request.ts | 2 +- 20 files changed, 129 insertions(+), 84 deletions(-) diff --git a/src/app/(main)/links/LinkDeleteButton.tsx b/src/app/(main)/links/LinkDeleteButton.tsx index 05a3217f..6754074f 100644 --- a/src/app/(main)/links/LinkDeleteButton.tsx +++ b/src/app/(main)/links/LinkDeleteButton.tsx @@ -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({ {({ close }) => ( {name}, + }} + /> + } isLoading={isPending} error={getErrorMessage(error)} onConfirm={handleConfirm.bind(null, close)} diff --git a/src/app/(main)/pixels/PixelDeleteButton.tsx b/src/app/(main)/pixels/PixelDeleteButton.tsx index c90261e5..98e34c1f 100644 --- a/src/app/(main)/pixels/PixelDeleteButton.tsx +++ b/src/app/(main)/pixels/PixelDeleteButton.tsx @@ -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({ {({ close }) => ( {name}, + }} + /> + } isLoading={isPending} error={getErrorMessage(error)} onConfirm={handleConfirm.bind(null, close)} diff --git a/src/app/(main)/teams/TeamLeaveForm.tsx b/src/app/(main)/teams/TeamLeaveForm.tsx index c8063365..1c7846b8 100644 --- a/src/app/(main)/teams/TeamLeaveForm.tsx +++ b/src/app/(main)/teams/TeamLeaveForm.tsx @@ -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 ( {teamName}, + }} + /> + } onConfirm={handleConfirm} onClose={onClose} isLoading={isPending} diff --git a/src/app/(main)/teams/TeamsDataTable.tsx b/src/app/(main)/teams/TeamsDataTable.tsx index 6475c456..5f81bca1 100644 --- a/src/app/(main)/teams/TeamsDataTable.tsx +++ b/src/app/(main)/teams/TeamsDataTable.tsx @@ -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 ( {({ data }) => { - return ; + return ; }} ); diff --git a/src/app/(main)/teams/TeamsJoinButton.tsx b/src/app/(main)/teams/TeamsJoinButton.tsx index 7e1e24e7..74295497 100644 --- a/src/app/(main)/teams/TeamsJoinButton.tsx +++ b/src/app/(main)/teams/TeamsJoinButton.tsx @@ -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() { diff --git a/src/app/(main)/teams/[teamId]/TeamMemberRemoveButton.tsx b/src/app/(main)/teams/[teamId]/TeamMemberRemoveButton.tsx index 22c871a3..065f637b 100644 --- a/src/app/(main)/teams/[teamId]/TeamMemberRemoveButton.tsx +++ b/src/app/(main)/teams/[teamId]/TeamMemberRemoveButton.tsx @@ -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({ {({ close }) => ( {userName}, + }} + /> + } isLoading={isPending} error={error} onConfirm={handleConfirm.bind(null, close)} diff --git a/src/app/(main)/teams/[teamId]/TeamSettings.tsx b/src/app/(main)/teams/[teamId]/TeamSettings.tsx index c5994c0d..9649c4f7 100644 --- a/src/app/(main)/teams/[teamId]/TeamSettings.tsx +++ b/src/app/(main)/teams/[teamId]/TeamSettings.tsx @@ -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 }) { - + {formatMessage(labels.teams)} diff --git a/src/app/(main)/websites/WebsitesPage.tsx b/src/app/(main)/websites/WebsitesPage.tsx index 313dccc7..5f1aaae8 100644 --- a/src/app/(main)/websites/WebsitesPage.tsx +++ b/src/app/(main)/websites/WebsitesPage.tsx @@ -14,7 +14,7 @@ export function WebsitesPage() { return ( - + diff --git a/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx b/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx index a4a0c189..723c0249 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx @@ -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); }; diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx index 239193b0..32a9180d 100644 --- a/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx @@ -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({ {({ close }) => ( {name}, + }} + /> + } isLoading={isPending} error={error} onConfirm={handleConfirm.bind(null, close)} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index df432901..7150f43f 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -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 = [] }) { - {row.eventName ? : } + {row.eventName ? : } {formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index c7bd1347..e994831d 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -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]: , - [TYPE_SESSION]: , - [TYPE_EVENT]: , + [TYPE_SESSION]: , + [TYPE_EVENT]: , }; 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: {eventName || formatMessage(labels.unknown)}, - url: ( - - {urlPath} - - ), - }); + return ( + {eventName || formatMessage(labels.unknown)}, + url: ( + + {urlPath} + + ), + }} + /> + ); } if (__type === TYPE_PAGEVIEW) { @@ -104,12 +109,17 @@ export function RealtimeLog({ data }: { data: any }) { } if (__type === TYPE_SESSION) { - return formatMessage(messages.visitorLog, { - country: {countryNames[country] || formatMessage(labels.unknown)}, - browser: {BROWSERS[browser]}, - os: {OS_NAMES[os] || os}, - device: {formatMessage(labels[device] || labels.unknown)}, - }); + return ( + {countryNames[country] || formatMessage(labels.unknown)}, + browser: {BROWSERS[browser]}, + os: {OS_NAMES[os] || os}, + device: {formatMessage(labels[device] || labels.unknown)}, + }} + /> + ); } }; diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx index a588d11a..6cbc24bd 100644 --- a/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx +++ b/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx @@ -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({ {({ close }) => ( {name}, + }} + /> + } isLoading={isPending} error={error} onConfirm={handleConfirm.bind(null, close)} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index d88fff7a..4ef120f7 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -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')} - {eventName ? : } + {eventName ? : } {eventName ? formatMessage(labels.triggeredEvent) diff --git a/src/components/common/PageHeader.tsx b/src/components/common/PageHeader.tsx index 4d0dac5c..a5850f73 100644 --- a/src/components/common/PageHeader.tsx +++ b/src/components/common/PageHeader.tsx @@ -4,6 +4,7 @@ import { Heading, Icon, Row, RowProps, Text, Column } from '@umami/react-zen'; export function PageHeader({ title, description, + label, icon, showBorder = true, children, @@ -11,6 +12,7 @@ export function PageHeader({ }: { title: string; description?: string; + label?: ReactNode; icon?: ReactNode; showBorder?: boolean; allowEdit?: boolean; @@ -26,7 +28,8 @@ export function PageHeader({ width="100%" {...props} > - + + {label} {icon && ( diff --git a/src/components/hooks/useMessages.ts b/src/components/hooks/useMessages.ts index 48b1ba60..42bbeb01 100644 --- a/src/components/hooks/useMessages.ts +++ b/src/components/hooks/useMessages.ts @@ -1,7 +1,22 @@ -import { useIntl } from 'react-intl'; +import { useIntl, FormattedMessage, type MessageDescriptor } from 'react-intl'; import { messages, labels } from '@/components/messages'; -export function useMessages() { +type FormatMessage = ( + descriptor: MessageDescriptor, + values?: Record, + opts?: any, +) => string | null; + +interface UseMessages { + formatMessage: FormatMessage; + messages: typeof messages; + labels: typeof labels; + getMessage: (id: string) => string; + getErrorMessage: (error: unknown) => string | undefined; + FormattedMessage: typeof FormattedMessage; +} + +export function useMessages(): UseMessages { const intl = useIntl(); const getMessage = (id: string) => { @@ -21,15 +36,12 @@ export function useMessages() { }; const formatMessage = ( - descriptor: { - id: string; - defaultMessage: string; - }, - values?: Record, + descriptor: MessageDescriptor, + values?: Record, opts?: any, ) => { return descriptor ? intl.formatMessage(descriptor, values, opts) : null; }; - return { formatMessage, messages, labels, getMessage, getErrorMessage }; + return { formatMessage, messages, labels, getMessage, getErrorMessage, FormattedMessage }; } diff --git a/src/components/icons.ts b/src/components/icons.ts index 0280c31a..bdba7367 100644 --- a/src/components/icons.ts +++ b/src/components/icons.ts @@ -1,17 +1,13 @@ export * from 'lucide-react'; export { Logo as LogoSvg, - Bolt as BoltSvg, - Change as ChangeSvg, Compare as CompareSvg, Funnel as FunnelSvg, - Lightbulb as LightbulbSvg, Lightning as LightningSvg, Location as LocationSvg, Magnet as MagnetSvg, Money as MoneySvg, Network as NetworkSvg, Path as PathSvg, - Tag as TagSvg, Target as TargetSvg, } from '@/components/svg'; diff --git a/src/components/input/WebsiteDateFilter.tsx b/src/components/input/WebsiteDateFilter.tsx index 475fce44..1045de21 100644 --- a/src/components/input/WebsiteDateFilter.tsx +++ b/src/components/input/WebsiteDateFilter.tsx @@ -18,7 +18,7 @@ export function WebsiteDateFilter({ showButtons = true, allowCompare, }: WebsiteDateFilterProps) { - const { dateRange } = useDateRange(websiteId); + const { dateRange, saveDateRange } = useDateRange(websiteId); const { value, endDate } = dateRange; const { formatMessage, labels } = useMessages(); const { @@ -32,6 +32,7 @@ export function WebsiteDateFilter({ const disableForward = value === 'all' || isAfter(endDate, new Date()); const handleChange = (date: string) => { + saveDateRange(date); router.push(updateParams({ date, offset: undefined })); }; diff --git a/src/components/metrics/WeeklyTraffic.tsx b/src/components/metrics/WeeklyTraffic.tsx index ff614739..82d1c1c1 100644 --- a/src/components/metrics/WeeklyTraffic.tsx +++ b/src/components/metrics/WeeklyTraffic.tsx @@ -85,7 +85,7 @@ export function WeeklyTraffic({ websiteId }: { websiteId: string }) { height="16px" borderRadius="full" style={{ margin: '0 auto' }} - role="cell" + role="button" >