5.7 KiB
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 anyestimate: AggregatedEstimateprop.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. Readtradein-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 likeisLoading/isPending→ usuallyfalsefor 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)
- Read source → write
.design-sync/previews/<Name>.tsx(named exports, no marker line). - 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> - Capture ONLY your components:
node .ds-sync/package-capture.mjs --out ./ds-bundle --components <YOUR_COMMA_LIST> - 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. - 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 untilgood.
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.mjsorpackage-validate.mjs. NEVER runpackage-capture.mjswithout--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/viewportoverride, 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).