From 8ebaf6c31dc642a6287461754a5bd70ab17f1d60 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Mon, 12 Jan 2026 11:03:55 +0100 Subject: [PATCH] Refine analytics page and i18n --- .../js/admin/i18n/locales/de/management.json | 2 ++ .../js/admin/i18n/locales/en/management.json | 2 ++ resources/js/admin/mobile/EventAnalyticsPage.tsx | 11 ++++++----- .../js/admin/mobile/__tests__/analytics.test.ts | 16 ++++++++++++++++ resources/js/admin/mobile/lib/analytics.ts | 7 +++++++ 5 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 resources/js/admin/mobile/__tests__/analytics.test.ts create mode 100644 resources/js/admin/mobile/lib/analytics.ts diff --git a/resources/js/admin/i18n/locales/de/management.json b/resources/js/admin/i18n/locales/de/management.json index 85f082d..a64ac60 100644 --- a/resources/js/admin/i18n/locales/de/management.json +++ b/resources/js/admin/i18n/locales/de/management.json @@ -176,6 +176,8 @@ }, "common": { "all": "Alle", + "anonymous": "Anonym", + "error": "Etwas ist schiefgelaufen", "loadMore": "Mehr laden", "processing": "Verarbeite …", "select": "Auswählen", diff --git a/resources/js/admin/i18n/locales/en/management.json b/resources/js/admin/i18n/locales/en/management.json index 5ce0f21..69827a8 100644 --- a/resources/js/admin/i18n/locales/en/management.json +++ b/resources/js/admin/i18n/locales/en/management.json @@ -172,6 +172,8 @@ }, "common": { "all": "All", + "anonymous": "Anonymous", + "error": "Something went wrong", "loadMore": "Load more", "processing": "Processing…", "select": "Select", diff --git a/resources/js/admin/mobile/EventAnalyticsPage.tsx b/resources/js/admin/mobile/EventAnalyticsPage.tsx index 00c3694..d95acc7 100644 --- a/resources/js/admin/mobile/EventAnalyticsPage.tsx +++ b/resources/js/admin/mobile/EventAnalyticsPage.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; -import { BarChart2, TrendingUp, Users, ListTodo, Lock, Trophy, Calendar } from 'lucide-react'; +import { TrendingUp, ListTodo, Lock, Trophy } from 'lucide-react'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { format, parseISO } from 'date-fns'; @@ -13,6 +13,7 @@ import { MobileCard, CTAButton, SkeletonCard } from './components/Primitives'; import { getEventAnalytics, EventAnalytics } from '../api'; import { ApiError } from '../lib/apiError'; import { useAdminTheme } from './theme'; +import { resolveMaxCount } from './lib/analytics'; import { adminPath } from '../constants'; export default function MobileEventAnalyticsPage() { @@ -99,7 +100,8 @@ export default function MobileEventAnalyticsPage() { const hasTasks = tasks.length > 0; // Prepare chart data - const maxCount = Math.max(...timeline.map((p) => p.count), 1); + const maxTimelineCount = resolveMaxCount(timeline.map((point) => point.count)); + const maxTaskCount = resolveMaxCount(tasks.map((task) => task.count)); return ( {timeline.map((point, index) => { - const heightPercent = (point.count / maxCount) * 100; + const heightPercent = (point.count / maxTimelineCount) * 100; const date = parseISO(point.timestamp); // Show label every 3rd point or if few points const showLabel = timeline.length < 8 || index % 3 === 0; @@ -138,7 +140,7 @@ export default function MobileEventAnalyticsPage() { /> {showLabel && ( - {format(date, 'HH:mm')} + {format(date, 'HH:mm', { locale: dateLocale })} )} @@ -212,7 +214,6 @@ export default function MobileEventAnalyticsPage() { {hasTasks ? ( {tasks.map((task) => { - const maxTaskCount = Math.max(...tasks.map(t => t.count), 1); const percent = (task.count / maxTaskCount) * 100; return ( diff --git a/resources/js/admin/mobile/__tests__/analytics.test.ts b/resources/js/admin/mobile/__tests__/analytics.test.ts new file mode 100644 index 0000000..e332270 --- /dev/null +++ b/resources/js/admin/mobile/__tests__/analytics.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { resolveMaxCount } from '../lib/analytics'; + +describe('resolveMaxCount', () => { + it('defaults to 1 for empty input', () => { + expect(resolveMaxCount([])).toBe(1); + }); + + it('returns the highest count', () => { + expect(resolveMaxCount([2, 5, 3])).toBe(5); + }); + + it('never returns less than 1', () => { + expect(resolveMaxCount([0])).toBe(1); + }); +}); diff --git a/resources/js/admin/mobile/lib/analytics.ts b/resources/js/admin/mobile/lib/analytics.ts new file mode 100644 index 0000000..cf2e9b4 --- /dev/null +++ b/resources/js/admin/mobile/lib/analytics.ts @@ -0,0 +1,7 @@ +export function resolveMaxCount(values: number[]): number { + if (!Array.isArray(values) || values.length === 0) { + return 1; + } + + return Math.max(...values, 1); +}