31 KiB
Phase 2 Implementation Plan — Merchant Analytics Dashboard
- Status: Approved — Phase 4 implementation authorized 2026-08-20
- SDD phase: 4 — Plan and task list approved; implementation authorized
- Capability: Merchant dashboard
- Implementation authorization: Granted by user on 2026-08-20
- Approved task list:
tasks/todo.md - Last updated: 2026-08-20
1. Outcome
Build the approved Persian RTL merchant analytics dashboard as a statically built SvelteKit application behind Nginx. The browser authenticates with the backend-owned server session, consumes the same-origin merchant API, renders the four analytics sections independently through snapshot/SSE delivery, provides safe merchant-owned detail views and CSV export, and meets the approved responsive, theme, privacy, and WCAG 2.2 AA contracts.
Implementation is authorized through the approved Phase 3 task list. Each work package remains constrained by its acceptance criteria, verification, dependencies, and approximately five-file limit.
2. Approved inputs
| Input | Status | Planning use |
|---|---|---|
docs/DASHBOARD-SPEC.md |
Approved for planning | Product scope and acceptance criteria |
docs/DASHBOARD-UX-SPEC.md |
Approved for planning | Screen, state, content, responsive, export, and accessibility behavior |
DESIGN.md 0.3.2 |
Approved baseline | Tokens, themes, FaNum font, RTL, component contracts, and motion |
Backend IDENTITY-SPEC.md |
Approved | Session, login, logout, CSRF, throttle, and tenant boundary |
Backend ANALYTICS-SPEC.md |
Approved | Metric lifecycle, partial delivery, retries, thresholds, and privacy |
Backend METRIC-CATALOGUE.md 0.4.0 |
Approved definitions | Metric semantics and formatting evidence |
Backend API-SPEC.md |
Approved | REST/SSE/CSV behavior and provider boundary |
Backend docs/openapi.yaml 1.0.0 |
Approved | Generated types and operation contracts |
| Nginx/static routing contract | Approved | Production/static-host verification contract |
3. Planning assumptions
- The user's “okay” approves the product and UX specifications for implementation planning, not implementation itself.
- The dashboard remains one frontend capability; authentication, analytics calculation, and deployment stay provider capabilities in their existing repositories.
- The frontend uses SvelteKit 2, Svelte 5 runes, strict TypeScript, Tailwind CSS v4 through the Vite plugin, and
@sveltejs/adapter-staticwith an Nginx-compatible fallback. - The application is authenticated and client-rendered; it has no SEO requirement, database access, SvelteKit server endpoints, browser-held credentials, or private server secrets.
- API DTOs are generated from an approved OpenAPI artifact. The frontend may add view models but does not hand-copy transport schemas.
- The same origin serves the static panel and
/api/v1; the server session cookie remains unreadable to JavaScript while the approved CSRF value from the session summary remains in memory only. - Exact dependency versions and the package-manager lockfile are selected and committed in the scaffold task after verifying current official documentation.
- No work package depending on the API begins until
API-SPEC.mdandopenapi.yamllose draft status or an explicitly approved version is identified.
If any assumption is rejected, revise this plan before generating Phase 3 tasks.
4. Entry gates
Implementation cannot start until all items in Gate A are approved. Foundation work cannot be called complete until Gate B passes.
Gate A — contract approval
- Backend OpenAPI
1.0.0is approved; implementation Task 1 records its source commit/checksum before generation. - CSV limits/encoding, audit retention, terminal-reference presentation, and the production Problem Details URI namespace are approved.
- UX export states are reconciled with synchronous CSV delivery and error handling.
- Nginx serves history fallback only for frontend routes;
/api/v1and reserved operational paths never fall through toindex.html. - Separate explicit instruction to begin implementation received on 2026-08-20.
merchant.displayNamefromAuthSessionis the shell/export display authority.- Visible semantic chart-data tables are sufficient; chart-data download is not a separate MVP export.
Gate B — scaffold verification
- Exact Node/npm and package versions are pinned in committed manifests/lockfile.
- Static build, deep-link refresh, global CSS, tracked font loading, and both themes work in the target Nginx topology.
- Formatting, lint,
svelte-check, unit/component test, Playwright, and production-build commands exist and pass on the empty shell. - OpenAPI validation/type generation is reproducible and fails CI on drift.
5. Architecture
5.1 Runtime boundary
Browser
├─ static SvelteKit application
├─ presentation-only local theme choice
├─ in-memory session summary / CSRF value
└─ generated typed API client
│ same-origin cookie requests
▼
Nginx
├─ static files + SPA fallback
└─ /api/v1 proxy
▼
Go merchant API
├─ server session / tenant authority
├─ periods, run snapshots, SSE, retry
├─ merchant-owned detail JSON / CSV
└─ approved aggregate peer results only
The frontend never accepts a merchant id as authorization input. merchantId may be rendered only as an opaque technical value if the approved screen needs it; it never selects scope.
5.2 Proposed source structure
The final scaffold may adjust filenames, but responsibility boundaries remain:
src/
app.css global Tailwind import, semantic tokens, font face
app.html Persian language/direction and pre-paint theme hook
lib/
api/
generated/ generated OpenAPI transport types only
client.ts same-origin request, Problem Details, CSRF policy
analytics-stream.ts approved SSE transport/recovery adapter
components/
ui/ small semantic primitives
layout/ app shell, header, sidebar/drawer, status region
analytics/ shared metric/chart/table/state presentation
auth/ login/session presentation
details/ merchant-owned detail and export presentation
features/
auth/ session orchestration
periods/ allowlisted period URL state
analytics-run/ run/section state reducer and event reconciliation
overview/
payment-performance/
fees/
peer-comparison/
session-details/
export/
format/ Persian digits, Jalali/Tehran, Rial, percent, bidi
testing/ contract-safe fixtures/builders
routes/
(auth)/login/
(app)/dashboard/
(app)/payments/
(app)/payments/unverified/
(app)/payments/failed/
(app)/payments/retried/
(app)/fees/
(app)/peers/
(app)/methodology/
(app)/account/
tests/
e2e/ critical user and recovery flows
static/fonts/ tracked approved font and license
Route groups share authentication/layout requirements without changing public URLs. Pages orchestrate; feature modules own business-facing state; reusable components remain presentation-focused.
5.3 State ownership
| State | Owner | Persistence |
|---|---|---|
| Server session and merchant scope | Backend | Secure server session; cookie unreadable to frontend |
| Session summary and CSRF value | Auth feature/context | Memory only; cleared on logout/401 |
| Theme choice | Theme service | Local presentation-only preference |
| Period type/id and detail filters/cursor | URL | Query parameters using server-allowlisted values |
| Analytics run and section states | Analytics-run feature | Memory; recover from run snapshot/SSE replay |
| Last SSE event id/liveness | Stream adapter | Memory for the current authorized run only |
| Form/dialog/drawer state | Owning component | Local Svelte rune state |
| Generated transport types | Build output/source | Regenerated from approved OpenAPI; never hand-edited |
Ready section content remains immutable for a run dataset snapshot. Event reconciliation is idempotent by event and section identity. A sibling event cannot erase a valid ready section.
5.4 API and error handling
- One generated client boundary maps successful transport schemas and RFC 9457
Problemresponses. - UI decisions use stable
code, HTTP status, and allowlisted field reasons; they never parse backendtitleordetailprose. 401 UNAUTHENTICATEDclears authorized UI state and returns to login with the approved generic message.404 RESOURCE_NOT_FOUNDdoes not distinguish absent from cross-tenant resources.429/retryable503honorRetry-After; no tight retry loop exists.- Mutations add the in-memory session-bound CSRF token and same-origin evidence through one policy boundary.
- Decimal strings remain exact until formatting; money/counts remain integers and are never converted to Toman.
5.5 SSE transport decision spike
Resolve before the analytics UI slice:
- Test native
EventSourcewith same-origin cookies, named heartbeat events, browser-managedLast-Event-ID, session termination, and server replay/reset behavior. - Verify whether native error opacity prevents typed handling of
429 CONNECTION_LIMIT_REACHED,Retry-After, and non-SSE Problem responses. - Compare a small fetch-stream adapter only if required for approved status/header handling; do not introduce an SSE library without evidence.
- Select one transport and document reconnect ownership, heartbeat watchdog, backoff, cleanup, and duplicate-event handling.
The selected approach must preserve ready sections and must not create parallel reconnect loops on top of browser behavior.
5.6 CSV delivery decision spike
The draft API proposes synchronous CSV up to 100,000 rows/50 MiB. Before implementation:
- Compare direct browser navigation/download with authenticated
fetchplus Blob/object URL. - Verify handling of Problem Details before bytes,
413 EXPORT_TOO_LARGE,429 Retry-After,504 EXPORT_TIMEOUT, interrupted streams, and session expiry. - Measure memory and mobile behavior at the proposed cap.
- Amend UX copy/state if synchronous delivery cannot truthfully expose separate “generating” and “ready” states.
Do not assemble CSV from paginated browser data. The export always comes from the authorized server endpoint.
5.7 Charts
Start with semantic HTML/SVG and visible table alternatives for the limited approved chart set. A chart dependency is added only if an early spike demonstrates a material accessibility, RTL, interaction, or delivery advantage over focused internal chart components.
Any option must support:
- Direct labels, keyboard-equivalent values, Persian digits, Rial/percent units, and a semantic table.
- Stable SSR/static build behavior, though analytics content is client-delivered.
- Semantic light/dark tokens, non-color cues, and reduced motion.
- No peer rows, hidden payer data, 3D charts, or inaccessible canvas-only output.
6. Dependency graph and build order
Approved API/OpenAPI + Nginx decisions
│
├─ scaffold + quality commands
│ ├─ design/theme/font foundation
│ ├─ generated types + API client
│ └─ contract-safe fixture/test harness
│
├─ authentication/session vertical slice
│ └─ authenticated app shell
│
├─ reporting periods + URL state
│ └─ analytics start/snapshot
│ └─ SSE spike + run state model
│ ├─ overview slice
│ ├─ payment-performance slice
│ ├─ fees slice
│ └─ peer-comparison slice
│ └─ full partial-delivery recovery checkpoint
│
├─ shared detail query/table foundation
│ ├─ unverified-paid slice
│ ├─ failed slice
│ └─ retried slice
│ └─ CSV spike + export slice
│
└─ methodology/account completion
└─ responsive/theme/accessibility/security hardening
└─ static production acceptance
Foundation dependencies are sequential. Once the run state and fixture contracts are stable, independent section presentations can be developed concurrently in isolated feature files, but integration and accessibility checkpoints remain shared gates.
7. Planned work packages
These are Phase 2 packages, not yet implementable Phase 3 tasks.
Phase I — contracts and foundation
| ID | Work package | Depends on | Result |
|---|---|---|---|
| P01 | Freeze API/OpenAPI and product limits | Gate A decisions | Approved versioned transport contract |
| P02 | Verify static-SPA/Nginx topology | P01, deployment decision | Deep links, /api/v1, assets, cache rules proven |
| P03 | Scaffold pinned SvelteKit/Svelte/Tailwind project | P02 | Minimal strict project and lockfile |
| P04 | Establish quality/test commands | P03 | Format, lint, check, unit, E2E, build scripts |
| P05 | Generate and drift-check API types | P01, P03 | Reproducible generated contract types |
| P06 | Implement design/theme/font foundation | P03 | RTL root, semantic themes, no-flash choice, tracked font |
| P07 | Establish contract-safe fixtures | P05 | Sensitive-free builders for all states/errors/events |
Phase II — identity and reporting context
| ID | Work package | Depends on | Result |
|---|---|---|---|
| P08 | Deliver login/session/logout slice | P04–P07 | Generic/throttled login and revocable session flow |
| P09 | Deliver authenticated shell | P08 | RTL sidebar/drawer, merchant identity, theme, route protection |
| P10 | Deliver Persian formatters | P04, P06 | Digits, Rial, Jalali/Tehran, percent, bidi isolation |
| P11 | Deliver periods and URL filter slice | P05, P08, P10 | Server-allowlisted default/selection and navigation restoration |
Phase III — partial analytics
| ID | Work package | Depends on | Result |
|---|---|---|---|
| P12 | Resolve SSE transport spike | P01, P05, P08 | Documented tested transport choice |
| P13 | Deliver run/snapshot/section state model | P07, P11, P12 | Idempotent start, independent sections, typed errors/retries |
| P14 | Deliver overview slice | P10, P13 | KPI priority, comparisons, eligible insights, definitions |
| P15 | Deliver payment-performance slice | P10, P13 | Outcomes, retry, timing, stage semantics |
| P16 | Deliver fees slice | P10, P13 | Session-grain real Rial fees and rate |
| P17 | Deliver peer-comparison slice | P10, P13 | Median/percentile or privacy-safe suppression |
| P18 | Integrate recovery and rate-limit flows | P13–P17 | Heartbeat, replay/reset, sibling isolation, manual retry |
Phase IV — merchant-owned evidence and export
| ID | Work package | Depends on | Result |
|---|---|---|---|
| P19 | Deliver shared cursor-detail foundation | P05, P09–P11 | Semantic responsive table, URL filters, cursor rules |
| P20 | Deliver unverified-paid detail slice | P15, P19 | Authorized read-only Paid evidence |
| P21 | Deliver failed detail slice | P15, P19 | Safe observed-stage filters without invented causes |
| P22 | Deliver retried detail slice | P15, P19 | Ordered attempt summaries and recovery evidence |
| P23 | Resolve CSV delivery spike | P01, P08, P19 | Approved browser download/error strategy |
| P24 | Deliver CSV export slice | P20–P23 | Current-filter authorized export and handoff UX |
Phase V — completion and hardening
| ID | Work package | Depends on | Result |
|---|---|---|---|
| P25 | Deliver methodology and account screens | P09–P18 | Metric/privacy explanations and demo switching/logout guidance |
| P26 | Complete responsive RTL and theme matrix | P14–P25 | 360/768/1024/1440 and both themes |
| P27 | Complete accessibility/reduced-motion verification | P14–P26 | WCAG 2.2 AA critical flows and chart/table equivalence |
| P28 | Complete security/privacy negative tests | P08–P24 | No cross-tenant/payer/peer/token exposure |
| P29 | Complete static production acceptance | P02–P28 | Build artifact passes target Nginx critical flows |
| P30 | Final documentation and human review | P29 | Current docs, evidence, approval for release handoff |
8. Vertical acceptance slices
Each implementation slice must end with user-visible behavior, a mocked/real contract path, focused tests, and the standing Definition of Done.
Slice A — authenticate and identify merchant
User can log in, see the server-returned merchant.displayName, refresh the authenticated shell, log out, and receive safe expired/throttled states. No token or tenant switch enters browser storage/URLs.
Slice B — choose an approved reporting period
User sees the server-selected latest complete Jalali month, changes among allowlisted periods, retains URL-backed state, and never submits arbitrary boundaries.
Slice C — receive the overview independently
User starts one idempotent run, sees stable four-section scaffolding, receives overview without waiting for siblings, and can interpret exact values/comparisons/insight eligibility accessibly.
Slice D — complete partial analytics
Each canonical section becomes independently useful. Failed/suppressed/rate-limited/reconnecting states preserve ready siblings and recover according to the approved transport contract.
Slice E — reconcile merchant-owned evidence
User opens each approved detail view, pages with an opaque cursor, sees only approved merchant-owned fields, and cannot broaden scope through URL/cursor changes.
Slice F — export for handoff
User confirms view/period/filter scope and downloads the synchronous server-owned CSV or receives a truthful typed error. No payer, peer, credential, or hidden browser row is exported.
9. Verification strategy
Exact command names are established in P04 and recorded in package.json; the required categories are:
| Level | Purpose | Required evidence |
|---|---|---|
| OpenAPI validation/generation | Prevent interface drift | Approved contract validates; generated output is reproducible |
| Unit | Formatters, reducers, error mapping, insight eligibility | Boundary tables for zero/null/decimal/RTL/event duplication |
| Component | Semantic states and interactions | Roles/names/focus/live-region behavior in both themes |
| Integration with contract fixtures | API client, auth, run/SSE, details/export | Every success/problem/event state maps correctly |
| Playwright | Critical user journeys | Login, default period, partial order, reconnect, detail, export, logout |
| Accessibility automation | Detect common WCAG failures | Axe-equivalent checks plus no serious/critical violations |
| Manual accessibility | Cover automation gaps | Keyboard, screen reader status, zoom/reflow, focus restoration |
| Visual/responsive | RTL/theme/layout stability | 360/768/1024/1440, light/dark, known long Persian content |
| Static production | Prove target topology | Built files behind Nginx, deep-link refresh, API/SSE/CSV proxy |
| Security/privacy | Negative authorization/data tests | Manipulated ids/cursors/filters and sensitive-field absence |
No task is complete with only typecheck/build evidence; runtime behavior and relevant error paths must be exercised.
10. Checkpoints
Checkpoint 1 — contract and scaffold
- Gate A and Gate B pass.
- No draft interface is embedded in generated code.
- The empty shell builds and runs through the target static topology.
Checkpoint 2 — identity and period
- Two fixture/demo users remain tenant-isolated.
- Login/session/logout and period URL flows pass keyboard and error-state checks.
- Persian formatting/theme/font foundations pass both themes.
Checkpoint 3 — first vertical analytics slice
- Idempotent start plus overview snapshot/SSE works end to end.
- Stable section scaffolding and restrained status announcements are verified.
- Review the chosen SSE transport before building remaining sections.
Checkpoint 4 — all analytics sections
- All four ready states, peer suppression, one-section failure, retry, rate limit, replay/reset, and heartbeat recovery pass.
- Metric semantics reconcile with the catalogue fixtures.
- Human review confirms chart/table legibility in RTL/light/dark.
Checkpoint 5 — details and export
- Three detail views reconcile to aggregates and resist cursor/filter/tenant manipulation.
- CSV strategy passes cap/error/interruption/privacy checks.
- Finance/legal handoff copy and behavior receive product approval.
Checkpoint 6 — release candidate
- Full Definition of Done, acceptance matrix, static Nginx verification, and human review pass.
- No deployment or release occurs without separate authorization.
11. Risks and mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Implementing against draft OpenAPI | High rework/contract drift | Hard Gate A; generated types only from approved version |
Native EventSource hides HTTP error detail |
High recovery/accessibility gap | P12 spike; select fetch stream only if evidence requires it |
| Two reconnect owners create duplicate streams | High rate-limit/data duplication | One transport owner, explicit cleanup, concurrency tests |
| Synchronous CSV cannot support promised ready/error UX | High misleading behavior | P23 spike and UX amendment before export implementation |
| 50 MiB Blob download harms mobile memory | Medium/High | Measure at cap; prefer server streaming/direct download or revise policy |
| Static fallback mishandles deep links/API paths | High production failure | P02 Nginx proof before feature work |
| Chart library adds inaccessible/RTL-heavy dependency | Medium | Start semantic SVG/HTML; add dependency only after focused evaluation |
| Decimal strings are coerced to floating point | High financial display error | Exact parsing/format tests; format transport decimals deliberately |
| Persian/Jalali/bidi formatting corrupts technical ids | High trust/usability issue | Central formatters, long/mixed fixtures, screen-reader and visual review |
| Theme initialization flashes or leaks state across tests | Medium | Pre-paint presentation-only bootstrap and deterministic theme tests |
| Completion-order rendering shifts layout/focus | High accessibility issue | Stable frames, reducer tests, focus-preservation E2E |
| Handwritten DTOs drift from OpenAPI | High integration failure | Generated types plus CI drift check; view models remain separate |
| Fixtures accidentally contain source-sensitive data | High privacy issue | Synthetic builders, forbidden-field scans, review gate |
12. Parallelization after foundation
Safe after P13 stabilizes:
- Overview, payment-performance, fees, and peer presentation against frozen fixtures.
- Methodology copy and account presentation.
- Independent component/accessibility tests for already-defined primitives.
Sequential or coordinated:
- API approval → type generation → client/state work.
- Static topology → scaffold/config.
- Auth/session → protected routes and all merchant data.
- Period selection → analytics run.
- SSE spike → run reconciliation → recovery integration.
- Detail foundation → three detail slices → export.
- Shared design tokens, formatters, generated files, and route layouts require one owner at a time.
13. Definition of Done
Every Phase 3 task must satisfy its acceptance criteria and the standing project bar:
- Runtime behavior and error paths are verified; new tests fail without the change and pass with it.
- Existing checks pass: formatting, lint, strict typing/
svelte-check, focused tests, and applicable build/E2E. - Code stays scoped, intent-revealing, free of duplication/debug/dead code, and within approved design/API contracts.
- Accessibility, responsive RTL, light/dark, reduced-motion, security, and privacy implications are checked in proportion to the change.
- Public behavior/interfaces and durable decisions are documented.
- The increment is an atomic Commitizen-style commit with no secrets or generated/build debris.
- Human review occurs at named checkpoints and before merge/deploy.
14. Phase 3 output
The draft Phase 3 list is recorded in tasks/todo.md with one task per focused session. Each task includes:
- One short outcome-focused title without combined independent concerns.
- At most three testable acceptance criteria.
- Focused tests, build/runtime verification, and manual check where needed.
- Explicit dependency ids and likely files.
- Estimated XS/S/M scope, normally no more than five files.
- Checkpoints matching section 10.
No Phase 3 task may say merely “build the dashboard” or combine a foundation layer with multiple unrelated screens.
15. Approval record
Approved on 2026-08-20:
- Build order and the hard rule that implementation consumes only the approved backend
openapi.yamlversion1.0.0from a recorded immutable source revision. - Synchronous CSV defaults: 100,000 rows, 50 MiB, 60 seconds, 3 requests/10 minutes with burst 1, UTF-8 BOM, and the approved stable Persian headers.
- Ninety-day analytics/export audit retention for the hackathon; minimal demo-authentication audit retention remains 30 days.
- Display/export of the existing approved opaque terminal reference.
merchant.displayNameas the shell and CSV-preflight merchant name.- Early SSE, CSV-delivery, and chart risk spikes before committing their implementation patterns or dependencies.
Resolved external contracts: the production Problem Details base URI is owned by the backend contract, and the exact Nginx deep-link/static fallback plus /api/v1 proxy behavior is owned by the deployment contract.
16. Official implementation references
- SvelteKit static adapter and fallback behavior
- Tailwind CSS v4 Vite installation
- Vitest guide
- Playwright installation and test runner
- WCAG 2.2
- WHATWG server-sent events
17. Approved feature-branch redesign delivery
The user approved docs/ZARINPAL-REDESIGN-AMENDMENT.md on 2026-08-20 and required the work to remain on feature/zarinpal-dashboard-redesign until a separately running preview is reviewed.
Build order:
- Extend the backend contract for four latest complete period windows, terminal-scoped runs, payment history, and routed transaction evidence.
- Generate the TypeScript contract and add shadcn-svelte/Lucide foundations.
- Replace the single-page dashboard with the shared routed shell and URL-backed controls.
- Deliver overview, transaction, performance, fee, peer, methodology, and detail routes as thin vertical slices.
- Verify the isolated feature instance on alternate ports, then wait for explicit merge approval.
The current instance remains untouched while the feature preview uses API port 8081 and frontend port 5174.