107 lines
12 KiB
Markdown
107 lines
12 KiB
Markdown
# 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`](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.
|