# 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).
