Files
zarinpal-analytics-frontend/docs/DASHBOARD-SPEC.md

12 KiB

Merchant Dashboard Capability Spec

  • Status: Approved product specification — implementation planning authorized; implementation not yet authorized
  • Capability: Merchant dashboard
  • Last updated: 2026-08-20

Detailed screen-level behavior, representative layouts, content, accessibility, partial-delivery states, and export workflows are specified in DASHBOARD-UX-SPEC.md. Both specifications, the Phase 2 plan, and the Phase 3 task list are approved. Implementation remains paused pending a separate explicit authorization and approval of the applicable interface contracts.

Objective

Give a non-technical merchant a fast, trustworthy Persian RTL view of payment performance, problems, opportunities, and anonymized peer context, with every claim traceable to deterministic metrics.

Actors

  • Demo merchant user: signs in with assigned username/password and sees one merchant.
  • Merchant user: same MVP permissions; broader roles are out of scope until specified.
  • Judge/operator: can use different seeded merchant accounts but receives no hidden cross-tenant browser privilege.

Functional requirements

  1. Login establishes a secure backend session and returns a generic failure for invalid credentials.
  2. The app shell identifies the active merchant and offers logout.
  3. Overview shows data freshness, selected period, primary outcomes, trend, and prioritized deterministic insights.
  4. Payment performance supports success/failure/retry/timing analysis and traceable drill-downs.
  5. Peer comparison uses other eligible merchants in the active merchant's category for the same selected period and excludes the active merchant. For payment success, verification completion, retry recovery, processing-time p50/p95, and paid fee rate, it shows the merchant-weighted cohort median plus the active merchant's direction-aware performance percentile. Every peer has equal weight regardless of transaction volume. Values appear only when at least 10 other merchants and 1,000 combined peer sessions contribute; otherwise the page explains that there is not enough data. This never hides the merchant's own analytics.
  6. Filters and pagination are reflected in the URL where sharing/reload is useful.
  7. Every screen implements loading, empty, partial-data, stale, unauthorized, forbidden, and recoverable-error behavior as applicable.
  8. Responsive behavior and component contracts follow DESIGN.md.
  9. Merchant-facing dates use the Jalali calendar in Asia/Tehran; API instants remain explicit ISO-8601 UTC and filtering sends unambiguous boundaries.
  10. Approved reporting periods are daily, weekly, Jalali-month, and Persian-season views. Weekly periods run Saturday through Friday in Asia/Tehran. Every comparison states its exact period and baseline.
  11. Paid and Verified both count as successful bank payments. Paid means funds were deducted but merchant verification is incomplete; Verified is finalized. Success and paid-volume metrics include both, while verification-health metrics show them separately.
  12. The first relevant dashboard access sends an idempotent authenticated POST that creates or returns the current analytics run and starts four sections in bounded parallel: overview, payment performance, fees, and peer comparison.
  13. The app then opens the run's authenticated GET SSE stream and renders each section as soon as its ready event arrives. A failed section has its own retry/error state and does not hide successful siblings.
  14. Later equivalent requests reuse 30-day Redis entries keyed by the immutable completed-import dataset snapshot and metric-definition version; cache age alone does not make static data stale.
  15. SSE reconnect uses stable event ids/resume semantics, shows a connection-recovery state, and never exposes job status or analytics for another merchant.
  16. Each section attempt times out after 60 seconds. The backend may perform at most two automatic transient retries using exponential backoff/jitter; the UI represents the section as calculating during automatic retries and exposes failure/retry only after exhaustion or a non-retryable error.
  17. SSE expects a heartbeat every 15 seconds and supports authorized replay for one hour. After replay expires, the app fetches the current run snapshot before reopening the stream.
  18. Manual retry targets only one exhausted failed section. It is disabled while active, never restarts ready siblings, and handles a typed 429 by honoring Retry-After.
  19. Analytics starts, manual retries, and concurrent SSE connections expose accessible rate-limited states without discarding already-ready content or retrying in a tight loop.
  20. Paid amount and fee totals count each eligible session once. Merchant fees are shown as real Rial amounts/rates; retry attempts cannot multiply a session fee.
  21. Retry recovery may finish in either Paid or Verified. Timing cards show p50, p95, sample size, and non-null coverage.
  22. Authentication uses a backend-owned server-side session. The frontend receives only a Secure, HttpOnly, SameSite=Lax cookie and never reads or stores an authentication token.
  23. Expired, revoked, logged-out, or disabled-account sessions stop dashboard and SSE access immediately and return the user to login without exposing internal authentication details.
  24. The demo login handles typed throttling with Retry-After, never reveals whether a username exists, and does not implement password recovery, registration, or SSO. ZarinPal SSO replaces the demo credential screen after product acceptance.
  25. First dashboard access defaults to the latest complete Jalali month containing data. The comparison is the immediately previous equivalent complete month; if unavailable/empty, the UI explicitly says comparison is unavailable and does not silently substitute another baseline.
  26. Overview KPI order is bank-paid amount, bank-paid session count, payment success rate, then verification completion. Retry, processing-time, and fee analysis follow below.
  27. Read-only drill-downs are available for unverified-paid, failed, and retried sessions. They exclude payer tokens and peer-level data and may distinguish zero-attempt/reached-bank stages without inventing failure causes.
  28. A deterministic insight is promoted into an actionable card only when at least 100 relevant sessions contribute and the comparison changes by at least 3 percentage points for a rate or 20 percent for an amount/count. Smaller changes remain available as metrics without an action claim.
  29. All merchant-facing values use Persian digits and monetary values use Rial only. Technical identifiers remain canonical and are directionally isolated.
  30. Light and dark modes are supported from the first release through the semantic token contract in DESIGN.md; system preference is the default and an explicit accessible user choice persists locally.
  31. Merchant-owned detail views that support operational handoff provide CSV export using the current authorized filters. Export never contains payer tokens, peer-level data, credentials, or fields absent from the corresponding authorized detail view.

Frontend architecture baseline

  • SvelteKit 2 + Svelte 5 runes + strict TypeScript + Tailwind CSS v4.
  • Static/client-rendered application behind Nginx, consuming the Go API under the same origin where practical.
  • Route groups separate authentication and authenticated application layouts.
  • Server state belongs to a typed API layer; local UI state uses the smallest appropriate Svelte primitive/context.
  • No SvelteKit server endpoints, database access, or private secrets in the frontend baseline.
  • API types should be generated from the approved interface description.

Acceptance criteria

  • A user can authenticate as at least two distinct seeded merchants and each dashboard shows different, correctly isolated data.
  • Direct URL/API manipulation cannot expose another merchant's details.
  • Critical flows work at 360 px, 768 px, 1024 px, and a wide desktop viewport in RTL.
  • Login, navigation drawer, filters, chart alternatives, and logout are keyboard complete and screen-reader understandable.
  • Values visibly specify period and unit; missing/suppressed/zero states are distinct.
  • Merchant-facing digits are Persian, money is labeled in Rial only, and technical identifiers remain uncorrupted in RTL layouts.
  • Light and dark modes both meet the documented contrast, focus, chart, state, and reduced-motion requirements at all acceptance viewports, with no first-paint theme flash.
  • Below-threshold comparisons never produce actionable insight language, while qualifying insight cards expose their numeric evidence and limitation.
  • Authorized CSV exports reconcile to the same filtered detail query shown in the dashboard, retain explicit Rial/date/status semantics, and remain tenant-isolated even when export parameters are manipulated.
  • Success views include Paid plus Verified, verification-completion views separate them, and unverified paid sessions are never styled or described as failures.
  • Paid volume and merchant fee values reconcile at session grain; fee totals remain unchanged when a session has additional attempts.
  • Retry recovery includes eventual Paid or Verified, and timing percentiles never treat missing values as zero.
  • Peer benchmarks use only the same category and selected period, exclude the active merchant, and give every eligible peer merchant one equal-weight metric value.
  • Peer cards show the cohort median and a percentile where higher always means better; processing-time and fee-rate ranks are direction-adjusted because lower values are better.
  • A high-volume peer cannot dominate the cohort median, and under-sized cohorts produce the typed insufficient-data state without hiding merchant-owned analytics.
  • All core sections begin on first access and become visible independently in completion order without blocking the whole dashboard.
  • The start request is idempotent and always maps equivalent concurrent starts to the same current run.
  • A second equivalent access reuses current Redis entries, and concurrent first accesses do not enqueue duplicate equivalent section jobs.
  • One section failure is isolated, retryable, and accessible; already-ready sections remain usable.
  • A section does not remain indefinitely loading: each attempt is bounded to 60 seconds and no more than two automatic exponential-backoff retries occur.
  • Disconnecting and reconnecting the SSE stream resumes safely without cross-merchant data or duplicated final sections.
  • Missing heartbeats trigger controlled connection recovery; one-hour replay or snapshot fallback restores the authorized current state.
  • Rate-limited responses enqueue no duplicate work, preserve ready sections, and delay retry according to Retry-After.
  • Daily, weekly, Jalali-month, and Persian-season filters use the same explicit boundaries as the API and database.
  • The initial static-demo view selects the latest complete Jalali month with data and uses only the immediately previous equivalent period as its baseline.
  • Overview KPI order and read-only unverified/failed/retried drill-downs match the approved analytics contract without exposing payer tokens or peers.
  • Production build passes type checking, linting, unit/component tests, and Playwright critical-flow tests.
  • No raw dataset record, payer-card key, credential, or AvalAI key enters client bundles/logging/storage.

Contract and implementation dependencies

Resolved specification inputs:

  • Backend ANALYTICS-SPEC.md is approved, and the definitions in METRIC-CATALOGUE.md version 0.4.0 are authoritative.
  • Backend IDENTITY-SPEC.md is approved for demo authentication, sessions, and tenant isolation.
  • DESIGN.md version 0.3.3 approves the Git-tracked static/fonts/IRANYekanXVFaNumVF.woff2 asset and adjacent static/fonts/FontLicense.txt.

Resolved implementation contracts:

  • Backend docs/openapi.yaml version 1.0.0 is the approved machine-readable analytics, identity, detail, SSE, error, and CSV export interface contract.
  • CSV uses the approved Persian headers, UTF-8 BOM, filename policy, 100,000-row and 50 MiB caps, synchronous 60-second limit, and rate limit. Analytics/detail/export audit retention is 90 days; minimal demo authentication audit retention is 30 days.
  • Static adapter output is served with history fallback only for frontend routes; /api/v1 and other reserved operational paths never fall through to index.html.
  • Separate explicit authorization to begin Phase 4 implementation.