docs(ux): specify merchant dashboard experience
This commit is contained in:
788
docs/DASHBOARD-UX-SPEC.md
Normal file
788
docs/DASHBOARD-UX-SPEC.md
Normal file
@@ -0,0 +1,788 @@
|
|||||||
|
# Dashboard UX Specification
|
||||||
|
|
||||||
|
- Status: Draft for product/design approval — implementation is not authorized
|
||||||
|
- Product: ZarinPal Merchant Analytics hackathon dashboard
|
||||||
|
- Audience: non-technical Persian-speaking merchants, hackathon judges, finance/legal handoff recipients
|
||||||
|
- Direction: Persian-first RTL
|
||||||
|
- Last updated: 2026-08-20
|
||||||
|
|
||||||
|
## 1. Purpose and authority
|
||||||
|
|
||||||
|
This document defines the visible structure, interaction behavior, content rules, responsive layouts, accessibility requirements, and asynchronous states of the merchant analytics dashboard. It is an implementation-neutral UX contract. It does not authorize Svelte components, API clients, backend changes, export endpoints, or deployment work.
|
||||||
|
|
||||||
|
The following sources are authoritative, in precedence order:
|
||||||
|
|
||||||
|
1. Approved backend `IDENTITY-SPEC.md` for authentication, session, and tenant boundaries.
|
||||||
|
2. Approved backend `ANALYTICS-SPEC.md` and the approved definitions in `METRIC-CATALOGUE.md` version `0.4.0` for metrics, periods, thresholds, privacy, and partial delivery.
|
||||||
|
3. Frontend `docs/DASHBOARD-SPEC.md` for product capability and acceptance requirements.
|
||||||
|
4. Frontend `DESIGN.md` version `0.3.2` for visual tokens, typography, themes, motion, direction, and component-system rules.
|
||||||
|
5. This document for screen-level presentation and interaction detail.
|
||||||
|
|
||||||
|
If an implementation-facing API description later conflicts with an approved source above, implementation pauses and the relevant specification is amended first. The UI never derives metric formulas, authorization scope, peer eligibility, retry policy, or export fields on its own.
|
||||||
|
|
||||||
|
## 2. Product boundary
|
||||||
|
|
||||||
|
### 2.1 In scope
|
||||||
|
|
||||||
|
- Demo username/password login for one user bound to one merchant.
|
||||||
|
- A merchant-scoped overview, payment performance, fee analysis, and anonymized peer comparison.
|
||||||
|
- Deterministic insights that meet the approved evidence threshold.
|
||||||
|
- Read-only merchant-owned detail views for unverified paid, failed, and retried sessions.
|
||||||
|
- Authorized CSV export from eligible merchant-owned detail views for finance/legal handoff.
|
||||||
|
- Metric definitions, freshness, coverage, and limitations.
|
||||||
|
- First-class light and dark themes.
|
||||||
|
- Desktop, tablet, and mobile layouts in Persian RTL.
|
||||||
|
|
||||||
|
### 2.2 Out of scope
|
||||||
|
|
||||||
|
- Registration, recovery, MFA, roles, delegated users, or simulated ZarinPal SSO.
|
||||||
|
- An authenticated cross-merchant picker, operator mode, or browser-side tenant override.
|
||||||
|
- AI-written findings, forecasting, causes, prescriptive automation, or free-form analysis.
|
||||||
|
- Holiday/event reporting, custom formulas, arbitrary date ranges, or cross-category peer filters.
|
||||||
|
- Peer identities, peer rows, individual peer values/ranks, payer tokens, credentials, or raw source records.
|
||||||
|
- Editing, refunding, verifying, retrying, or otherwise mutating payment sessions from the dashboard.
|
||||||
|
- Exporting aggregate peer data or peer-level detail.
|
||||||
|
|
||||||
|
## 3. Experience principles
|
||||||
|
|
||||||
|
1. **Answer first.** Lead with the exact metric and a one-sentence takeaway; use charts to support interpretation.
|
||||||
|
2. **Trust is visible.** Every analytical surface states period, unit, definition, comparison baseline, sample/coverage where material, and calculation time.
|
||||||
|
3. **Ready work stays useful.** A slow, failed, reconnecting, or rate-limited section never hides ready sibling sections.
|
||||||
|
4. **No invented certainty.** Distinguish zero, empty period, undefined denominator, missing source, unavailable baseline, suppressed peer result, calculation failure, and pending work.
|
||||||
|
5. **Merchant scope is immutable in-session.** Tenant identity comes from the server session; a user cannot switch merchants by changing a URL, filter, payload, or export parameter.
|
||||||
|
6. **Detail without leakage.** Merchant-owned evidence reconciles aggregates while payer tokens and all peer-level data remain absent from the browser and exports.
|
||||||
|
7. **Persian by default.** Merchant-facing copy, digits, money, dates, reading order, and control order are deliberately RTL; technical values are isolated LTR.
|
||||||
|
8. **Calm over spectacle.** Motion is short and functional. Streaming completion is communicated by stable structure and text, not animated card choreography.
|
||||||
|
|
||||||
|
## 4. Actors and session model
|
||||||
|
|
||||||
|
| Actor | Entry | Visible merchant scope | Switching behavior |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Demo merchant user | Assigned username and password | Exactly the merchant bound to the authenticated server session | Log out, then sign in with another seeded merchant account |
|
||||||
|
| Merchant user | Same MVP login adapter | Exactly one merchant | Same as demo user until ZarinPal SSO replaces the adapter |
|
||||||
|
| Judge/operator | Uses separately distributed seeded credentials | No hidden operator or cross-tenant browser privilege | Uses explicit logout/login between demonstrations |
|
||||||
|
| Finance/legal recipient | Receives an authorized CSV outside the dashboard workflow | Only fields in the merchant-owned exported detail view | No dashboard access is implied by receiving a file |
|
||||||
|
|
||||||
|
The active merchant appears as identity context, not as a selector. The UI must not present a merchant dropdown that suggests the current session can change tenant scope.
|
||||||
|
|
||||||
|
## 5. Screen map
|
||||||
|
|
||||||
|
Technical route names are proposed for planning only; final route contracts remain subject to the approved interface and static-host fallback.
|
||||||
|
|
||||||
|
| Screen | Persian label | Primary task | Entry and exits |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Login | ورود | Establish the demo merchant session | Entry from unauthenticated routes/session expiry; exit to overview |
|
||||||
|
| Overview | پیشخوان | Understand current outcomes and the most important qualifying changes | Default authenticated destination; links to focused analysis/detail |
|
||||||
|
| Payment performance | عملکرد پرداخت | Diagnose success, verification, failure, retry, and timing | From navigation/KPIs/insights; links to three approved detail views |
|
||||||
|
| Unverified-paid detail | پرداختهای تأییدنشده | Reconcile bank-paid sessions awaiting merchant verification | From verification card; CSV eligible |
|
||||||
|
| Failed-session detail | پرداختهای ناموفق | Review observed failure stages without invented causes | From failure card; CSV eligible |
|
||||||
|
| Retried-session detail | پرداختهای تکرارشده | Review attempt history and eventual recovery | From retry card; CSV eligible |
|
||||||
|
| Fees | کارمزد و درآمد | Understand real merchant fee amount and paid fee rate in Rial | From navigation/overview; no attempt-grain fee multiplication |
|
||||||
|
| Peer comparison | مقایسه با همگروهها | Compare approved metrics with eligible same-category merchants | Aggregate-only; never links to peer rows or peer export |
|
||||||
|
| Metric quality | کیفیت داده و شاخصها | Understand definitions, periods, freshness, coverage, suppression, and limitations | From navigation and definition/provenance links |
|
||||||
|
| Account/session | حساب کاربری | Confirm current merchant identity, switch theme, or log out | No roles/profile editing in MVP |
|
||||||
|
|
||||||
|
### 5.1 Navigation hierarchy
|
||||||
|
|
||||||
|
Desktop navigation order from top to bottom:
|
||||||
|
|
||||||
|
1. Product mark and label.
|
||||||
|
2. Merchant identity block: merchant display name plus non-interactive account context.
|
||||||
|
3. Primary analysis links: پیشخوان، عملکرد پرداخت، کارمزد و درآمد، مقایسه با همگروهها.
|
||||||
|
4. Trust link: کیفیت داده و شاخصها.
|
||||||
|
5. Bottom utility group: theme control, account, logout.
|
||||||
|
|
||||||
|
Mobile uses the same order in a right-side drawer. The utility header keeps only the menu trigger, current page title, compact merchant identity, and theme access if space permits. Active location uses text, background/bar, and `aria-current`; color alone is insufficient.
|
||||||
|
|
||||||
|
## 6. Global information hierarchy
|
||||||
|
|
||||||
|
Every authenticated analysis screen follows the same reading order:
|
||||||
|
|
||||||
|
1. Skip link and page heading.
|
||||||
|
2. Merchant identity and selected reporting context.
|
||||||
|
3. Overall analytics run/progress or connection status when relevant.
|
||||||
|
4. Period controls and exact comparison baseline.
|
||||||
|
5. Page takeaway and primary exact KPI(s).
|
||||||
|
6. Supporting charts/tables and qualifying deterministic insights.
|
||||||
|
7. Traceability: samples, coverage, freshness, definitions, limitations.
|
||||||
|
8. Related merchant-owned detail action where authorized.
|
||||||
|
|
||||||
|
The period control is shared across analysis screens through URL-backed state. A navigation or reload retains a valid selected period. The first access defaults to the latest complete Jalali month containing data and compares only with the immediately previous equivalent complete month. No alternative baseline is silently substituted.
|
||||||
|
|
||||||
|
## 7. Representative shell layouts
|
||||||
|
|
||||||
|
Wireframes show reading hierarchy, not pixel-perfect styling. In each diagram, the visual right edge is shown on the right.
|
||||||
|
|
||||||
|
### 7.1 Wide desktop, 12-column content grid
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ابزارهای کمتأکید: وضعیت اتصال · زمان محاسبه · تغییر پوسته عنوان صفحه · بازرگان │
|
||||||
|
├───────────────────────────────────────────────────────────────┬──────────────────────┤
|
||||||
|
│ │ نشان / تحلیل زرینپال │
|
||||||
|
│ [پیام پیشرفت چهار بخش یا بازیابی اتصال؛ فقط هنگام نیاز] │ [هویت بازرگان ثابت] │
|
||||||
|
│ │ │
|
||||||
|
│ [بازه و مبنای مقایسه] [خلاصه صفحه]│ ● پیشخوان │
|
||||||
|
│ │ عملکرد پرداخت │
|
||||||
|
│ ┌────────┬────────┬────────┬────────┐ │ کارمزد و درآمد │
|
||||||
|
│ │ KPI ۴ │ KPI ۳ │ KPI ۲ │ KPI ۱ │ ← ترتیب خواندن RTL │ مقایسه همگروهها │
|
||||||
|
│ └────────┴────────┴────────┴────────┘ │ │
|
||||||
|
│ │ کیفیت داده و شاخصها│
|
||||||
|
│ ┌───────────────────────────────────┬───────────────────────┐ │ │
|
||||||
|
│ │ نمودار/جدول اصلی، ۸ ستون │ بینش/خلاصه، ۴ ستون │ │ پوسته · حساب · خروج │
|
||||||
|
│ └───────────────────────────────────┴───────────────────────┘ │ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐ │ │
|
||||||
|
│ │ شواهد، جزئیات مجاز، تعریف و پوشش داده │ │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │ │
|
||||||
|
└───────────────────────────────────────────────────────────────┴──────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
- Sidebar width: 248–272 px; main gutters: 24–32 px.
|
||||||
|
- The primary plot takes eight columns when its comparison/insight needs a four-column companion. Otherwise it may span all 12.
|
||||||
|
- Card heights reserve the final ready footprint during async states to prevent completion-order layout shift.
|
||||||
|
- Tables use the full content column when required.
|
||||||
|
|
||||||
|
### 7.2 Tablet, 768–1023 px
|
||||||
|
|
||||||
|
- The sidebar becomes a focus-managed drawer from the right.
|
||||||
|
- Header and filter controls wrap into two stable rows; the Apply action remains adjacent to the period group.
|
||||||
|
- KPI cards use two columns only when values, units, and comparison labels remain legible without truncating meaning.
|
||||||
|
- Primary chart and insight stack vertically. A secondary two-column arrangement is allowed only after a 768 px content-fit check.
|
||||||
|
- Priority columns replace horizontal overflow where possible; approved full detail tables may use a labeled horizontal scroll region.
|
||||||
|
|
||||||
|
### 7.3 Mobile, 360 px representative
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ [پوسته] پیشخوان [منو] │
|
||||||
|
│ نام بازرگان │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ وضعیت تحلیل: ۲ از ۴ بخش آماده│
|
||||||
|
│ عملکرد پرداخت در حال محاسبه… │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ آخرین ماه کامل: مرداد ۱۴۰۵ │
|
||||||
|
│ مقایسه: تیر ۱۴۰۵ │
|
||||||
|
│ [تغییر بازه] │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ مبلغ پرداختشده بانکی │
|
||||||
|
│ ۱۲٬۳۴۵٬۰۰۰٬۰۰۰ ریال │
|
||||||
|
│ در مقایسه با بازه قبل: ۸٪ ↑ │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ تعداد پرداختهای بانکی │
|
||||||
|
│ ۳٬۲۴۱ نشست │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ نرخ موفقیت پرداخت │
|
||||||
|
│ ۹۱٫۴٪ │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ تکمیل تأیید │
|
||||||
|
│ ۸۷٫۲٪ │
|
||||||
|
│ [مشاهده پرداختهای تأییدنشده] │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ نتیجه اصلی این بازه │
|
||||||
|
│ متن کوتاه + شواهد عددی │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ روند بازه │
|
||||||
|
│ [خلاصه متنی] │
|
||||||
|
│ [نمودار با ارتفاع ≥ ۲۴۰px] │
|
||||||
|
│ [نمایش جدول داده] │
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ تعریف · پوشش · زمان محاسبه │
|
||||||
|
└──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
- Outer gutter: 16 px; cards are one column.
|
||||||
|
- The exact KPI precedes its chart; no chart is necessary to learn the current value.
|
||||||
|
- Long merchant names wrap to two lines before truncation, with the full value available in account context.
|
||||||
|
- No sticky action may cover the last table row, focused control, or device safe area.
|
||||||
|
- The navigation drawer traps focus, closes on Escape/backdrop, locks background scroll, and restores focus to the trigger.
|
||||||
|
|
||||||
|
## 8. Global filters and reporting context
|
||||||
|
|
||||||
|
### 8.1 Period selector
|
||||||
|
|
||||||
|
Allowed period types only:
|
||||||
|
|
||||||
|
- روزانه
|
||||||
|
- هفتگی، شنبه تا جمعه in `Asia/Tehran`
|
||||||
|
- ماه شمسی
|
||||||
|
- فصل ایرانی
|
||||||
|
|
||||||
|
The control does not offer arbitrary date ranges, Gregorian display, holiday/event filters, or future/incomplete periods as the default. The selected label uses the Jalali calendar and Persian digits; request boundaries remain unambiguous Tehran-local half-open intervals in the API contract.
|
||||||
|
|
||||||
|
### 8.2 Apply behavior
|
||||||
|
|
||||||
|
- Changing controls edits a pending selection; data does not refetch until the user chooses «اعمال بازه».
|
||||||
|
- «بازنشانی» returns to the product default, not an empty selection.
|
||||||
|
- Applying updates the URL and starts/returns the equivalent analytics run idempotently.
|
||||||
|
- During start, existing ready results stay visible but are clearly associated with the prior period until replaced. The pending/new period label is never placed over old values without an explicit loading distinction.
|
||||||
|
- Browser Back/Forward restores the prior valid filter state and its matching results.
|
||||||
|
- Invalid or unsupported URL filter values are rejected safely and replaced with an explanatory default state; they never expand authorization or period scope.
|
||||||
|
|
||||||
|
### 8.3 Reporting context block
|
||||||
|
|
||||||
|
Every section exposes:
|
||||||
|
|
||||||
|
- Exact selected period label and boundaries in merchant-readable Jalali form.
|
||||||
|
- Comparison label and exact equivalent baseline, or «مبنای مقایسه در دسترس نیست».
|
||||||
|
- Unit: ریال، درصد، نشست، تلاش، میلیثانیه, or optional readable seconds in addition to milliseconds.
|
||||||
|
- Calculation timestamp in Jalali/Tehran.
|
||||||
|
- Sample/denominator and non-null coverage where the metric requires it.
|
||||||
|
- Metric definition link and definition version.
|
||||||
|
|
||||||
|
## 9. Screen specifications
|
||||||
|
|
||||||
|
### 9.1 Login
|
||||||
|
|
||||||
|
#### Goal
|
||||||
|
|
||||||
|
Authenticate one seeded merchant user without revealing account existence and without implying registration or recovery.
|
||||||
|
|
||||||
|
#### Content order
|
||||||
|
|
||||||
|
1. Product identity and concise demo context.
|
||||||
|
2. Heading «ورود به تحلیل بازرگان».
|
||||||
|
3. Visible username label and input.
|
||||||
|
4. Visible password label and input with show/hide control.
|
||||||
|
5. Submit action «ورود».
|
||||||
|
6. Generic form error/status area.
|
||||||
|
7. Short notice that access credentials are assigned for the demo.
|
||||||
|
|
||||||
|
#### Interaction contract
|
||||||
|
|
||||||
|
- Username uses LTR isolation, disables spellcheck, and supports password-manager autofill.
|
||||||
|
- Password supports paste and `autocomplete="current-password"`; the UI never blocks password managers or requires a cognitive test.
|
||||||
|
- Submit remains available until a request begins; while pending, its label becomes «در حال ورود…» and duplicate submissions are prevented.
|
||||||
|
- Invalid username, invalid password, disabled account, and expected authentication failures share this exact safe message: «نام کاربری یا رمز عبور درست نیست. دوباره تلاش کنید.»
|
||||||
|
- A typed `429` shows: «تعداد تلاشها زیاد است. پس از {زمان باقیمانده} دوباره تلاش کنید.» The countdown is informative; no tight client retry loop runs.
|
||||||
|
- A service failure shows a distinct safe recovery message: «ورود اکنون انجام نشد. اتصال را بررسی کنید و دوباره تلاش کنید.»
|
||||||
|
- On success, the new session leads to overview. No authentication token enters frontend-readable storage or the URL.
|
||||||
|
- On session expiry/revocation, return to login with: «نشست شما پایان یافته است. برای ادامه دوباره وارد شوید.» This does not disclose the internal reason.
|
||||||
|
|
||||||
|
### 9.2 Overview / پیشخوان
|
||||||
|
|
||||||
|
#### Goal
|
||||||
|
|
||||||
|
Answer, in order: how much was bank-paid, how many sessions were bank-paid, how successful payment was, and how completely the merchant verified bank-paid sessions.
|
||||||
|
|
||||||
|
#### Primary KPI order
|
||||||
|
|
||||||
|
1. مبلغ پرداختشده بانکی — `bank_paid_amount_rial` — Rial only.
|
||||||
|
2. تعداد پرداختهای بانکی — `bank_paid_sessions` — sessions.
|
||||||
|
3. نرخ موفقیت پرداخت — `payment_success_rate`.
|
||||||
|
4. تکمیل تأیید — `verification_completion_rate`.
|
||||||
|
|
||||||
|
`Paid` and `Verified` both contribute to the first three success/volume measures. The fourth deliberately separates completion, and a `Paid` session is described as bank-paid but not yet merchant-verified, never as failed.
|
||||||
|
|
||||||
|
#### Supporting content
|
||||||
|
|
||||||
|
- One trend chart for the four headline outcomes, with a metric switch rather than four simultaneous axes.
|
||||||
|
- Zero or more qualifying deterministic insight cards, ordered by materiality and merchant actionability.
|
||||||
|
- A verification-gap summary linking to authorized unverified-paid detail.
|
||||||
|
- Compact continuation links to payment performance and fees.
|
||||||
|
|
||||||
|
#### Insight card anatomy
|
||||||
|
|
||||||
|
1. Observed change in plain Persian.
|
||||||
|
2. Exact current and baseline evidence.
|
||||||
|
3. Relevant session count.
|
||||||
|
4. Possible merchant action, phrased without causal certainty.
|
||||||
|
5. Limitation/definition link.
|
||||||
|
|
||||||
|
An insight card appears only with at least 100 relevant sessions and a qualifying absolute change: at least 3 percentage points for rates or 20 percent for amounts/counts. A zero, missing, or undefined baseline never produces relative-change insight copy. Below-threshold comparisons remain visible on KPI cards without promotional styling or an action claim.
|
||||||
|
|
||||||
|
Example qualifying pattern: «نرخ تکمیل تأیید ۴٫۲ واحد درصد کمتر از بازه قبل است. ۲۴۸ پرداخت بانکی هنوز تأیید نشدهاند. مسیر تأیید سمت پذیرنده را بررسی کنید.» The product must not claim why the change occurred.
|
||||||
|
|
||||||
|
### 9.3 Payment performance / عملکرد پرداخت
|
||||||
|
|
||||||
|
#### Section order
|
||||||
|
|
||||||
|
1. Payment outcome summary: Verified, unverified Paid, Failed, and Reversed when non-zero.
|
||||||
|
2. Retry behavior and eventual bank-payment recovery.
|
||||||
|
3. Processing time for initialization and verification.
|
||||||
|
4. Observable stage diagnostics and read-only detail links.
|
||||||
|
|
||||||
|
#### Recommended visual forms
|
||||||
|
|
||||||
|
| Question | Primary form | Exact alternative |
|
||||||
|
|---|---|---|
|
||||||
|
| How are final session outcomes distributed? | Direct-labeled stacked bar; `Paid` and `Verified` visually distinct but grouped under bank-paid meaning | Semantic table of count, share, and definition |
|
||||||
|
| Is retry recovery changing? | Period trend line/columns with exact current rate and denominator | Period/value table |
|
||||||
|
| What are p50 and p95 times? | Paired dot/range plot per timing metric; no dual-axis chart | Table with p50, p95, sample, coverage, unit |
|
||||||
|
| Where did failed sessions observably reach? | Ordered horizontal bars for zero-attempt, reached-bank, and other observed stages | Stage/count table with caveat |
|
||||||
|
|
||||||
|
`NoAttempt` means zero normalized attempts, not a failed attempt. `InBank` means an observed gateway stage, not bank rejection. Timing nulls are excluded, never displayed as zero. Reversed is outside the success numerator and appears separately when non-zero.
|
||||||
|
|
||||||
|
#### Approved detail entries
|
||||||
|
|
||||||
|
- «مشاهده پرداختهای تأییدنشده»
|
||||||
|
- «مشاهده پرداختهای ناموفق»
|
||||||
|
- «مشاهده پرداختهای تکرارشده»
|
||||||
|
|
||||||
|
Each detail opens with the active period/filter summary, result count, read-only notice, and definition. No action mutates payment state.
|
||||||
|
|
||||||
|
### 9.4 Merchant-owned detail views
|
||||||
|
|
||||||
|
#### Shared table contract
|
||||||
|
|
||||||
|
- A semantic table with server-owned pagination and sorting.
|
||||||
|
- Columns are limited to fields returned by the approved detail interface and visible to the current merchant.
|
||||||
|
- Never render, request, cache, log, or export payer tokens, credentials, another merchant's rows, or peer-level data.
|
||||||
|
- Session identifiers are canonical Latin technical values in isolated LTR spans; user-facing dates/counts/status labels use Persian conventions.
|
||||||
|
- Attempt history expands within a row or dedicated merchant-scoped detail surface; keyboard activation must not rely on clicking the whole row.
|
||||||
|
- Row actions have explicit names such as «نمایش تاریخچه تلاشهای این نشست».
|
||||||
|
- A table caption states the selected period, result type, and total known result count.
|
||||||
|
- Pagination/filter state appears in the URL where supported.
|
||||||
|
|
||||||
|
#### Responsive priority
|
||||||
|
|
||||||
|
1. Merchant-readable status/stage.
|
||||||
|
2. Date/time.
|
||||||
|
3. Amount in Rial where authorized/relevant.
|
||||||
|
4. Session identifier.
|
||||||
|
5. Attempt count/recovery result where relevant.
|
||||||
|
6. Additional approved evidence.
|
||||||
|
|
||||||
|
At mobile width, the first three fields form a stacked row summary and a labeled disclosure reveals remaining fields. Horizontal scrolling is reserved for the full table alternative and is announced/labeled; critical meaning cannot require an off-screen unlabeled column.
|
||||||
|
|
||||||
|
#### Empty copy
|
||||||
|
|
||||||
|
- Unverified paid: «در این بازه پرداخت بانکیِ تأییدنشدهای وجود ندارد.»
|
||||||
|
- Failed: «در این بازه پرداخت ناموفقی ثبت نشده است.»
|
||||||
|
- Retried: «در این بازه نشست پرداختِ تکرارشدهای وجود ندارد.»
|
||||||
|
|
||||||
|
Empty results are successful query outcomes, not system errors.
|
||||||
|
|
||||||
|
### 9.5 Fees / کارمزد و درآمد
|
||||||
|
|
||||||
|
#### Primary hierarchy
|
||||||
|
|
||||||
|
1. کارمزد پرداختهای بانکی — `paid_fee_amount_rial`.
|
||||||
|
2. مبلغ پرداختشده بانکی — denominator in Rial.
|
||||||
|
3. نرخ کارمزد پرداختشده — `paid_fee_rate`.
|
||||||
|
4. Equivalent prior-period comparison or explicit unavailable baseline.
|
||||||
|
|
||||||
|
The primary chart is a period trend for fee amount with an optional explicit switch to fee rate. Never place Rial and percentage on an unlabeled shared axis. Every amount is summed once per eligible session; additional attempts cannot multiply the fee.
|
||||||
|
|
||||||
|
The product calls the value «کارمزد واقعی بازرگان» or «کارمزد پرداختهای بانکی», never obfuscated, estimated, or attempt-based. A fee rate with a zero paid amount is undefined, not `۰٪`.
|
||||||
|
|
||||||
|
### 9.6 Peer comparison / مقایسه با همگروهها
|
||||||
|
|
||||||
|
#### Eligibility preface
|
||||||
|
|
||||||
|
The page explains that comparison uses other eligible merchants in the active merchant's category and selected period, with equal weight per merchant. It never exposes merchant identities or individual values.
|
||||||
|
|
||||||
|
#### Metric card anatomy
|
||||||
|
|
||||||
|
1. Metric label.
|
||||||
|
2. Active merchant exact value.
|
||||||
|
3. Cohort merchant-weighted median.
|
||||||
|
4. Direction-aware merchant performance percentile, worded so a higher percentile is always better.
|
||||||
|
5. Eligible peer merchant count and combined peer session count.
|
||||||
|
6. Direction note when lower is better for processing time or paid fee rate.
|
||||||
|
7. Definition and suppression policy link.
|
||||||
|
|
||||||
|
Recommended plot: a compact benchmark range/bar with two direct markers, «شما» and «میانه همگروهها», plus a textual percentile. It is not a distribution of individual peers and must not imply unseen individual ranks.
|
||||||
|
|
||||||
|
#### Suppressed state
|
||||||
|
|
||||||
|
If there are fewer than 10 other eligible merchants or fewer than 1,000 combined peer sessions, replace peer values and plot with:
|
||||||
|
|
||||||
|
«برای نمایش مقایسه قابلاعتماد، داده همگروههای کافی در این بازه وجود ندارد. آمار خود شما همچنان کامل نمایش داده میشود.»
|
||||||
|
|
||||||
|
Do not reveal which threshold failed, a near-threshold count that could aid differencing, or any peer identity/value. Merchant-owned sections remain visible.
|
||||||
|
|
||||||
|
### 9.7 Metric quality / کیفیت داده و شاخصها
|
||||||
|
|
||||||
|
#### Sections
|
||||||
|
|
||||||
|
- Metric dictionary: Persian display name, canonical id, definition, numerator/denominator, grain, unit, missing-data behavior, and version.
|
||||||
|
- Period model: Jalali day/month/season, Saturday–Friday week, Tehran timezone, half-open API boundaries.
|
||||||
|
- Payment lifecycle: why `Paid` and `Verified` are bank successes and only `Verified` is finalized.
|
||||||
|
- Coverage: unknown statuses, timing non-null coverage, sample counts, and missing baseline behavior.
|
||||||
|
- Peer privacy: equal-per-merchant median, direction-aware percentile, suppression, and prohibited peer detail.
|
||||||
|
- Calculation lifecycle: lazy first access, independent four-section delivery, revision/version trace, and 30-day disposable cache without implying TTL-based staleness.
|
||||||
|
- Insight policy: 100-session evidence floor and approved change thresholds; observational, not causal language.
|
||||||
|
|
||||||
|
Definitions open as a page or anchored side panel. They are linkable, keyboard reachable, and do not trap important content inside hover-only tooltips.
|
||||||
|
|
||||||
|
### 9.8 Account/session / حساب کاربری
|
||||||
|
|
||||||
|
- Shows the authenticated merchant display name and demo username context without exposing session details or credentials.
|
||||||
|
- Offers the accessible light/dark theme control.
|
||||||
|
- Offers «خروج» as the only account mutation.
|
||||||
|
- Explains demo switching: «برای نمایش حساب بازرگان دیگری، خارج شوید و با اطلاعات همان حساب وارد شوید.»
|
||||||
|
- Logout shows an in-progress state, revokes server-side access before returning to login, and clears ready dashboard content from the rendered session context.
|
||||||
|
|
||||||
|
## 10. Analytics run and partial SSE model
|
||||||
|
|
||||||
|
### 10.1 Stable page scaffold
|
||||||
|
|
||||||
|
On first relevant access, render the page heading, merchant identity, filter context, four section regions, and an overall status region before results arrive. Each section owns a stable minimum footprint based on its final contract. The UI then:
|
||||||
|
|
||||||
|
1. Sends the authenticated idempotent start request.
|
||||||
|
2. Renders any immediately returned ready snapshots.
|
||||||
|
3. Opens the authenticated SSE stream for the run.
|
||||||
|
4. Applies section events independently by stable section id.
|
||||||
|
5. Preserves ready sections across sibling failures, reconnects, and rate limits.
|
||||||
|
|
||||||
|
Overall progress is written as a contextual message such as «۲ از ۴ بخش تحلیل آماده است». Do not create four competing live regions or announce every heartbeat.
|
||||||
|
|
||||||
|
### 10.2 Section state matrix
|
||||||
|
|
||||||
|
| Contract/UI state | Visible content | Available actions | Accessibility behavior |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Not requested | Section title and neutral placeholder only when the screen has not initiated that section | Apply valid filters/start access | No live announcement |
|
||||||
|
| Starting | Stable section frame; «در حال آمادهسازی تحلیل…» | Controls that would duplicate start are busy/disabled | One overall polite status; `aria-busy` on affected region |
|
||||||
|
| Queued | Section title, period/unit context, layout-matched skeleton, «در صف محاسبه» | None | Status text remains available; skeleton is hidden from assistive tech |
|
||||||
|
| Calculating | Same stable frame, «در حال محاسبه…»; automatic backend retries remain in this state | None; no manual retry yet | Overall progress announces meaningful transitions, not animation frames |
|
||||||
|
| Ready | Exact KPI/takeaway/chart/table/trace metadata | Definition, approved drill-down/export | Focus does not move; one concise completion announcement when useful |
|
||||||
|
| Ready with no rows | Purpose-specific empty copy and period | Change period; no retry unless backend says failure | Announced as successful empty result, not error |
|
||||||
|
| Ready with unavailable comparison | Current exact metric plus «مبنای مقایسه در دسترس نیست» | Definition/change period | No trend icon, relative change, or insight claim |
|
||||||
|
| Ready with undefined rate | Numerator/denominator evidence and «نرخ قابل محاسبه نیست» | Definition/change period | Never spoken/displayed as zero |
|
||||||
|
| Suppressed | Peer-specific privacy/insufficient-data explanation; no peer values | Definition/change period | Status text explains that merchant data is unaffected |
|
||||||
|
| Failed after exhaustion | Section-local safe error; error/reference id only if approved and non-sensitive | «تلاش دوباره برای این بخش» | Error is associated with section heading; retry receives focus only if user initiated it |
|
||||||
|
| Manual retry active | Ready siblings unchanged; failed section returns to calculating with retry label | Retry disabled until terminal response | Polite status; no repeated countdown announcements |
|
||||||
|
| Rate-limited | Ready content retained; affected action explains wait and honors `Retry-After` | Retry becomes available only when permitted; no loop | Remaining time is not announced every second; announce initial limit and availability |
|
||||||
|
| Connection recovering | Ready content retained; unfinished sections show «در حال بازیابی اتصال…» | Optional explicit reconnect only after automatic strategy stops | One overall polite status; heartbeat events are silent |
|
||||||
|
| Replay expired | Ready content retained; «در حال همگامسازی آخرین وضعیت…» while current run snapshot loads | None during snapshot; safe retry on failure | Announce snapshot recovery outcome once |
|
||||||
|
| Unauthorized/session ended | Merchant data removed from active view and navigation returns to login | «ورود دوباره» | Focus lands on login heading; generic session-ended message |
|
||||||
|
| Forbidden/not found | No other-merchant existence detail; safe boundary message | Return to authorized overview | Page title and error are announced; no leaked identifier |
|
||||||
|
|
||||||
|
### 10.3 Stale/freshness language
|
||||||
|
|
||||||
|
The frontend does not label data stale merely because a Redis entry is old or its 30-day TTL is approaching. Revisioned source/cohort keys and metric-definition version determine reuse. “Stale” appears only if a future approved interface returns an explicit stale/quality state. During SSE interruption, ready data is labeled with its calculation timestamp and connection recovery state, not silently called current or stale.
|
||||||
|
|
||||||
|
### 10.4 Event ordering and duplication
|
||||||
|
|
||||||
|
- Section updates are keyed by stable section id and event id; duplicate replay events do not duplicate cards, insights, or announcements.
|
||||||
|
- A later non-ready event cannot erase a valid ready section unless an approved new run/period is visibly active.
|
||||||
|
- Completion order never reorders the planned page hierarchy.
|
||||||
|
- Automatic worker retry counts are not exposed as merchant actions; the section remains calculating until exhausted.
|
||||||
|
- Manual retry targets only the failed section and never clears ready siblings.
|
||||||
|
|
||||||
|
## 11. Charts, tables, and insight behavior
|
||||||
|
|
||||||
|
### 11.1 Universal chart contract
|
||||||
|
|
||||||
|
Every chart card contains, in reading order:
|
||||||
|
|
||||||
|
1. Heading.
|
||||||
|
2. Exact takeaway.
|
||||||
|
3. Period, comparison, and unit.
|
||||||
|
4. Plot with direct labels where practical.
|
||||||
|
5. Legend only if direct labels cannot carry the meaning.
|
||||||
|
6. Visible control «نمایش جدول داده» and a semantic tabular alternative.
|
||||||
|
7. Sample, coverage, calculation time, limitation, and definition link.
|
||||||
|
|
||||||
|
Chart rules:
|
||||||
|
|
||||||
|
- Persian digits on merchant-facing axes, labels, tooltips, and tables.
|
||||||
|
- Rial only for money, explicitly labeled «ریال»; never Toman.
|
||||||
|
- Tooltips are keyboard reachable but never the sole source of an important value.
|
||||||
|
- Series use color plus line style, point shape, pattern, or direct text label.
|
||||||
|
- Ordered measures use ordered visual encoding; no rainbow palette.
|
||||||
|
- No 3D charts, gauges, decorative donuts, or unlabeled dual axes.
|
||||||
|
- Plot surfaces and all series meet the `DESIGN.md` light/dark contrast contract.
|
||||||
|
- Empty/unavailable/suppressed/failed states replace, rather than fake, a zero line.
|
||||||
|
- Chart height is reserved while loading; mobile plots remain at least 240 px tall.
|
||||||
|
|
||||||
|
### 11.2 Table behavior
|
||||||
|
|
||||||
|
- Use true table semantics for tabular alternatives and desktop detail views.
|
||||||
|
- Headers identify unit and sort state; sorting is server-owned for paginated details.
|
||||||
|
- Numeric cells use tabular numerals and align consistently for RTL scanning.
|
||||||
|
- The first visible column on the right carries the row's semantic identity/status; technical identifiers do not dominate.
|
||||||
|
- Page size follows the approved API; the client does not fetch hidden rows to simulate pagination.
|
||||||
|
- More than 50 rendered rows requires an approved pagination/virtualization strategy, but virtualization must preserve headers, focus, and screen-reader usability.
|
||||||
|
- Empty strings, missing values, and unavailable values use explicit localized labels rather than blank cells or em dashes with ambiguous meaning.
|
||||||
|
|
||||||
|
### 11.3 Status vocabulary
|
||||||
|
|
||||||
|
| Data meaning | Persian display | Prohibited interpretation |
|
||||||
|
|---|---|---|
|
||||||
|
| `Verified` | تأییدشده | — |
|
||||||
|
| `Paid` | پرداختشده، در انتظار تأیید پذیرنده | ناموفق |
|
||||||
|
| `Failed` | ناموفق | A specific cause unless the source proves it |
|
||||||
|
| `Reversed` | برگشتخورده | Successful/finalized |
|
||||||
|
| `NoAttempt` | بدون تلاش پرداخت | A failed attempt |
|
||||||
|
| `InBank` | رسیده به درگاه بانکی | ردشده توسط بانک |
|
||||||
|
| Undefined rate | نرخ قابل محاسبه نیست | `۰٪` |
|
||||||
|
| No rows | دادهای در این بازه وجود ندارد | System failure |
|
||||||
|
| Peer suppression | داده همگروه کافی نیست | Merchant underperformance |
|
||||||
|
|
||||||
|
## 12. Persian RTL content and formatting
|
||||||
|
|
||||||
|
### 12.1 Language and direction
|
||||||
|
|
||||||
|
- Root document: `lang="fa" dir="rtl"`.
|
||||||
|
- Persian copy uses Persian `ی` and `ک`, correct نیمفاصله, and Persian punctuation.
|
||||||
|
- Copy is professional and plain, not bureaucratic or mechanically translated.
|
||||||
|
- Complete Persian messages replace concatenated fragments, especially around counts, dates, units, and errors.
|
||||||
|
- Layout order follows RTL reading; icon placement and directional arrows are mirrored only when their meaning is directional.
|
||||||
|
|
||||||
|
### 12.2 Bidi isolation
|
||||||
|
|
||||||
|
The following remain canonical and use isolated LTR direction where rendered:
|
||||||
|
|
||||||
|
- Session/run/event ids.
|
||||||
|
- URLs, hashes, filenames, error reference ids.
|
||||||
|
- API field names and metric canonical ids on the definitions page.
|
||||||
|
- Demo usernames such as `merchant-0001`.
|
||||||
|
|
||||||
|
Do not allow a Latin identifier to reorder adjacent Persian punctuation or status text. Technical identifiers use `translate="no"` where automatic translation could corrupt them.
|
||||||
|
|
||||||
|
### 12.3 Digits, dates, and units
|
||||||
|
|
||||||
|
- Merchant-facing numbers use Persian digits through `fa-IR` locale-aware formatting.
|
||||||
|
- Money is always Rial with explicit «ریال» text. Do not use currency symbols alone and do not convert to Toman.
|
||||||
|
- Merchant-facing dates are Jalali in `Asia/Tehran`; calculation timestamps include enough context to avoid ambiguity.
|
||||||
|
- API instants and technical export metadata remain canonical ISO-8601 UTC only where the approved interface needs machine readability; the UI also shows the equivalent merchant-facing Jalali/Tehran value.
|
||||||
|
- Percentages state the denominator/sample nearby. Percentage-point change is labeled «واحد درصد», not confused with percent relative change.
|
||||||
|
- Tables/KPIs use tabular numerals where supported by Iran Yekan X Professional FaNum.
|
||||||
|
|
||||||
|
### 12.4 Typography
|
||||||
|
|
||||||
|
- Approved family: licensed Iran Yekan X Professional FaNum variable webfont, `IRANYekanXVFaNumVF.woff2`, weights 100–1000.
|
||||||
|
- The reviewed font bytes and required license are tracked at `static/fonts/IRANYekanXVFaNumVF.woff2` and `static/fonts/FontLicense.txt`; implementation uses these project assets without a runtime or build-time CDN download.
|
||||||
|
- Product weights: 400 body/value, 500 labels/controls, 600 emphasis, 700 headings; avoid ultralight and unnecessary bold.
|
||||||
|
- Use the 400–700 range in normal dashboard UI even though the file supports a wider axis.
|
||||||
|
- Fallback: Tahoma/system sans-serif for resilience only, never treated as visual acceptance baseline.
|
||||||
|
- Reserve metrics and long Persian labels for fallback-font variation to avoid clipping/layout shift.
|
||||||
|
- Do not rename, subset, regenerate, or add other files from the purchased package without updating `DESIGN.md` and confirming license compliance.
|
||||||
|
|
||||||
|
## 13. Light and dark themes
|
||||||
|
|
||||||
|
- Light and dark are equal acceptance targets using the semantic roles in `DESIGN.md`; components do not embed raw theme-specific colors.
|
||||||
|
- Initial mode follows system preference unless a presentation-only explicit choice exists locally.
|
||||||
|
- Theme is applied before first paint; no light/dark flash is acceptable.
|
||||||
|
- The theme control exposes current state and destination in its accessible name, for example «پوسته روشن فعال است؛ تغییر به پوسته تیره».
|
||||||
|
- Ready, pending, error, warning, suppressed, disabled, focus, chart series, scrim, and skeleton states each have verified values in both themes.
|
||||||
|
- Native controls and page metadata match the active color scheme.
|
||||||
|
- A theme change preserves page, filters, focus, scroll position, and loaded analytics; it never starts a new analytics run.
|
||||||
|
- Status/trend meaning never depends on light/dark hue changes alone.
|
||||||
|
|
||||||
|
## 14. CSV export for finance/legal handoff
|
||||||
|
|
||||||
|
### 14.1 Eligibility
|
||||||
|
|
||||||
|
CSV export appears only on an approved merchant-owned detail view whose backend interface authorizes export. MVP candidates are unverified-paid, failed, and retried session details. Overview cards, fee aggregate cards, definitions, and all peer comparison surfaces do not expose export unless separately approved.
|
||||||
|
|
||||||
|
The action is absent—not merely disabled—when the user lacks an eligible detail context. Client visibility is not authorization; every generation/download request is reauthorized against the server session and current merchant boundary.
|
||||||
|
|
||||||
|
### 14.2 Preflight summary
|
||||||
|
|
||||||
|
Selecting «دریافت CSV» opens an inline panel or dialog with:
|
||||||
|
|
||||||
|
- Detail type.
|
||||||
|
- Active merchant display name.
|
||||||
|
- Exact selected Jalali/Tehran period.
|
||||||
|
- Applied approved filters and sort-independent export scope.
|
||||||
|
- Known result count or a statement that the server will determine it.
|
||||||
|
- Money unit: Rial.
|
||||||
|
- Data notice: «فایل فقط شامل ردیفها و فیلدهای مجاز همین نمای جزئیات است و اطلاعات پرداختکننده یا همگروهها را ندارد.»
|
||||||
|
- Primary action «ساخت فایل CSV» and cancel action.
|
||||||
|
|
||||||
|
This is a scope confirmation, not a destructive confirmation. Focus returns to the export trigger when dismissed.
|
||||||
|
|
||||||
|
### 14.3 Generation and download states
|
||||||
|
|
||||||
|
| State | UX behavior |
|
||||||
|
|---|---|
|
||||||
|
| Ready to request | Show scope summary; generation action enabled |
|
||||||
|
| Requesting | «در حال ثبت درخواست…»; prevent duplicate request |
|
||||||
|
| Generating | Keep detail table usable; show «فایل در حال آمادهسازی است…» with period/unit; do not imply browser-side row assembly |
|
||||||
|
| Ready | Show «فایل آماده است» plus explicit «دانلود CSV»; download is tied to the authorized artifact/response |
|
||||||
|
| Empty | «برای فیلترهای انتخابشده ردیفی برای خروجی وجود ندارد.»; no blank misleading file unless interface explicitly returns one |
|
||||||
|
| Rate-limited | Preserve table and generation context; honor `Retry-After`; no retry loop |
|
||||||
|
| Failed/recoverable | Safe reason category and «تلاش دوباره» for the same approved scope |
|
||||||
|
| Session ended | Cancel access to artifact, remove ready download action, return to login; re-login does not silently resume old download |
|
||||||
|
| Forbidden/not found | Do not confirm another merchant/artifact exists; return to authorized detail context |
|
||||||
|
| Filters changed | Invalidate the prior preflight scope; require a new explicit generation request for current filters |
|
||||||
|
|
||||||
|
### 14.4 Export content boundary
|
||||||
|
|
||||||
|
- The exported row set reconciles to the same authorized filtered detail query represented in the UI, independent of visual pagination.
|
||||||
|
- The backend owns schema, row authorization, escaping, and generation. The browser never exports hidden cached rows.
|
||||||
|
- No payer token, peer identity/value/row, credential, cookie, raw session token, internal cache/job key, or field absent from the authorized detail contract appears.
|
||||||
|
- Money columns identify Rial explicitly. Status and stage values preserve the approved lifecycle meaning.
|
||||||
|
- File and UI metadata make the selected period and generation time unambiguous.
|
||||||
|
- CSV encoding, headers, canonical date representation, filename convention, retention, audit event, maximum row count, and delivery mechanism remain interface/product approval questions; this UX spec does not silently invent them.
|
||||||
|
|
||||||
|
### 14.5 Handoff guidance
|
||||||
|
|
||||||
|
After successful download, show a non-blocking notice: «فایل شامل دادههای عملیاتی بازرگان است. آن را فقط از مسیر مورد تأیید تیم مالی یا حقوقی بهاشتراک بگذارید.» The product does not implement email, cloud sharing, or legal attestation in MVP.
|
||||||
|
|
||||||
|
## 15. Accessibility specification
|
||||||
|
|
||||||
|
Target: WCAG 2.2 AA at all acceptance viewports and in both themes.
|
||||||
|
|
||||||
|
Normative basis: [WCAG 2.2](https://www.w3.org/TR/WCAG22/). The focus-obscuring and accessible-authentication requirements follow the W3C's [WCAG 2.2 additions](https://www.w3.org/WAI/standards-guidelines/wcag/new-in-22/); restrained progress announcements follow the W3C technique for [`role="status"`](https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA22). These references guide implementation and testing but do not override the product contracts in section 1.
|
||||||
|
|
||||||
|
### 15.1 Structure and navigation
|
||||||
|
|
||||||
|
- One `h1` per page and hierarchical section headings.
|
||||||
|
- A first-focus skip link targets main content.
|
||||||
|
- Landmarks distinguish navigation, utility header, main content, and page footer/status if present.
|
||||||
|
- Navigation uses links; actions use buttons; clickable rows/divs are prohibited.
|
||||||
|
- Heading anchors account for fixed/sticky header offset.
|
||||||
|
|
||||||
|
### 15.2 Keyboard and focus
|
||||||
|
|
||||||
|
- Every function works with keyboard only, including filters, theme, drawer, chart table toggle, pagination, row disclosure, export, retry, and logout.
|
||||||
|
- Visible `:focus-visible` uses the approved focus token; no outline removal without an equivalent.
|
||||||
|
- Fixed headers, drawers, sticky actions, banners, and safe-area padding never fully obscure focus.
|
||||||
|
- Opening a drawer/dialog moves focus to a meaningful control; closing restores the invoker.
|
||||||
|
- Async section completion never steals focus or resets a user's position.
|
||||||
|
- No animation is required to understand focus, state, or completion.
|
||||||
|
|
||||||
|
### 15.3 Forms and authentication
|
||||||
|
|
||||||
|
- Visible labels, programmatic associations, meaningful names, correct autocomplete, and inline errors.
|
||||||
|
- On invalid submit, focus the first invalid field or an error summary that links to it.
|
||||||
|
- Password paste/password managers remain supported.
|
||||||
|
- Error wording states the next step without revealing account existence.
|
||||||
|
- Touch targets are at least 44×44 px on touch layouts, with no dead zone between label and control.
|
||||||
|
|
||||||
|
### 15.4 Async announcements
|
||||||
|
|
||||||
|
- Use one restrained atomic polite status for overall analytics progress/connection state.
|
||||||
|
- Section errors are programmatically associated with their headings; urgent session termination may use an assertive announcement once.
|
||||||
|
- Do not announce heartbeats, skeleton animation, every percentage/count change, or countdown seconds.
|
||||||
|
- Toasts supplement visible durable state; they never carry the only error/success message.
|
||||||
|
|
||||||
|
### 15.5 Charts and tables
|
||||||
|
|
||||||
|
- Every chart has a concise textual takeaway and a semantic table alternative.
|
||||||
|
- Important values remain available without hover, pointer precision, color, or visual inspection.
|
||||||
|
- Keyboard focus can reveal the same point value as pointer hover where point interaction exists.
|
||||||
|
- Series/state distinctions use labels/shapes/patterns in addition to hue.
|
||||||
|
- Table captions, headers, sorting state, pagination labels, and row disclosure names are programmatic.
|
||||||
|
|
||||||
|
### 15.6 Zoom, reflow, and motion
|
||||||
|
|
||||||
|
- Do not disable browser zoom.
|
||||||
|
- At 200% zoom and 320 CSS px width, core tasks reflow without two-dimensional scrolling; a labeled data-table region may scroll horizontally when essential.
|
||||||
|
- Respect `prefers-reduced-motion`: remove drawer/chart positional movement and keep only immediate or short opacity/color feedback needed for comprehension.
|
||||||
|
- No autoplay or dashboard-wide entrance sequence.
|
||||||
|
|
||||||
|
## 16. Responsive behavior matrix
|
||||||
|
|
||||||
|
| Concern | Mobile <768 px | Tablet 768–1023 px | Desktop ≥1024 px |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Navigation | Right drawer; compact header | Right drawer | Fixed right sidebar |
|
||||||
|
| Main gutter | 16 px | 20–24 px | 24–32 px |
|
||||||
|
| KPI layout | One column | One or two by content fit | Four headline cards in one row when space permits |
|
||||||
|
| Filter bar | Disclosure/full-width controls; explicit Apply | Two rows | One quiet row where content fits |
|
||||||
|
| Charts | ≥240 px; exact value first; table toggle | Full width or two-column by fit | 8/4 or full 12-column |
|
||||||
|
| Detail table | Priority row summary + disclosure; optional labeled horizontal table | Priority columns; controlled horizontal scroll | Full semantic table |
|
||||||
|
| Export preflight | Full-screen dialog/sheet respecting safe areas | Modal/inline panel | Modal/inline panel near action |
|
||||||
|
| Insights | Stack after relevant KPI | Stack or companion column | Companion 4-column panel where useful |
|
||||||
|
| Merchant identity | Two-line compact context | Compact context | Full sidebar block |
|
||||||
|
| Theme control | Accessible icon+name in drawer/header | Header/drawer | Utility area |
|
||||||
|
|
||||||
|
Acceptance widths: 360 px, 768 px, 1024 px, and 1440 px; include 320 CSS px reflow/zoom verification.
|
||||||
|
|
||||||
|
## 17. Content catalogue for system states
|
||||||
|
|
||||||
|
All strings are complete messages; variable insertion must preserve Persian grammar and bidi isolation.
|
||||||
|
|
||||||
|
| Situation | Approved pattern |
|
||||||
|
|---|---|
|
||||||
|
| Overall progress | «{آماده} از ۴ بخش تحلیل آماده است.» |
|
||||||
|
| Calculating | «این بخش در حال محاسبه است…» |
|
||||||
|
| Recovering SSE | «ارتباط با تحلیل قطع شد؛ در حال بازیابی اتصال…» |
|
||||||
|
| Snapshot fallback | «در حال همگامسازی آخرین وضعیت تحلیل…» |
|
||||||
|
| Section failure | «محاسبه این بخش انجام نشد. میتوانید فقط همین بخش را دوباره اجرا کنید.» |
|
||||||
|
| Rate limited | «درخواستهای زیادی ثبت شده است. پس از {زمان} دوباره تلاش کنید.» |
|
||||||
|
| Baseline unavailable | «مبنای مقایسه برای این بازه در دسترس نیست.» |
|
||||||
|
| Undefined rate | «بهدلیل نبود مخرج معتبر، این نرخ قابل محاسبه نیست.» |
|
||||||
|
| Empty period | «در بازه انتخابشده دادهای برای این شاخص وجود ندارد.» |
|
||||||
|
| Session ended | «نشست شما پایان یافته است. برای ادامه دوباره وارد شوید.» |
|
||||||
|
| Safe forbidden | «به این اطلاعات دسترسی ندارید. به پیشخوان حساب خود برگردید.» |
|
||||||
|
| Export ready | «فایل CSV برای بازه انتخابشده آماده است.» |
|
||||||
|
| Export failed | «ساخت فایل انجام نشد. میتوانید همین درخواست را دوباره ثبت کنید.» |
|
||||||
|
|
||||||
|
Persian digits are applied to rendered variable values. Technical identifiers inside an error/support reference remain Latin and isolated LTR.
|
||||||
|
|
||||||
|
## 18. Privacy and security UX checks
|
||||||
|
|
||||||
|
- Merchant identity is display context, never an editable authorization field.
|
||||||
|
- Changing route/query/form/export parameters cannot reveal another merchant; UI errors remain non-enumerating.
|
||||||
|
- Logout/session expiry removes rendered merchant analytics before login is shown.
|
||||||
|
- Ready analytics and export download actions are not persisted in browser storage across authentication contexts.
|
||||||
|
- Session cookies and CSRF values are never shown, logged, copied, or treated as app state.
|
||||||
|
- Peer results contain only approved aggregates/suppression metadata; no click path leads to peers.
|
||||||
|
- Payer tokens never appear in DOM, accessibility text, tooltips, URLs, CSV, telemetry, or support references.
|
||||||
|
- Export warnings describe operational sensitivity without claiming legal compliance not established by policy.
|
||||||
|
|
||||||
|
## 19. UX acceptance scenarios
|
||||||
|
|
||||||
|
### 19.1 Authentication and demo switching
|
||||||
|
|
||||||
|
- Two seeded accounts authenticate separately and display distinct merchant identities/data.
|
||||||
|
- Invalid and disabled accounts produce indistinguishable user-facing failures.
|
||||||
|
- Login throttling honors `Retry-After`, supports paste/password managers, and never permanently locks through the UI.
|
||||||
|
- A judge switches merchant demonstrations only through logout then a new login; no in-session tenant selector exists.
|
||||||
|
- Expiry/revocation ends dashboard and SSE access immediately and returns a generic accessible login message.
|
||||||
|
|
||||||
|
### 19.2 Analytics delivery
|
||||||
|
|
||||||
|
- All four section frames appear in stable order; completion in any order does not shift or reorder them.
|
||||||
|
- A ready overview remains usable while fees calculate, peer comparison is suppressed, and payment performance fails.
|
||||||
|
- Automatic retries remain “calculating”; a manual retry appears only after exhaustion and targets one section.
|
||||||
|
- Heartbeat loss enters controlled connection recovery without clearing ready data or announcing every reconnect attempt.
|
||||||
|
- Replay recovery deduplicates final sections; expired replay falls back to an authorized current snapshot.
|
||||||
|
- Rate limiting preserves ready content, waits for the server-defined interval, and starts no tight retry loop.
|
||||||
|
|
||||||
|
### 19.3 Metric semantics
|
||||||
|
|
||||||
|
- Bank-paid count/amount and success include both `Paid` and `Verified`; verification completion separates them.
|
||||||
|
- Unverified Paid uses actionable-gap language but never failure styling/copy.
|
||||||
|
- Fee totals remain session-grain and unchanged by additional attempts.
|
||||||
|
- Retry recovery accepts eventual `Paid` or `Verified`.
|
||||||
|
- Timing shows p50, p95, sample, and non-null coverage; missing values never become zero.
|
||||||
|
- `NoAttempt` and `InBank` remain observed stages without invented failure causes.
|
||||||
|
- Below-threshold trends remain metric comparisons but never become actionable insight cards.
|
||||||
|
|
||||||
|
### 19.4 Peer privacy
|
||||||
|
|
||||||
|
- Eligible results show merchant-weighted median and higher-is-better percentile for approved metrics only.
|
||||||
|
- Lower-is-better processing time/fee rate uses inverted percentile direction and an explicit explanation.
|
||||||
|
- Under-threshold cohorts show the suppression copy and no peer values/rows.
|
||||||
|
- URL, client state, chart interaction, table alternative, and export cannot reveal peer identities or individual values.
|
||||||
|
|
||||||
|
### 19.5 Export
|
||||||
|
|
||||||
|
- Export is available only from an approved merchant-owned detail view and repeats the current filter/period/unit scope before generation.
|
||||||
|
- Export reconciles to the authorized filtered detail query beyond the currently visible page.
|
||||||
|
- Filter changes invalidate an earlier preflight; session expiry invalidates download access.
|
||||||
|
- No payer token, peer data, credentials, internal ids outside the approved detail schema, or other-merchant rows appear.
|
||||||
|
- Empty, generating, ready, rate-limited, failed, unauthorized, and forbidden states are distinct and keyboard/screen-reader operable.
|
||||||
|
|
||||||
|
### 19.6 RTL, themes, and accessibility
|
||||||
|
|
||||||
|
- Merchant-facing digits are Persian, money is Rial only, dates are Jalali/Tehran, and technical ids remain bidi-safe.
|
||||||
|
- Iran Yekan X Professional FaNum variable typography is verified using the Git-tracked licensed asset and adjacent license file; fallback resilience does not replace baseline review.
|
||||||
|
- Light and dark modes pass contrast/focus/chart/state review without first-paint flash and without refetching analytics.
|
||||||
|
- Login, drawer, filters, charts, tables, pagination, row disclosures, retries, export, theme, and logout are keyboard complete.
|
||||||
|
- Screen readers receive page structure, exact KPI meaning, chart alternatives, restrained async status, and associated errors.
|
||||||
|
- 360/768/1024/1440 px layouts and 320 CSS px reflow preserve core tasks; reduced-motion mode removes nonessential movement.
|
||||||
|
|
||||||
|
## 20. Implementation handoff checklist (after approval only)
|
||||||
|
|
||||||
|
- Obtain approved machine-readable analytics, identity, detail, SSE, error, and export contracts.
|
||||||
|
- Resolve static adapter/Nginx fallback behavior.
|
||||||
|
- Verify that local, CI, and Docker builds consume the tracked FaNum variable font and retain its adjacent license file without external downloads.
|
||||||
|
- Create implementation tasks for shell, formatting, authentication, filters, run/SSE state model, each canonical section, detail tables, export, themes, and tests.
|
||||||
|
- Build contract fixtures containing no sensitive source data, payer tokens, credentials, or peer rows.
|
||||||
|
- Verify formatting, lint, strict typing, `svelte-check`, unit/component tests, Playwright critical flows, production build, responsive RTL, both themes, accessibility, and reduced motion.
|
||||||
|
|
||||||
|
No implementation work begins until the dashboard capability and this UX specification are explicitly approved.
|
||||||
|
|
||||||
|
## 21. Remaining approval questions
|
||||||
|
|
||||||
|
1. What is the approved CSV interface contract: eligible detail types, exact columns/headers, UTF-8/BOM policy, canonical date fields, filename, row cap, synchronous versus asynchronous delivery, retention, and audit event?
|
||||||
|
2. Should CSV preflight be a modal dialog on desktop or an inline disclosure, provided the mobile experience remains a focus-managed full-screen dialog/sheet?
|
||||||
|
3. Which non-sensitive merchant display field is authoritative for the shell/export preflight when the demo account username and merchant business name differ?
|
||||||
|
4. Is a downloadable chart-data table required in MVP, or is a visible semantic table alternative sufficient while CSV remains limited to authorized detail views?
|
||||||
Reference in New Issue
Block a user