fix: scope page insights to visible periods
This commit is contained in:
4
src/lib/api/schema.d.ts
vendored
4
src/lib/api/schema.d.ts
vendored
@@ -1076,7 +1076,7 @@ export interface components {
|
||||
};
|
||||
AIInsightRefreshRequest: {
|
||||
terminalId?: string;
|
||||
/** @description Used only by the transactions module. */
|
||||
/** @description Server-offered reporting period used to match the visible analytics run. */
|
||||
periodId?: string;
|
||||
};
|
||||
AIInsightJob: {
|
||||
@@ -2469,7 +2469,7 @@ export interface operations {
|
||||
parameters: {
|
||||
query?: {
|
||||
terminalId?: string;
|
||||
/** @description Used only by the transactions module to match the selected reporting period. */
|
||||
/** @description Server-offered reporting period used to match the visible analytics run. */
|
||||
periodId?: string;
|
||||
};
|
||||
header?: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen, waitFor } from '@testing-library/svelte';
|
||||
import { cleanup, render, screen, waitFor } from '@testing-library/svelte';
|
||||
import { tick } from 'svelte';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { AIInsight } from '$lib/api/client';
|
||||
import AIInsightPanel from './AIInsightPanel.svelte';
|
||||
@@ -57,6 +57,8 @@ const insight: AIInsight = {
|
||||
generatedAt: '2026-08-22T00:00:00Z',
|
||||
};
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
describe('AIInsightPanel', () => {
|
||||
it('leaves the skeleton after loading an insight and does not fetch it again', async () => {
|
||||
latestAIInsight.mockReset();
|
||||
@@ -64,24 +66,33 @@ describe('AIInsightPanel', () => {
|
||||
|
||||
render(AIInsightPanel, { module: 'overview' });
|
||||
|
||||
await screen.findByText('فروش در این بازه رشد کرده است.');
|
||||
await screen.findByText('آخرین روز ثبتشده باثبات بوده است.');
|
||||
await tick();
|
||||
|
||||
await waitFor(() => expect(screen.queryByLabelText('در حال دریافت تحلیل هوشمند')).toBeNull());
|
||||
expect(latestAIInsight).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByText('فروش در این بازه رشد کرده است.')).not.toBeNull();
|
||||
expect(screen.queryByText('روند کوتاهمدت باثبات پیشبینی میشود.')).toBeNull();
|
||||
expect(screen.queryByText('یافتهها')).toBeNull();
|
||||
});
|
||||
|
||||
it('loads a transaction insight with the resolved period scope', async () => {
|
||||
it('loads every page insight with the resolved period scope', async () => {
|
||||
latestAIInsight.mockReset();
|
||||
latestAIInsight.mockResolvedValueOnce({
|
||||
...insight,
|
||||
module: 'transactions',
|
||||
module: 'overview',
|
||||
scope: { terminalId: 'T83', periodId: 'period-7' },
|
||||
});
|
||||
|
||||
render(AIInsightPanel, { module: 'transactions', terminalId: 'T83', periodId: 'period-7' });
|
||||
render(AIInsightPanel, {
|
||||
module: 'overview',
|
||||
terminalId: 'T83',
|
||||
periodId: 'period-7',
|
||||
periodLabel: 'مرداد ۱۴۰۵',
|
||||
});
|
||||
|
||||
await screen.findByText('فروش در این بازه رشد کرده است.');
|
||||
expect(latestAIInsight).toHaveBeenCalledWith('transactions', 'T83', 'period-7');
|
||||
await screen.findByText('آخرین روز ثبتشده باثبات بوده است.');
|
||||
expect(screen.getByText('مرداد ۱۴۰۵')).not.toBeNull();
|
||||
expect(latestAIInsight).toHaveBeenCalledWith('overview', 'T83', 'period-7');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,8 +120,6 @@
|
||||
|
||||
<PageHeader title={titles[kind][0]} description={titles[kind][1]} icon={icons[kind]} />
|
||||
|
||||
<AIInsightPanel module={kind} terminalId={filters.terminal === 'all' ? undefined : filters.terminal} />
|
||||
|
||||
{#if loading}
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4" role="status" aria-label="در حال محاسبه گزارش">
|
||||
{#each Array(4) as _}<Skeleton class="h-40 rounded-lg" />{/each}
|
||||
@@ -140,6 +138,13 @@
|
||||
{@const fees = run.sections.fees}
|
||||
{@const peers = run.sections.peerComparison}
|
||||
|
||||
<AIInsightPanel
|
||||
module={kind}
|
||||
terminalId={filters.terminal === 'all' ? undefined : filters.terminal}
|
||||
periodId={run.period.periodId}
|
||||
periodLabel={run.period.jalaliLabelFa}
|
||||
/>
|
||||
|
||||
{#if kind === 'overview' && overview.state === 'READY'}
|
||||
<div class="kpi-grid grid gap-3 sm:gap-4">
|
||||
<KpiCard
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { useDashboardContext } from '$lib/dashboard-context.svelte';
|
||||
import { parseDashboardFilters } from '$lib/dashboard-filters';
|
||||
import { formatPercent, formatValue } from '$lib/format';
|
||||
import AIInsightPanel from './AIInsightPanel.svelte';
|
||||
import KpiCard from './KpiCard.svelte';
|
||||
import PageHeader from './PageHeader.svelte';
|
||||
import TransactionStatusChart from './TransactionStatusChart.svelte';
|
||||
@@ -79,6 +80,12 @@
|
||||
</Card>
|
||||
{:else if run}
|
||||
{@const transactions = run.sections.transactions}
|
||||
<AIInsightPanel
|
||||
module="transactions"
|
||||
terminalId={filters.terminal === 'all' ? undefined : filters.terminal}
|
||||
periodId={run.period.periodId}
|
||||
periodLabel={run.period.jalaliLabelFa}
|
||||
/>
|
||||
{#if transactions.state === 'READY'}
|
||||
<div class="kpi-grid grid gap-3 sm:gap-4">
|
||||
<KpiCard
|
||||
@@ -146,8 +153,8 @@
|
||||
><ChartColumnBigIcon class="text-primary size-5" /> روند ترکیب وضعیتها</CardTitle
|
||||
>
|
||||
<p class="text-muted-foreground mt-2 max-w-2xl text-xs leading-6">
|
||||
هر خط، تغییر تعداد یک وضعیت را در طول زمان نشان میدهد؛ جزئیات تعداد و سهم هر نقطه با
|
||||
تمرکز صفحهکلید یا اشارهگر در دسترس است.
|
||||
هر خط، تغییر تعداد یک وضعیت را در طول زمان نشان میدهد؛ جزئیات تعداد و سهم هر نقطه با تمرکز
|
||||
صفحهکلید یا اشارهگر در دسترس است.
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="secondary" class="rounded-md">{run.period.jalaliLabelFa}</Badge>
|
||||
|
||||
Reference in New Issue
Block a user