mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Use FormattedMessage. Updated icons. Fixed bugs.
This commit is contained in:
parent
3afe843461
commit
83a014e884
20 changed files with 129 additions and 84 deletions
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
<Column>
|
||||
<Column gap="2">
|
||||
{label}
|
||||
<Row alignItems="center" gap="3">
|
||||
{icon && (
|
||||
<Icon size="md" color="muted">
|
||||
|
|
|
|||
|
|
@ -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<string, string | number | boolean | null | undefined>,
|
||||
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<string, string>,
|
||||
descriptor: MessageDescriptor,
|
||||
values?: Record<string, string | number | boolean | null | undefined>,
|
||||
opts?: any,
|
||||
) => {
|
||||
return descriptor ? intl.formatMessage(descriptor, values, opts) : null;
|
||||
};
|
||||
|
||||
return { formatMessage, messages, labels, getMessage, getErrorMessage };
|
||||
return { formatMessage, messages, labels, getMessage, getErrorMessage, FormattedMessage };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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 }));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export function WeeklyTraffic({ websiteId }: { websiteId: string }) {
|
|||
height="16px"
|
||||
borderRadius="full"
|
||||
style={{ margin: '0 auto' }}
|
||||
role="cell"
|
||||
role="button"
|
||||
>
|
||||
<Row
|
||||
backgroundColor="primary"
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export async function getQueryFilters(
|
|||
...dateRange,
|
||||
...filters,
|
||||
page: params?.page,
|
||||
pageSize: params?.page ? params?.pageSize || DEFAULT_PAGE_SIZE : undefined,
|
||||
pageSize: params?.pageSize ? params?.pageSize || DEFAULT_PAGE_SIZE : undefined,
|
||||
orderBy: params?.orderBy,
|
||||
sortDescending: params?.sortDescending,
|
||||
search: params?.search,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue