Files
sase.tr/MANIFEST.md
Fusion 759e5c8e9a feat(FN-400): Phase-1 selector & route manifest for FN-367/FN-368
Commits merged:
- feat(FN-400): Phase-1 selector & route manifest for FN-367/FN-368

Files changed:
MANIFEST.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

Fusion-Task-Id: FN-400
2026-05-16 11:15:24 +03:00

115 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FN-400 — Phase-1 Selector & Route Manifest
**Scope:** sase.tr web app (`apps/web`), current `main` (commit `08dbde7`).
**Source of truth for FN-367:** commit `e274e02` (`feat(FN-367): add inline retry affordance after VIN decode failure`).
**Source of truth for FN-368:** commit `0ce68a7` (`feat(FN-368): parts panel loading state, inert unavailable rows, escape hatch`).
**Convention note:** This codebase does **not** use `data-testid`. Testability hooks are `data-faro-user-action-name="…"` plus semantic ARIA (`role`, `aria-live`, `aria-disabled`). All selectors below were re-verified against the shipped tree.
---
## FN-367 — VIN decode failure → rage clicks
### Route(s)
- `css=/dashboard/search` — file: `apps/web/src/routes/dashboard/search.tsx:46` (`createFileRoute("/dashboard/search")`). VIN decode is triggered exclusively from this route. Successful decode navigates to `/dashboard/vehicles/$id` (`search.tsx:179`).
### Input selector (VIN field)
- `css=input[maxlength="17"]` — file: `apps/web/src/routes/dashboard/search.tsx:333-358`.
- Stable accessor (preferred): `role=textbox` with placeholder `"Şase numarasını girin (17 karakter)"` (`search.tsx:334`).
- **No `data-testid`, no `data-faro-user-action-name` on the input.** Use the placeholder or `role=textbox`.
### Submit / decode trigger
- `text="Şase Çöz"` button (type=submit) — file: `apps/web/src/routes/dashboard/search.tsx:399-409`.
- Stable accessor: `role=button` with name `"Şase Çöz"`.
- Form submit handler: `handleSearch` (`search.tsx:204`).
- **No `data-faro-user-action-name` on the submit button.**
### Error / failure-state selector (the element a user would rage-click)
- `role="alert"` + `aria-live="assertive"` on the error banner — file: `apps/web/src/routes/dashboard/search.tsx:412-471` (attributes at lines `414` and `415`).
- Title text inside the banner: `"Şase çözümlenemedi"` (`search.tsx:421`).
- Introduced/touched by FN-367 (commit `e274e02`). Confirmed present in current tree.
### Retry affordance selector (introduced by FN-367)
- `data-faro-user-action-name="vin-decode-retry"` — file: `apps/web/src/routes/dashboard/search.tsx:446`.
- Button text: `"Tekrar Dene"` (`search.tsx:453`).
- Rendered conditionally: only when `error` does **not** contain the substrings `"abone olun"` (subscription wall) or `"tanınamadı"` (unknown VIN — that branch shows a different "report to admin" CTA instead). See guard at `search.tsx:443`.
- Handler: `handleRetry` (`search.tsx:189`), which fires PostHog event `vin_decode_retry_clicked` (`search.tsx:193`).
### data-testid hooks added by FN-367
- **NOT PRESENT IN CURRENT CODE.** FN-367 did not add `data-testid` attributes. The verification-relevant hooks it added are: (1) `data-faro-user-action-name="vin-decode-retry"` on the retry button (`search.tsx:446`), (2) `role="alert"` + `aria-live="assertive"` on the error banner (`search.tsx:414-415`). The vitest test file `apps/web/src/routes/__tests__/dashboard-search.test.tsx` queries by role/name, not by testid — Playwright should follow the same pattern.
### Other useful hooks on this route
- `data-faro-user-action-name="fill-example-vin"` — example-VIN helper button (`search.tsx:390`).
- `data-faro-user-action-name="select-history-vin"` — history-row buttons (`search.tsx:560`).
---
## FN-368 — Parts panel → rage clicks → abandonment
### Route(s)
The parts panel is rendered indirectly via `<SchemaViewer>`, which is mounted on four category-level routes (all four are in scope; the "vehicle detail page" referenced in the spec is the first):
- `css=/dashboard/vehicles/$id/categories/$categoryId` — file: `apps/web/src/routes/dashboard/vehicles_/$id/categories_/$categoryId.tsx:157-158`. **Primary post-VIN-decode parts surface.**
- `css=/dashboard/catalog/$brandName/$modelId/categories/$categoryId` — file: `apps/web/src/routes/dashboard/catalog_/$brandName_/$modelId/categories_/$categoryId.tsx:224-225`.
- `css=/dashboard/catalog/emex/$catalogCode/$vehicleId/groups/$groupId` — file: `apps/web/src/routes/dashboard/catalog_/emex/$catalogCode_/$vehicleId_/groups/$groupId.tsx:83`.
- `css=/dashboard/catalog/pcat/$catalogId/$modelId/$carId/groups/$groupId` — file: `apps/web/src/routes/dashboard/catalog_/pcat/$catalogId_/$modelId_/$carId_/groups/$groupId.tsx:100`.
`SchemaViewer` then renders `<PartsPanel>` at `apps/web/src/components/schema/schema-viewer.tsx:100` (no-schema fallback) and `:168` (split-pane right column).
### Parts panel container selector
- `text="Parcalar"` — the heading inside the container header — file: `apps/web/src/components/schema/parts-panel.tsx:95` (loading state) and `:112` (loaded state).
- Stable accessor: `role=heading` with name `"Parcalar"`.
- **No dedicated container `data-testid` or `data-faro-user-action-name` on the wrapper `<div>`.** Use the heading text to scope locators.
### Part category / part item selector (the elements users were rage-clicking pre-fix)
- `data-faro-user-action-name="select-part"` on each part row — file: `apps/web/src/components/schema/parts-panel.tsx:146`.
- Available rows: `role="button"` + `tabIndex={0}` (`parts-panel.tsx:148-149`).
- Unavailable rows (the inert, post-fix state that visually replaces the rage-clicked target): `aria-disabled="true"` + `tabIndex={-1}` + stripped click/keydown handlers (`parts-panel.tsx:147-149`, with handler-strip logic at `:166-181`).
- Visual hint: `className` includes `opacity-40` on unavailable rows (`parts-panel.tsx:158`).
### Loading / skeleton state selector
- `text="Yukleniyor..."` (note: no diacritics in source) inside the panel header during load — file: `apps/web/src/components/schema/parts-panel.tsx:96`.
- Skeleton rows are 8× `<Skeleton>` elements rendered from `SKELETON_ROW_KEYS` (`parts-panel.tsx:18`, mapped at `:101-103`).
- Stable accessor: scope to panel by `role=heading name=Parcalar`, then assert sibling `text="Yukleniyor..."`.
- Introduced by FN-368 (the entire `if (isLoading)` block at `parts-panel.tsx:89-106` is new in commit `0ce68a7`).
### Empty / error state selector
- Empty state: `text="Bu kategori icin parca bulunamadi."` — file: `apps/web/src/components/schema/parts-panel.tsx:119`.
- Escape-hatch button (introduced by FN-368): `text="Geri don"` — file: `apps/web/src/components/schema/parts-panel.tsx:121` (`<Button … onClick={() => window.history.back()}>Geri don</Button>`).
- Stable accessor for escape hatch: `role=button` name `"Geri don"`.
- **No dedicated error state.** The panel does not render a server-error branch — upstream `use-parts` failures bubble to the page-level loading skeleton in `SchemaViewer` (`schema-viewer.tsx:69-83`). Flag for verification author: if FN-368 testing needs an explicit error UI, it does not exist in current code.
### data-testid hooks added by FN-368
- **NOT PRESENT IN CURRENT CODE.** FN-368 did not add `data-testid` attributes. The verification-relevant hooks it added are:
- `data-faro-user-action-name="select-part"` on every row (`parts-panel.tsx:146`).
- `aria-disabled="true"` on unavailable rows (`parts-panel.tsx:147`).
- `role="button"` + `tabIndex={0}` on available rows; `tabIndex={-1}` on unavailable rows (`parts-panel.tsx:148-149`).
- Loading-state branch with `"Yukleniyor..."` text + skeleton rows (`parts-panel.tsx:89-106`).
- "Geri don" escape-hatch button in empty state (`parts-panel.tsx:120-123`).
The companion vitest spec `apps/web/src/components/schema/__tests__/parts-panel.test.tsx` (added by FN-368) queries by `role` / accessible-name / text — Playwright should match that pattern.
---
## Drift check
| Selector | In commit | In current `main` | Status |
|---|---|---|---|
| `role="alert"` + `aria-live="assertive"` on VIN error banner | `e274e02` `search.tsx` | `search.tsx:414-415` | ✅ unchanged |
| `data-faro-user-action-name="vin-decode-retry"` | `e274e02` `search.tsx` | `search.tsx:446` | ✅ unchanged |
| `"Tekrar Dene"` button text | `e274e02` | `search.tsx:453` | ✅ unchanged |
| `data-faro-user-action-name="select-part"` | `0ce68a7` `parts-panel.tsx` | `parts-panel.tsx:146` | ✅ unchanged |
| `aria-disabled="true"` on unavailable rows | `0ce68a7` | `parts-panel.tsx:147` | ✅ unchanged |
| `"Yukleniyor..."` loading text | `0ce68a7` | `parts-panel.tsx:96` | ✅ unchanged |
| `"Geri don"` empty-state escape hatch | `0ce68a7` | `parts-panel.tsx:121` | ✅ unchanged |
No drift between the FN-367 / FN-368 PRs and current shipped code on `main` as of `08dbde7`.
---
## Recommended Playwright locator priority (per project convention)
1. `[data-faro-user-action-name="…"]` — preferred where present.
2. `getByRole(role, { name })``role=alert`, `role=button`, `role=heading`, `role=textbox`.
3. `getByText("…")` / `getByPlaceholder("…")` — last resort for elements without (1) or (2).
Do **not** request new `data-testid` attributes unless a target has none of the above; that would force a code change purely for testability and conflicts with the established convention.