79 lines
5.7 KiB
Markdown
79 lines
5.7 KiB
Markdown
# Preview authoring guide (design-sync, tradein-mvp-frontend)
|
||
|
||
You author `.design-sync/previews/<Name>.tsx` for assigned components so their preview cards
|
||
render real, styled, plausible compositions. The bundle is already built; you only recompile
|
||
your own previews.
|
||
|
||
## Import contract (IMPORTANT)
|
||
- Import the component from the package name: `import { OfferCard } from 'tradein-mvp-frontend';`
|
||
(the build maps this to the shipped bundle global — do NOT import from a relative src path).
|
||
- Import realistic data from the shared fixtures: `import { FIXTURE_ESTIMATE } from './_fixtures';`
|
||
- Type-only imports (`import type {...} from '@/types/trade-in'`) are erased — fine to use for casts.
|
||
- A provider is ALREADY configured globally (the app's `Providers` = TanStack Query). You do NOT
|
||
wrap previews in QueryClientProvider yourself (a second copy breaks context identity).
|
||
|
||
## Shared fixtures available from `./_fixtures` (realistic ЕКБ 2-к secondary)
|
||
- `FIXTURE_ESTIMATE: AggregatedEstimate` — median 9.85M ₽, range 9.1–10.6M, confidence high,
|
||
analogs[3], actual_deals[2], cian_valuation, avito_imv, dkp_corridor, price_trend[6]. Covers any
|
||
`estimate: AggregatedEstimate` prop.
|
||
- `FIXTURE_INPUT: TradeInEstimateInput` — the quartira params (area 55.3, 2 rooms, floor 5/11, monolith, good).
|
||
- `FIXTURE_IMV: IMVBenchmarkResponse` — for IMVBenchmark.
|
||
- `FIXTURE_HOUSES: HouseInfoForEstimate[]` — for HouseInfoCard.
|
||
- `FIXTURE_PLACEMENT: PlacementHistoryItem[]` — placement history rows.
|
||
- `FIXTURE_ANALYTICS: HouseAnalyticsResponse` — kpi + price_history[4] + recent_sold[2].
|
||
- `FIXTURE_SELLTIME: SellTimeSensitivityResponse` — exposure-vs-premium buckets.
|
||
- `FIXTURE_SALES: SalesVsListingsResponse` — street deals/listings pairs.
|
||
Read `tradein-mvp/frontend/src/app/ui-preview/estimate/page.tsx` — it shows EXACT prop usage for
|
||
many components (the canonical "hero" composition). Port it.
|
||
|
||
## How to find props
|
||
Read each component's source `tradein-mvp/frontend/src/components/<group>/<Name>.tsx`. The `interface Props`
|
||
(or inline destructure) is the contract. Many take `estimate`; some take other fixtures; some take
|
||
callbacks (pass `() => {}`); some fetch via hooks by id (see "fetch-coupled" below).
|
||
|
||
## Recipe
|
||
- 1 canonical story (the docs/page.tsx usage) named `Default`. Add 1–3 more ONLY if they visibly
|
||
differ (sweep a real variant axis: a state, an enum, empty-vs-full). Budget 1–4 cells.
|
||
- Realistic content only (the ЕКБ fixtures) — never foo/test.
|
||
- Callbacks → `() => {}`. Booleans like `isLoading`/`isPending` → usually `false` for the full state.
|
||
- Keep visually-identical variants OUT (e.g. a flag that only changes a link, not the view) — one cell.
|
||
|
||
## Fetch-coupled components (PlacementHistoryCard, HouseAnalyticsSection, PhotoUpload, ListingsCard
|
||
maybe) take an `estimateId` and fetch internally. The global Provider's query cache is EMPTY, so they
|
||
render their loading/empty state. If the component accepts data via props too, prefer that. If it can
|
||
ONLY fetch and renders blank/loading, author the best you can and RECORD it in your learnings file as
|
||
"renders loading/empty — fetch-coupled" — do NOT fake data you can't pass.
|
||
|
||
## Your loop (per component)
|
||
1. Read source → write `.design-sync/previews/<Name>.tsx` (named exports, no marker line).
|
||
2. Rebuild ONLY your components:
|
||
`node .ds-sync/lib/preview-rebuild.mjs --config .design-sync/config.json --node-modules tradein-mvp/frontend/node_modules --out ./ds-bundle --components <YOUR_COMMA_LIST>`
|
||
3. Capture ONLY your components:
|
||
`node .ds-sync/package-capture.mjs --out ./ds-bundle --components <YOUR_COMMA_LIST>`
|
||
4. READ each `ds-bundle/_screenshots/review/<group>__<Name>.png`. Grade each cell on the absolute
|
||
rubric: Styled (DS tokens/font visible) · Complete (renders whole, no missing children) · Plausible.
|
||
5. Write `.design-sync/.cache/review/<Name>.grade.json`:
|
||
`{"cells":{"<CellLabel>":{"verdict":"good"|"needs-work","note":"..."}}}` (keys = exact cell labels
|
||
from the capture log). `needs-work` → fix the .tsx, rebuild, recapture, regrade until `good`.
|
||
|
||
## HARD RULES (violating corrupts other agents' work)
|
||
- Edit ONLY your assigned `previews/<Name>.tsx`, your `.cache/review/<Name>.grade.json`, and your
|
||
`.design-sync/learnings/<BATCH>.md`. NOTHING else. Never touch config.json / NOTES.md / other previews.
|
||
- NEVER run `package-build.mjs` or `package-validate.mjs`. NEVER run `package-capture.mjs` without
|
||
`--components` (a full run prunes other agents' state). Only the two scoped commands above.
|
||
- If the SAME root cause hits 2+ of your components, or ANY config-level issue (a needed provider chain,
|
||
a missing token/css, a needed `cardMode`/`viewport` override, an import that won't resolve) → STOP on
|
||
those, record in your learnings file `.design-sync/learnings/<BATCH>.md`, and report to the orchestrator.
|
||
Config/NOTES changes are orchestrator-only.
|
||
|
||
## Wide/overlay components
|
||
Cards with `className="card"` are full-width and fine. If a card visibly overflows its grid cell or an
|
||
overlay (dialog/map modal) collapses, that needs a `cfg.overrides.<Name>.cardMode` change — you CANNOT
|
||
do that (config is orchestrator-only). Record it in learnings and move on.
|
||
|
||
## Calibration learnings from the solo set (HeroSummary, OfferCard, PriceRangeBar)
|
||
- The contract works: `import { X } from 'tradein-mvp-frontend'` + `./_fixtures` + global Provider.
|
||
- Cards render fully styled with the trade-in tokens/Manrope font.
|
||
- Components with analog photos show a grey placeholder (fixture photo_url=null, offline) — that is the
|
||
component's REAL no-photo state, grade it good (do not try to add external image URLs).
|
||
- Drop variants that don't change the view (brandSlug, isResubmitting rendered identical → single cell).
|