From 5616a330cda9d0bf6a2fbf199d44978a13b6d1f5 Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Fri, 21 Aug 2026 16:28:13 +0800 Subject: [PATCH] feat: show hourly labels for daily charts --- src/lib/api/schema.d.ts | 2 ++ src/lib/components/AnalyticsPage.svelte | 8 +++-- src/lib/components/PaymentHistoryChart.svelte | 32 ++++++++++++++++--- .../components/PaymentHistoryChart.test.ts | 32 +++++++++++++++++++ 4 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 src/lib/components/PaymentHistoryChart.test.ts diff --git a/src/lib/api/schema.d.ts b/src/lib/api/schema.d.ts index 9ae13cc..69d871b 100644 --- a/src/lib/api/schema.d.ts +++ b/src/lib/api/schema.d.ts @@ -723,6 +723,8 @@ export interface components { PaymentHistoryPoint: { /** Format: date */ localDate: string; + /** @description Tehran-local hour bucket, present only for DAY periods. */ + localHour?: number; /** Format: int64 */ totalSessions: number; /** Format: int64 */ diff --git a/src/lib/components/AnalyticsPage.svelte b/src/lib/components/AnalyticsPage.svelte index 9387fff..532c276 100644 --- a/src/lib/components/AnalyticsPage.svelte +++ b/src/lib/components/AnalyticsPage.svelte @@ -177,14 +177,18 @@ > روند پرداخت‌های موفق

- برای دیدن مبلغ و تعداد هر روز، روی نقطه‌های نمودار بروید. + برای دیدن مبلغ و تعداد هر {run.period.periodType === 'DAY' ? 'ساعت' : 'روز'}، روی نقطه‌های + نمودار بروید.

{run.period.jalaliLabelFa} diff --git a/src/lib/components/PaymentHistoryChart.svelte b/src/lib/components/PaymentHistoryChart.svelte index 67a3292..eabf6e4 100644 --- a/src/lib/components/PaymentHistoryChart.svelte +++ b/src/lib/components/PaymentHistoryChart.svelte @@ -5,7 +5,8 @@ import { formatMoney, formatValue } from '$lib/format'; type Point = components['schemas']['PaymentHistoryPoint']; - let { data }: { data: Point[] } = $props(); + type PeriodType = components['schemas']['PeriodType']; + let { data, periodType }: { data: Point[]; periodType: PeriodType } = $props(); let active = $state(null); const width = 760; @@ -40,6 +41,27 @@ ); } + function persianHour(value: number) { + const hour = new Intl.NumberFormat('fa-IR', { + minimumIntegerDigits: 2, + maximumFractionDigits: 0, + useGrouping: false, + }).format(value); + return `${hour}:۰۰`; + } + + function axisLabel(point: Point) { + return periodType === 'DAY' && point.localHour !== undefined + ? persianHour(point.localHour) + : persianDate(point.localDate); + } + + function fullPointLabel(point: Point) { + return periodType === 'DAY' && point.localHour !== undefined + ? `${persianDate(point.localDate)}، ساعت ${persianHour(point.localHour)}` + : persianDate(point.localDate); + } + function tooltipPosition(point: (typeof positions)[number]) { const horizontalPercent = (point.x / width) * 100; const verticalPercent = (point.y / height) * 100; @@ -104,7 +126,7 @@ fill="var(--muted-foreground)" font-size="9" text-anchor="middle" - direction="rtl">{persianDate(tick.localDate)}{axisLabel(tick)} {/each} تاریخ{periodType === 'DAY' ? 'ساعت' : 'تاریخ'} (active = index)} onmouseleave={() => (active = null)} onfocus={() => (active = index)} @@ -165,7 +187,7 @@ class="rounded-md bg-zinc-950 p-3 text-right text-xs leading-6 text-white shadow-xl" transition:scale={{ duration: 150, start: 0.96 }} > - {persianDate(positions[active].localDate)} + {fullPointLabel(positions[active])} {formatValue(positions[active].bankPaidSessions)} پرداخت موفق diff --git a/src/lib/components/PaymentHistoryChart.test.ts b/src/lib/components/PaymentHistoryChart.test.ts new file mode 100644 index 0000000..2f6037a --- /dev/null +++ b/src/lib/components/PaymentHistoryChart.test.ts @@ -0,0 +1,32 @@ +import { render, screen } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; + +import PaymentHistoryChart from './PaymentHistoryChart.svelte'; + +describe('PaymentHistoryChart', () => { + it('uses Persian hour labels for a one-day period', () => { + render(PaymentHistoryChart, { + periodType: 'DAY', + data: [ + { + localDate: '2026-07-01', + localHour: 0, + totalSessions: 10, + bankPaidSessions: 8, + bankPaidAmountRial: 800_000, + }, + { + localDate: '2026-07-01', + localHour: 12, + totalSessions: 14, + bankPaidSessions: 11, + bankPaidAmountRial: 1_100_000, + }, + ], + }); + + expect(screen.getByText('۰۰:۰۰')).not.toBeNull(); + expect(screen.getByText('۱۲:۰۰')).not.toBeNull(); + expect(screen.getByText('ساعت')).not.toBeNull(); + }); +});