# Preview authoring guide (design-sync, tradein-mvp-frontend) You author `.design-sync/previews/.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//.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/.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 ` 3. Capture ONLY your components: `node .ds-sync/package-capture.mjs --out ./ds-bundle --components ` 4. READ each `ds-bundle/_screenshots/review/__.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/.grade.json`: `{"cells":{"":{"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/.tsx`, your `.cache/review/.grade.json`, and your `.design-sync/learnings/.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/.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..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).