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

7.2 KiB

Merchant Dashboard Capability Spec

  • Status: Draft — not approved for implementation
  • Capability: Merchant dashboard
  • Last updated: 2026-08-20

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 excludes the active merchant and exposes only eligible anonymized cohort aggregates. It displays a value only when at least 10 other merchants and 1,000 combined peer sessions contribute; otherwise it 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. PostgreSQL merchant/cohort revision changes select new cache keys and cause only missing sections to be recalculated; 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.

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.
  • 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.
  • 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.
  • 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.

Unresolved dependencies

  • Approved analytics metric catalogue and cohort suppression policy
  • Approved authentication/session API
  • OpenAPI/interface contract and error format
  • Decisions listed in DESIGN.md
  • Static adapter fallback/routing behavior under the final Nginx configuration