From 375d59c48d12e83556a50b204aae2e2482c50395 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Fri, 3 Jul 2026 10:23:45 +0300 Subject: [PATCH] =?UTF-8?q?chore(design-sync):=20durable-=D0=B8=D0=BD?= =?UTF-8?q?=D0=BF=D1=83=D1=82=D1=8B=20tradein=20DS-=D0=BF=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BB=D0=B8=20=E2=80=94=206=20floor-card=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D1=8C=D1=8E,=20=D0=B1=D1=80=D0=B5=D0=BD=D0=B4-?= =?UTF-8?q?=D1=88=D1=80=D0=B8=D1=84=D1=82=D1=8B=20Manrope/Plex,=20source-k?= =?UTF-8?q?it=20fork-=D1=84=D0=B8=D0=BA=D1=81=20(#2267)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .design-sync/AUTHORING.md | 79 +++++++ .design-sync/NOTES.md | 71 ++++++ .design-sync/config.json | 53 +++++ .design-sync/conventions.md | 56 +++++ .../fonts/IBMPlexMono-300-cyrillic.woff2 | Bin 0 -> 8268 bytes .../fonts/IBMPlexMono-300-latin.woff2 | Bin 0 -> 14748 bytes .../fonts/IBMPlexMono-400-cyrillic.woff2 | Bin 0 -> 8356 bytes .../fonts/IBMPlexMono-400-latin.woff2 | Bin 0 -> 14708 bytes .../fonts/IBMPlexMono-500-cyrillic.woff2 | Bin 0 -> 8460 bytes .../fonts/IBMPlexMono-500-latin.woff2 | Bin 0 -> 14888 bytes .design-sync/fonts/Inter-cyrillic.woff2 | Bin 0 -> 18748 bytes .design-sync/fonts/Inter-latin.woff2 | Bin 0 -> 48256 bytes .../fonts/JetBrainsMono-cyrillic.woff2 | Bin 0 -> 12108 bytes .design-sync/fonts/JetBrainsMono-latin.woff2 | Bin 0 -> 40404 bytes .design-sync/fonts/Manrope-cyrillic.woff2 | Bin 0 -> 14500 bytes .design-sync/fonts/Manrope-latin.woff2 | Bin 0 -> 24836 bytes .design-sync/fonts/brand-fonts.css | 112 +++++++++ .design-sync/overrides/source-kit.mjs | 213 ++++++++++++++++++ .design-sync/preview-provider.tsx | 73 ++++++ .design-sync/previews/AddressInput.tsx | 12 + .../previews/BuildingListingsDrawer.tsx | 36 +++ .design-sync/previews/CianValuationCard.tsx | 6 + .design-sync/previews/DataQualitySection.tsx | 6 + .design-sync/previews/DealsCard.tsx | 6 + .design-sync/previews/DistributionCard.tsx | 25 ++ .design-sync/previews/EstimateForm.tsx | 13 ++ .design-sync/previews/ExposureCard.tsx | 25 ++ .design-sync/previews/HeroSummary.tsx | 13 ++ .design-sync/previews/HeroTransparency.tsx | 9 + .../previews/HouseAnalyticsKpiRow.tsx | 6 + .../previews/HouseAnalyticsSection.tsx | 9 + .design-sync/previews/HouseInfoCard.tsx | 6 + .design-sync/previews/IMVBenchmark.tsx | 6 + .design-sync/previews/ListingsCard.tsx | 9 + .design-sync/previews/LocationDrawer.tsx | 25 ++ .design-sync/previews/MapCard.tsx | 8 + .design-sync/previews/MapPicker.tsx | 8 + .design-sync/previews/NoAccessScreen.tsx | 13 ++ .design-sync/previews/OfferCard.tsx | 6 + .design-sync/previews/PacingSection.tsx | 6 + .design-sync/previews/PhotoUpload.tsx | 8 + .../previews/PlacementHistoryCard.tsx | 8 + .design-sync/previews/PriceHistoryChart.tsx | 8 + .design-sync/previews/PriceRangeBar.tsx | 24 ++ .design-sync/previews/PriceTrendCard.tsx | 5 + .../previews/ProviderProxySection.tsx | 6 + .design-sync/previews/RecentSoldList.tsx | 8 + .design-sync/previews/ResultPanel.tsx | 10 + .design-sync/previews/RouteGuard.tsx | 15 ++ .design-sync/previews/RunsTable.tsx | 6 + .design-sync/previews/SaleShareControls.tsx | 54 +++++ .design-sync/previews/SaleShareList.tsx | 50 ++++ .design-sync/previews/SaleShareMap.tsx | 30 +++ .design-sync/previews/ScheduleControl.tsx | 25 ++ .design-sync/previews/SectionOverlay.tsx | 39 ++++ .design-sync/previews/SellTimeSensitivity.tsx | 6 + .design-sync/previews/SourcesProgress.tsx | 6 + .design-sync/previews/StreetDealsCard.tsx | 8 + .design-sync/previews/SystemHealthSection.tsx | 6 + .design-sync/previews/TestPresets.tsx | 5 + .design-sync/previews/Topbar.tsx | 6 + .design-sync/previews/UserMenu.tsx | 10 + .design-sync/previews/WhatIfPanel.tsx | 9 + .design-sync/previews/_fixtures.ts | 3 + .gitignore | 8 +- 65 files changed, 1271 insertions(+), 2 deletions(-) create mode 100644 .design-sync/AUTHORING.md create mode 100644 .design-sync/NOTES.md create mode 100644 .design-sync/config.json create mode 100644 .design-sync/conventions.md create mode 100644 .design-sync/fonts/IBMPlexMono-300-cyrillic.woff2 create mode 100644 .design-sync/fonts/IBMPlexMono-300-latin.woff2 create mode 100644 .design-sync/fonts/IBMPlexMono-400-cyrillic.woff2 create mode 100644 .design-sync/fonts/IBMPlexMono-400-latin.woff2 create mode 100644 .design-sync/fonts/IBMPlexMono-500-cyrillic.woff2 create mode 100644 .design-sync/fonts/IBMPlexMono-500-latin.woff2 create mode 100644 .design-sync/fonts/Inter-cyrillic.woff2 create mode 100644 .design-sync/fonts/Inter-latin.woff2 create mode 100644 .design-sync/fonts/JetBrainsMono-cyrillic.woff2 create mode 100644 .design-sync/fonts/JetBrainsMono-latin.woff2 create mode 100644 .design-sync/fonts/Manrope-cyrillic.woff2 create mode 100644 .design-sync/fonts/Manrope-latin.woff2 create mode 100644 .design-sync/fonts/brand-fonts.css create mode 100644 .design-sync/overrides/source-kit.mjs create mode 100644 .design-sync/preview-provider.tsx create mode 100644 .design-sync/previews/AddressInput.tsx create mode 100644 .design-sync/previews/BuildingListingsDrawer.tsx create mode 100644 .design-sync/previews/CianValuationCard.tsx create mode 100644 .design-sync/previews/DataQualitySection.tsx create mode 100644 .design-sync/previews/DealsCard.tsx create mode 100644 .design-sync/previews/DistributionCard.tsx create mode 100644 .design-sync/previews/EstimateForm.tsx create mode 100644 .design-sync/previews/ExposureCard.tsx create mode 100644 .design-sync/previews/HeroSummary.tsx create mode 100644 .design-sync/previews/HeroTransparency.tsx create mode 100644 .design-sync/previews/HouseAnalyticsKpiRow.tsx create mode 100644 .design-sync/previews/HouseAnalyticsSection.tsx create mode 100644 .design-sync/previews/HouseInfoCard.tsx create mode 100644 .design-sync/previews/IMVBenchmark.tsx create mode 100644 .design-sync/previews/ListingsCard.tsx create mode 100644 .design-sync/previews/LocationDrawer.tsx create mode 100644 .design-sync/previews/MapCard.tsx create mode 100644 .design-sync/previews/MapPicker.tsx create mode 100644 .design-sync/previews/NoAccessScreen.tsx create mode 100644 .design-sync/previews/OfferCard.tsx create mode 100644 .design-sync/previews/PacingSection.tsx create mode 100644 .design-sync/previews/PhotoUpload.tsx create mode 100644 .design-sync/previews/PlacementHistoryCard.tsx create mode 100644 .design-sync/previews/PriceHistoryChart.tsx create mode 100644 .design-sync/previews/PriceRangeBar.tsx create mode 100644 .design-sync/previews/PriceTrendCard.tsx create mode 100644 .design-sync/previews/ProviderProxySection.tsx create mode 100644 .design-sync/previews/RecentSoldList.tsx create mode 100644 .design-sync/previews/ResultPanel.tsx create mode 100644 .design-sync/previews/RouteGuard.tsx create mode 100644 .design-sync/previews/RunsTable.tsx create mode 100644 .design-sync/previews/SaleShareControls.tsx create mode 100644 .design-sync/previews/SaleShareList.tsx create mode 100644 .design-sync/previews/SaleShareMap.tsx create mode 100644 .design-sync/previews/ScheduleControl.tsx create mode 100644 .design-sync/previews/SectionOverlay.tsx create mode 100644 .design-sync/previews/SellTimeSensitivity.tsx create mode 100644 .design-sync/previews/SourcesProgress.tsx create mode 100644 .design-sync/previews/StreetDealsCard.tsx create mode 100644 .design-sync/previews/SystemHealthSection.tsx create mode 100644 .design-sync/previews/TestPresets.tsx create mode 100644 .design-sync/previews/Topbar.tsx create mode 100644 .design-sync/previews/UserMenu.tsx create mode 100644 .design-sync/previews/WhatIfPanel.tsx create mode 100644 .design-sync/previews/_fixtures.ts diff --git a/.design-sync/AUTHORING.md b/.design-sync/AUTHORING.md new file mode 100644 index 00000000..08a2bec4 --- /dev/null +++ b/.design-sync/AUTHORING.md @@ -0,0 +1,79 @@ +# 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). diff --git a/.design-sync/NOTES.md b/.design-sync/NOTES.md new file mode 100644 index 00000000..df12c959 --- /dev/null +++ b/.design-sync/NOTES.md @@ -0,0 +1,71 @@ +# design-sync NOTES — tradein-mvp-frontend + +Target: `tradein-mvp/frontend` (NOT the repo-root `frontend/`, which is the Site-Finder app). Project: gendesign-tradein. + +## Build gotchas +- **No dist / no Storybook → synth-entry mode.** The package is a Next.js app, not a lib; `package.json` has no `main`/`module`/`exports`. +- **`--entry` must point at a NON-existent dist path** (`tradein-mvp/frontend/dist/index.js`). Two jobs: + 1. PKG_DIR resolution walks up `dirname(--entry)` to the real named `package.json` → PKG_DIR=`tradein-mvp/frontend` (without `--entry`, PKG_DIR defaults to `node_modules/` which doesn't self-install → ENOENT crash). + 2. The path being absent makes `resolveDistEntry(soft)` return null → `synthEntry=true` → `deriveComponentsFromSrc` discovers components from `src/`. A path that EXISTS (e.g. package.json) suppresses synth mode → `[ZERO_MATCH]` tokens-only. +- **`cfg.*` path fields are PACKAGE-relative** (relative to PKG_DIR), not repo-relative. srcDir=`src`, cssEntry=`src/components/trade-in/trade-in.css`, tokensGlob=`src/app/globals.css`. +- `--node-modules tradein-mvp/frontend/node_modules` (react + @types/react live there). + +## Styling +- Tokens in `src/app/globals.css` (`:root{--bg-app,--accent,…}`); component CSS in `src/components/trade-in/trade-in.css` (2277 lines). Manrope via remote Google Fonts `@import` (→ `[FONT_REMOTE]`, no action). + +## Re-sync risks +- Synth scan over-includes non-component PascalCase exports (51 found vs ~37 files) — prune with `componentSrcMap: {Name: null}` as identified. +- Components are app-coupled (next/*, TanStack Query, data hooks); many need providers/mock props to render → expect floor cards / authored previews with composed props. + +## Authoring (38 components, all authored-good) +- **Import contract:** preview imports `{X} from 'tradein-mvp-frontend'` (mapped to window.TradeInUI bundle) + data from `./_fixtures` (re-exports the repo's offline `app/ui-preview/estimate/fixture.ts`). Type imports erased. +- **Provider = seeded `PreviewProvider`** (`.design-sync/preview-provider.tsx`, wired via `cfg.extraEntries` + `cfg.provider`). Mirrors `app/ui-preview/estimate/page.tsx`'s seeded QueryClient: pre-fills `["auth","me"]` (fake admin user) + the estimate sub-queries (placement-history, house-analytics, sell-time-sensitivity, cian-price-changes=[], sales-vs-listings). This is what makes fetch-coupled cards (StreetDealsCard, HouseAnalyticsSection, PlacementHistoryCard) and auth-gated ones (RouteGuard, UserMenu) render offline. + - **CRITICAL:** an extraEntries module must NOT import anything that reads `process.env` at module top-level (e.g. `@/lib/useMe` → `@/lib/api`) — extraEntries evaluate BEFORE the synth-entry `.pkg-shim.mjs`, so `process` is undefined and the whole IIFE aborts (38/38 vanish from the global). The provider hardcodes `ME_QUERY_KEY = ["auth","me"]` instead of importing useMe. + - extraEntries path is PACKAGE-relative: `../../.design-sync/preview-provider.tsx`. Its `@/` aliases don't resolve from outside the tsconfig root → use relative `../tradein-mvp/frontend/src/...` for repo imports inside it. +- **Chart cards gate on analog count ≥8** (DistributionCard, ExposureCard): the shared fixture has 3 analogs → "not enough data" fallback. Those two previews extend `analogs` to 10 realistic ЕКБ lots INLINE in their own preview file (spread FIXTURE_ESTIMATE, override analogs+n_analogs) — never edit `_fixtures.ts`. +- **Admin/scraper panels** (DataQualitySection, ProviderProxySection, RunsTable, PacingSection, SystemHealthSection) fetch their own data with no repo fixture → they render their real styled LOADING/header state. Graded good as honest states; richer data would need admin fixtures that don't exist in the repo. +- **MapCard / MapPicker**: Leaflet from unpkg loads in the capture env; OSM raster tiles sometimes don't paint within the screenshot window (external tile fetch) — markers/popup/controls still render. Both graded good. MapPicker is a `createPortal` full-viewport overlay — rendered contained in capture; if a future capture viewport change makes it escape, add `cfg.overrides.MapPicker.cardMode`/`viewport`. + +## Re-sync risks +- The `process` shim env defaults live in `.design-sync/overrides/source-kit.mjs` (synth entry). If the app reads NEW `process.env.NEXT_PUBLIC_*` vars, add them there or components throw. +- `PreviewProvider` seed keys are pinned to the fixture's `estimate_id` and address/area/rooms — if `fixture.ts` changes those, update `preview-provider.tsx` to match (else fetch-coupled cards re-floor). +- Synth scan re-includes Next route/layout/error files on every build → `componentSrcMap` nulls (13 entries) must persist. New app-router pages would need adding. +- `.d.ts` props are weak (`[key:string]:unknown`) — synth mode has no built types. Real prop contracts live in each component's source `interface Props`. A real `tsup`/`tsc` lib build would fix this (recommend if the agent needs strong API contracts). +- Grades clear on any `cfg.provider`/preview-affecting config change (expected) — re-grade from fresh sheets. + +## Re-sync 2026-07-02 — v2 dashboard sync (main was 223 commits ahead; harness authored on June components) + +**Two source-kit.mjs fork fixes were REQUIRED for the evolved v2 codebase (both committed in the override, declared in cfg.libOverrides):** +1. **Exclude `next/font` importers from the synth-entry.** `src/app/v2/layout.tsx` calls `Manrope()`/`IBM_Plex_Mono()` from `next/font/google` at MODULE TOP-LEVEL. esbuild can't resolve the Next-only loader → stubs it `(void 0)` → `undefined()` aborts the whole browser IIFE → `window.TradeInUI` empty → ALL 58 components vanish (`[RENDER] root empty` everywhere, `[BUNDLE_EXPORT]`). Fix: `comps` filter drops any file whose content matches `/from\s+['"]next\/font/`. If a NEW file top-level-calls a Next build-time loader, same class of crash — extend the filter. +2. **Re-export default-exported components.** `export * from ` does NOT re-export a module's `default`. The v2 views/nav/overlay/panel are authored `export default function ` (AnalyticsView, HeroBar, HistoryView, ParamsPanel, SectionOverlay, SourcesView, TopNav) → absent from `window.TradeInUI` → `[BUNDLE_EXPORT] not a component`. Fix: entry now also emits `export { default as } from ` for each `export default function/class `. Named-export components (`export function X`) were always fine. + +**componentSrcMap:** added `TradeInV2Layout / TradeInV2Page / SaleShareLayout / SaleSharePage` = null (Next route files, never DS components — same as RootLayout et al.). + +**overrides (grid):** MapPicker `{cardMode:single, primaryStory:Default}` (portal/fixed), StreetDealsCard + Topbar `{cardMode:column}` (wider than a grid cell). + +**Floor-card components (6, authorable on any future re-sync):** SaleShareControls, SaleShareList, SaleShareMap, SectionOverlay, LocationDrawer, BuildingListingsDrawer — overlays/drawers whose props don't seed rich data via PreviewProvider, so they show the honest typographic floor. All the OTHER v2 components (views/nav/hero/panel) render richly because the provider seeds their data. + +**Known render warn:** ResultPanel — `variants identical` (Default vs Error cells render near-identically; not broken, the Error cell just doesn't diverge visually enough). Recorded here so re-syncs don't read it as new. + +**Font note (re-sync risk):** the v2 HUD's real typefaces are **Manrope + IBM_Plex_Mono** (loaded by the excluded `v2/layout.tsx` via next/font → CSS vars `--font-manrope`/`--font-plex-mono`). `cfg.extraFonts` ships **Inter + JetBrains Mono** (June brand fonts). v2 previews therefore render Manrope-slot text in the shipped fallback. If brand-exact v2 rendering is wanted, add Manrope + IBM Plex Mono woff2 to `.design-sync/fonts/` + `cfg.extraFonts` and map the `--font-manrope`/`--font-plex-mono` vars. + +**ResultPanel NAME COLLISION (fixed 2026-07-02):** two components named `ResultPanel` in src — `app/scrapers/_components/ScraperPage.tsx` (`export function ResultPanel`, scraper mut-panel) and `components/trade-in/v2/ResultPanel.tsx` (`export default function ResultPanel`, the v2 estimate result / honest hero). The default-re-export fix makes the v2 one win `window.TradeInUI.ResultPanel` (explicit `export {default as ResultPanel}` beats the star export). But the June authored preview `previews/ResultPanel.tsx` was the SCRAPER one (`mut` props) → the v2 component rendered fine (reads data from context) but its prompt.md documented the wrong (scraper) API. FIX: (1) `cfg.componentSrcMap.ResultPanel = "src/components/trade-in/v2/ResultPanel.tsx"` pins enrichment to v2; (2) re-authored `previews/ResultPanel.tsx` → `{}} />` (v2 API; `data` defaults to the component's built-in RESULT_FIXTURE = honest-hero). If a re-sync ever shows ResultPanel with scraper markup, the pin/preview regressed. Any NEW duplicate PascalCase component name will hit the same class of issue — the default-re-export makes the default-export win; pin + author the intended one. + +**Upload gotcha (this machine):** after a follow-up single-component rebuild, `resync-verdict.json upload.deletePaths` came back with ALL other components (318 paths) — a stale-anchor diff artifact, NOT real deletions (ResultPanel + audit/uploads were absent from it). Do NOT feed that deletePaths to delete_files (would nuke the project). For a focused single-component re-upload: write just that component's `components///*` + `_preview/.js` + `_ds_sync.json` (sentinel-fenced), deletes=[]. + - **CORRECTION (2026-07-03):** the 318 deletePaths were NOT a diff artifact — they were REAL local deletions caused by a source-kit fork bug (see below): the July-02 `componentSrcMap.ResultPanel` pin collapsed the synth build to 1 component, so the local ds-bundle genuinely lost the other 53. The "don't feed deletePaths blindly" advice stands (it saved the project), but the root cause is fixed now. + +## Re-sync 2026-07-03 — 6 floor cards authored + v2 brand fonts (#2267) + +**source-kit.mjs fork fix #3 (REQUIRED): componentSrcMap pin must AUGMENT synth discovery, not replace it.** In synth mode there is no shipped `.d.ts`, so `exportedNames()` is empty and `names` contains ONLY the non-null `componentSrcMap` pins. The old guard `if (!components.length && synthEntry) components = deriveComponentsFromSrc(...)` therefore never ran once a single pin existed (ResultPanel, added 2026-07-02) → the whole bundle collapsed to 1 component (`(stale preview: X — component no longer exported)` for everything else; verdict shows all others as `removed` + bogus deletePaths). Fix in the fork: when `synthEntry`, always union `deriveComponentsFromSrc(srcFiles)` (minus `null`-excluded) with the pinned names. Any future pin would have re-triggered this. NB: the fork edit re-keys EVERY component's sourceKey → full re-grade pass (done: 47/54 renderHashes byte-identical to the 2026-07-02 anchor → carry-forward grades; the rest eyeballed). + +**6 floor cards → authored (all graded good):** +- `SaleShareControls` — inline `SaleShareSummary` (histogram 7 корзин, coverage 92.1%); порог 8% приглушает нижнюю корзину; все фильтры. +- `SaleShareList` — 6 инлайн-домов ЕКБ (heat-бейджи, «аварийный», over_100 «возможно, несколько корпусов», selected-row) + Empty cell. `cardMode: column`. +- `SaleShareMap` — 8 heat-маркеров + открытый popup выбранного дома; OSM-тайлы офлайн не красятся (известно, как MapCard). +- `SectionOverlay` (v2) — рендерится contained в relative-«артборде» (absolute-позиционирование против ближайшего positioned ancestor; wrapper height 680 + v2-градиент). 2 cells: HistoryView (04) / AnalyticsView (06) на их встроенных fixtures (data-props не переданы). `cardMode: column`. +- `LocationDrawer` (v2) — open=true в таком же contained-артборде (height 640). `cardMode: column`. +- `BuildingListingsDrawer` — `createPortal` + `.ss-drawer-overlay` = position:fixed → `cardMode: single` (прецедент MapPicker). Шапка богатая (props), тело fetch-coupled (`useBuildingListings`, data-prop нет) → офлайн честный error-state «Не удалось загрузить объявления дома». Сидировать можно было бы ключом `["sale-share","listings",]` в PreviewProvider — сознательно НЕ сделано (кэш-ключ завязан на house_id фикстуры превью; хрупко). + +**v2 brand fonts shipped (Manrope + IBM Plex Mono).** woff2 (latin+cyrillic; Manrope variable 200-800, Plex Mono static 300/400/500 — веса из `app/v2/layout.tsx`) скачаны с Google Fonts → `.design-sync/fonts/`, @font-face добавлены в `brand-fonts.css`. **ГОЧА: extraFonts-пайплайн (`css.mjs extractFonts`) извлекает ТОЛЬКО `@font-face`-блоки — `:root{}` из brand-fonts.css молча выбрасывается.** Маппинг `--font-manrope`/`--font-plex-mono` поэтому живёт в `preview-provider.tsx` (` + {children} + + ); +} diff --git a/.design-sync/previews/AddressInput.tsx b/.design-sync/previews/AddressInput.tsx new file mode 100644 index 00000000..7bfe0a21 --- /dev/null +++ b/.design-sync/previews/AddressInput.tsx @@ -0,0 +1,12 @@ +import { AddressInput } from 'tradein-mvp-frontend'; + +/** Combobox адреса ЕКБ с автокомплитом. Dropdown открывается по focus/набору + * (фетч suggest офлайн недоступен) — карточка показывает заполненный control. */ +export const Default = () => ( + {}} + onPickCoords={() => {}} + placeholder="ул. Малышева, 30 · Куйбышева, 48…" + /> +); diff --git a/.design-sync/previews/BuildingListingsDrawer.tsx b/.design-sync/previews/BuildingListingsDrawer.tsx new file mode 100644 index 00000000..84f65b34 --- /dev/null +++ b/.design-sync/previews/BuildingListingsDrawer.tsx @@ -0,0 +1,36 @@ +import { BuildingListingsDrawer } from 'tradein-mvp-frontend'; + +// Правый drawer с активными объявлениями выбранного дома (/trade-in/sale-share): +// createPortal в document.body, .ss-drawer-overlay = position:fixed по всему +// вьюпорту (как MapPicker) → cardMode:single. Шапка — адрес + тепловой бейдж +// доли + «N из M квартир» (из props). Тело FETCH-COUPLED: useBuildingListings +// (GET /buildings/{id}/listings) — data-prop нет, глобальный query-cache пуст → +// в офлайн-capture честный pending/error-state («Загрузка объявлений…» / +// «Не удалось загрузить объявления дома»). На реальной странице — список +// объявлений с ценой, ₽/м², этажом и ссылкой на оригинал. +const building = { + house_id: 3101, + address: 'ул. Викулова, 46', + lat: 56.8412, + lon: 60.5556, + sale_share_pct: 34.6, + sale_share_pct_45d: 41.2, + listings_45d: 33, + over_100: false, + active_secondary: 27, + flat_count_effective: 78, + gar_match_method: 'cadastre', + median_price_rub: 4_950_000, + median_price_per_m2: 158_500, + avg_days_on_market: 74, + year_built: 1972, + house_type: 'panel', + total_floors: 9, + series_name: '1-468', + is_emergency: false, +}; + +/** Открытый drawer дома «ул. Викулова, 46» (34.6% в продаже, 27 из 78 квартир). */ +export const Default = () => ( + {}} /> +); diff --git a/.design-sync/previews/CianValuationCard.tsx b/.design-sync/previews/CianValuationCard.tsx new file mode 100644 index 00000000..3e5c8690 --- /dev/null +++ b/.design-sync/previews/CianValuationCard.tsx @@ -0,0 +1,6 @@ +import { CianValuationCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Компактный блок «Оценка Cian» — продажа 9.70 млн ₽ + аренда 42 000 ₽/мес, + * спарклайн за 6 мес и бейдж ↑3.2%. Узкий компонент (section, не full-width card). */ +export const Default = () => ; diff --git a/.design-sync/previews/DataQualitySection.tsx b/.design-sync/previews/DataQualitySection.tsx new file mode 100644 index 00000000..78ed2b32 --- /dev/null +++ b/.design-sync/previews/DataQualitySection.tsx @@ -0,0 +1,6 @@ +import { DataQualitySection } from 'tradein-mvp-frontend'; + +/** Coverage-секция: таблица заполнения полей по источникам + обогащение домов. + * Фетчит /admin/scraper/data-quality через useQuery (без data-prop). В превью + * без сети рендерит заголовок + hint + graceful-degradation. Fetch-coupled. */ +export const Default = () => ; diff --git a/.design-sync/previews/DealsCard.tsx b/.design-sync/previews/DealsCard.tsx new file mode 100644 index 00000000..dd27c81c --- /dev/null +++ b/.design-sync/previews/DealsCard.tsx @@ -0,0 +1,6 @@ +import { DealsCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Секция 3 «Сделки» — фактические ДКП-сделки Росреестра по аналогам (2 строки): + * count-strip (кол-во / медиана / диапазон) + чипы источников + таблица. */ +export const Default = () => ; diff --git a/.design-sync/previews/DistributionCard.tsx b/.design-sync/previews/DistributionCard.tsx new file mode 100644 index 00000000..0ebed1a7 --- /dev/null +++ b/.design-sync/previews/DistributionCard.tsx @@ -0,0 +1,25 @@ +import { DistributionCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +// Канонический FIXTURE_ESTIMATE содержит лишь 3 аналога — DistributionCard рисует +// гистограмму только при ≥8 (иначе fallback-текст «мало аналогов»). Расширяем до 10 +// правдоподобных ЕКБ-аналогов (₽/м² вокруг median 178 000) — чтобы показать сам график. +// Реалистичные ЕКБ-улицы/цены, не foo/test; правим только этот preview-файл. +const ekbAnalogs = [ + { address: 'ул. Репина, 73', area_m2: 54, rooms: 2, floor: 7, total_floors: 16, price_rub: 9_700_000, price_per_m2: 179_629, listing_date: '2026-05-12', days_on_market: 18, photo_url: null, source: 'avito', source_url: null, distance_m: 120, tier: null, lat: 56.8401, lon: 60.5702 }, + { address: 'ул. Викулова, 33', area_m2: 57, rooms: 2, floor: 4, total_floors: 10, price_rub: 9_950_000, price_per_m2: 174_561, listing_date: '2026-05-20', days_on_market: 10, photo_url: null, source: 'cian', source_url: null, distance_m: 340, tier: null, lat: 56.8389, lon: 60.5681 }, + { address: 'ул. Кирова, 28', area_m2: 53, rooms: 2, floor: 9, total_floors: 12, price_rub: 9_400_000, price_per_m2: 177_358, listing_date: '2026-05-04', days_on_market: 31, photo_url: null, source: 'avito', source_url: null, distance_m: 510, tier: null, lat: 56.8372, lon: 60.5749 }, + { address: 'ул. Серафимы Дерябиной, 24', area_m2: 56, rooms: 2, floor: 6, total_floors: 14, price_rub: 9_600_000, price_per_m2: 171_429, listing_date: '2026-05-08', days_on_market: 42, photo_url: null, source: 'cian', source_url: null, distance_m: 680, tier: null, lat: 56.8318, lon: 60.5666 }, + { address: 'ул. Ясная, 6', area_m2: 52, rooms: 2, floor: 11, total_floors: 16, price_rub: 9_600_000, price_per_m2: 184_615, listing_date: '2026-05-18', days_on_market: 14, photo_url: null, source: 'avito', source_url: null, distance_m: 430, tier: null, lat: 56.8295, lon: 60.5803 }, + { address: 'ул. Белореченская, 17', area_m2: 58, rooms: 2, floor: 3, total_floors: 9, price_rub: 9_800_000, price_per_m2: 168_966, listing_date: '2026-04-28', days_on_market: 55, photo_url: null, source: 'yandex', source_url: null, distance_m: 900, tier: null, lat: 56.8267, lon: 60.5611 }, + { address: 'ул. Гурзуфская, 16', area_m2: 55, rooms: 2, floor: 8, total_floors: 12, price_rub: 10_000_000, price_per_m2: 181_818, listing_date: '2026-05-22', days_on_market: 9, photo_url: null, source: 'cian', source_url: null, distance_m: 260, tier: null, lat: 56.8344, lon: 60.5727 }, + { address: 'ул. Посадская, 40', area_m2: 51, rooms: 2, floor: 5, total_floors: 10, price_rub: 9_600_000, price_per_m2: 188_235, listing_date: '2026-05-15', days_on_market: 22, photo_url: null, source: 'avito', source_url: null, distance_m: 770, tier: null, lat: 56.8231, lon: 60.5832 }, + { address: 'ул. Шаумяна, 86', area_m2: 60, rooms: 2, floor: 2, total_floors: 18, price_rub: 9_900_000, price_per_m2: 165_000, listing_date: '2026-04-25', days_on_market: 60, photo_url: null, source: 'yandex', source_url: null, distance_m: 1100, tier: null, lat: 56.8189, lon: 60.5598 }, + { address: 'ул. Чкалова, 124', area_m2: 54, rooms: 2, floor: 12, total_floors: 19, price_rub: 10_400_000, price_per_m2: 192_593, listing_date: '2026-05-24', days_on_market: 7, photo_url: null, source: 'cian', source_url: null, distance_m: 350, tier: null, lat: 56.8276, lon: 60.5689 }, +]; + +const estimate = { ...FIXTURE_ESTIMATE, analogs: ekbAnalogs, n_analogs: ekbAnalogs.length }; + +/** Гистограмма распределения ₽/м² по 10 аналогам с маркером «ваша квартира» + * на median 178 000 ₽/м² (оранжевый столбец/референс-линия). */ +export const Default = () => ; diff --git a/.design-sync/previews/EstimateForm.tsx b/.design-sync/previews/EstimateForm.tsx new file mode 100644 index 00000000..2f8f2a94 --- /dev/null +++ b/.design-sync/previews/EstimateForm.tsx @@ -0,0 +1,13 @@ +import { EstimateForm } from 'tradein-mvp-frontend'; + +/** Шаг 1/1 — параметры квартиры (адрес + площадь/комнаты/этаж/тип/ремонт + + * свёрнутый CRM-блок). Полное «спокойное» состояние: не считает, без ошибок. */ +export const Default = () => ( + {}} + isPending={false} + error={null} + remaining={12} + limit={15} + /> +); diff --git a/.design-sync/previews/ExposureCard.tsx b/.design-sync/previews/ExposureCard.tsx new file mode 100644 index 00000000..e18b4693 --- /dev/null +++ b/.design-sync/previews/ExposureCard.tsx @@ -0,0 +1,25 @@ +import { ExposureCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +// Канонический FIXTURE_ESTIMATE содержит лишь 3 аналога — ExposureCard рисует +// scatter (цена × срок экспозиции) только при ≥8 точках с days_on_market (иначе +// fallback-текст «недостаточно данных»). Расширяем до 10 правдоподобных ЕКБ-аналогов +// (у всех непустой days_on_market) — чтобы показать сам график. Правим только этот файл. +const ekbAnalogs = [ + { address: 'ул. Репина, 73', area_m2: 54, rooms: 2, floor: 7, total_floors: 16, price_rub: 9_700_000, price_per_m2: 179_629, listing_date: '2026-05-12', days_on_market: 18, photo_url: null, source: 'avito', source_url: null, distance_m: 120, tier: null, lat: 56.8401, lon: 60.5702 }, + { address: 'ул. Викулова, 33', area_m2: 57, rooms: 2, floor: 4, total_floors: 10, price_rub: 9_950_000, price_per_m2: 174_561, listing_date: '2026-05-20', days_on_market: 10, photo_url: null, source: 'cian', source_url: null, distance_m: 340, tier: null, lat: 56.8389, lon: 60.5681 }, + { address: 'ул. Кирова, 28', area_m2: 53, rooms: 2, floor: 9, total_floors: 12, price_rub: 9_400_000, price_per_m2: 177_358, listing_date: '2026-05-04', days_on_market: 31, photo_url: null, source: 'avito', source_url: null, distance_m: 510, tier: null, lat: 56.8372, lon: 60.5749 }, + { address: 'ул. Серафимы Дерябиной, 24', area_m2: 56, rooms: 2, floor: 6, total_floors: 14, price_rub: 9_600_000, price_per_m2: 171_429, listing_date: '2026-05-08', days_on_market: 42, photo_url: null, source: 'cian', source_url: null, distance_m: 680, tier: null, lat: 56.8318, lon: 60.5666 }, + { address: 'ул. Ясная, 6', area_m2: 52, rooms: 2, floor: 11, total_floors: 16, price_rub: 9_600_000, price_per_m2: 184_615, listing_date: '2026-05-18', days_on_market: 14, photo_url: null, source: 'avito', source_url: null, distance_m: 430, tier: null, lat: 56.8295, lon: 60.5803 }, + { address: 'ул. Белореченская, 17', area_m2: 58, rooms: 2, floor: 3, total_floors: 9, price_rub: 9_800_000, price_per_m2: 168_966, listing_date: '2026-04-28', days_on_market: 55, photo_url: null, source: 'yandex', source_url: null, distance_m: 900, tier: null, lat: 56.8267, lon: 60.5611 }, + { address: 'ул. Гурзуфская, 16', area_m2: 55, rooms: 2, floor: 8, total_floors: 12, price_rub: 10_000_000, price_per_m2: 181_818, listing_date: '2026-05-22', days_on_market: 9, photo_url: null, source: 'cian', source_url: null, distance_m: 260, tier: null, lat: 56.8344, lon: 60.5727 }, + { address: 'ул. Посадская, 40', area_m2: 51, rooms: 2, floor: 5, total_floors: 10, price_rub: 9_600_000, price_per_m2: 188_235, listing_date: '2026-05-15', days_on_market: 22, photo_url: null, source: 'avito', source_url: null, distance_m: 770, tier: null, lat: 56.8231, lon: 60.5832 }, + { address: 'ул. Шаумяна, 86', area_m2: 60, rooms: 2, floor: 2, total_floors: 18, price_rub: 9_900_000, price_per_m2: 165_000, listing_date: '2026-04-25', days_on_market: 60, photo_url: null, source: 'yandex', source_url: null, distance_m: 1100, tier: null, lat: 56.8189, lon: 60.5598 }, + { address: 'ул. Чкалова, 124', area_m2: 54, rooms: 2, floor: 12, total_floors: 19, price_rub: 10_400_000, price_per_m2: 192_593, listing_date: '2026-05-24', days_on_market: 7, photo_url: null, source: 'cian', source_url: null, distance_m: 350, tier: null, lat: 56.8276, lon: 60.5689 }, +]; + +const estimate = { ...FIXTURE_ESTIMATE, analogs: ekbAnalogs, n_analogs: ekbAnalogs.length }; + +/** Scatter «цена × срок экспозиции» по 10 аналогам (дешевле → быстрее), + * вертикальная референс-линия на median 9.85 млн ₽. */ +export const Default = () => ; diff --git a/.design-sync/previews/HeroSummary.tsx b/.design-sync/previews/HeroSummary.tsx new file mode 100644 index 00000000..0daea6bf --- /dev/null +++ b/.design-sync/previews/HeroSummary.tsx @@ -0,0 +1,13 @@ +import { HeroSummary } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE, FIXTURE_INPUT } from './_fixtures'; + +/** Секция 1 «Сводка» — медиана + достоверность CV + параметры объекта. + * Фото-аналог = плейсхолдер (фикстура офлайн, photo_url: null). */ +export const Default = () => ( + {}} + isResubmitting={false} + /> +); diff --git a/.design-sync/previews/HeroTransparency.tsx b/.design-sync/previews/HeroTransparency.tsx new file mode 100644 index 00000000..7c1e22ec --- /dev/null +++ b/.design-sync/previews/HeroTransparency.tsx @@ -0,0 +1,9 @@ +import { HeroTransparency } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Блок «доверие + действия» в шапке результата — CTA «заявка» (лид-инбокс задан + * в shim env), «Скачать PDF», свежесть данных и collapsible «Как рассчитано» + * (band достоверности, аналоги, точность адреса). Generic-бренд (brandSlug=null). */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/HouseAnalyticsKpiRow.tsx b/.design-sync/previews/HouseAnalyticsKpiRow.tsx new file mode 100644 index 00000000..a5bc14d5 --- /dev/null +++ b/.design-sync/previews/HouseAnalyticsKpiRow.tsx @@ -0,0 +1,6 @@ +import { HouseAnalyticsKpiRow } from 'tradein-mvp-frontend'; +import { FIXTURE_ANALYTICS } from './_fixtures'; + +/** KPI-строка аналитики дома — 3 карточки: средняя экспозиция, средний торг, + * доля снятых (sold_count из total_lots). */ +export const Default = () => ; diff --git a/.design-sync/previews/HouseAnalyticsSection.tsx b/.design-sync/previews/HouseAnalyticsSection.tsx new file mode 100644 index 00000000..332b69a1 --- /dev/null +++ b/.design-sync/previews/HouseAnalyticsSection.tsx @@ -0,0 +1,9 @@ +import { HouseAnalyticsSection } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Секция-обёртка «Аналитика дома» — fetch-coupled: тянет house-analytics + + * sell-time-sensitivity по estimateId через TanStack Query. Глобальный provider + * preview-режима с пустым кэшем → isPending → секция возвращает null (пустой + * рендер). Содержимое покрыто отдельными ячейками HouseAnalyticsKpiRow / + * PriceHistoryChart / RecentSoldList / SellTimeSensitivity. */ +export const Default = () => ; diff --git a/.design-sync/previews/HouseInfoCard.tsx b/.design-sync/previews/HouseInfoCard.tsx new file mode 100644 index 00000000..3c3e3233 --- /dev/null +++ b/.design-sync/previews/HouseInfoCard.tsx @@ -0,0 +1,6 @@ +import { HouseInfoCard } from 'tradein-mvp-frontend'; +import { FIXTURE_HOUSES } from './_fixtures'; + +/** Карточка «Дом» — ближайший дом из выборки: адрес, рейтинг, параметры дома + * (год, этажность, тип, лифты, двор, застройщик). length>0 → не null. */ +export const Default = () => ; diff --git a/.design-sync/previews/IMVBenchmark.tsx b/.design-sync/previews/IMVBenchmark.tsx new file mode 100644 index 00000000..68405a4e --- /dev/null +++ b/.design-sync/previews/IMVBenchmark.tsx @@ -0,0 +1,6 @@ +import { IMVBenchmark } from 'tradein-mvp-frontend'; +import { FIXTURE_IMV } from './_fixtures'; + +/** Avito IMV benchmark — рекомендованная цена + диапазон + аналоги в рынке, + * сравнение с нашей медианой (diff_pct). available:true → не null. */ +export const Default = () => ; diff --git a/.design-sync/previews/ListingsCard.tsx b/.design-sync/previews/ListingsCard.tsx new file mode 100644 index 00000000..330d9f0f --- /dev/null +++ b/.design-sync/previews/ListingsCard.tsx @@ -0,0 +1,9 @@ +import { ListingsCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Секция 2 «Рынок» — объявления-аналоги: count-strip, фильтры/источники, + * ценовой бар P25–P75 + таблица. cian-price-changes fetch офлайн пуст → бейджи + * скидок не показываются (graceful), всё остальное берётся из estimate-пропа. */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/LocationDrawer.tsx b/.design-sync/previews/LocationDrawer.tsx new file mode 100644 index 00000000..15460646 --- /dev/null +++ b/.design-sync/previews/LocationDrawer.tsx @@ -0,0 +1,25 @@ +import { LocationDrawer } from 'tradein-mvp-frontend'; + +// «ПОЯСНЕНИЕ К РАСЧЁТУ» — правый drawer HUD «МЕРА Оценка» (/trade-in/v2), +// открывается с «?» у «КОЭФ. ЛОКАЦИИ» в HeroBar. Честная методика: как +// агрегируются источники (Циан/Я.Недвижимость/Авито/Домклик + Росреестр) и +// явная плашка «коэффициент локации в разработке». Контент статичный — +// данных не принимает, только open/onClose. Drawer absolute-позиционирован +// (width 452 + scrim) — рендерим contained внутри relative-обёртки. + +/** Открытое состояние: scrim + выдвинутая панель с методикой и info-плашкой + * о локации. */ +export const Default = () => ( +
+ {}} /> +
+); diff --git a/.design-sync/previews/MapCard.tsx b/.design-sync/previews/MapCard.tsx new file mode 100644 index 00000000..a9536c9f --- /dev/null +++ b/.design-sync/previews/MapCard.tsx @@ -0,0 +1,8 @@ +import { MapCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Карта аналитики — target + 3 аналога с гео-точками (Leaflet + OSM-тайлы). + * NB: компонент тянет Leaflet с unpkg CDN + тайлы с tile.openstreetmap.org. В + * офлайн-capture карта не грузится (loadLeaflet → error → mapError, либо серый + * холст --surface-2). Card-обёртка (шапка/легенда/футер) рендерится в любом случае. */ +export const Default = () => ; diff --git a/.design-sync/previews/MapPicker.tsx b/.design-sync/previews/MapPicker.tsx new file mode 100644 index 00000000..3608c4ce --- /dev/null +++ b/.design-sync/previews/MapPicker.tsx @@ -0,0 +1,8 @@ +import { MapPicker } from 'tradein-mvp-frontend'; + +/** Модалка выбора адреса на карте ЕКБ (Leaflet+OSM). Рендерится через + * createPortal в document.body как position:fixed оверлей — НЕ заперт в ячейке + * грида. Leaflet тянется с CDN (офлайн → «не удалось загрузить карту»). */ +export const Default = () => ( + {}} onClose={() => {}} /> +); diff --git a/.design-sync/previews/NoAccessScreen.tsx b/.design-sync/previews/NoAccessScreen.tsx new file mode 100644 index 00000000..9a580728 --- /dev/null +++ b/.design-sync/previews/NoAccessScreen.tsx @@ -0,0 +1,13 @@ +import { NoAccessScreen } from 'tradein-mvp-frontend'; + +/** Полноэкранный «доступа нет» — pure-props, 4 реальных варианта enum. + * variant сменяет title + subtitle (trial — со ссылкой в Telegram). */ +export const UserDenied = () => ; + +export const PathDenied = () => ( + +); + +export const SessionExpired = () => ; + +export const TrialEnded = () => ; diff --git a/.design-sync/previews/OfferCard.tsx b/.design-sync/previews/OfferCard.tsx new file mode 100644 index 00000000..63a508a0 --- /dev/null +++ b/.design-sync/previews/OfferCard.tsx @@ -0,0 +1,6 @@ +import { OfferCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Секция 4 «Оффер» — разбивка издержек самостоятельной продажи на дефолтных + * ставках (brandSlug меняет только PDF-endpoint, не вид). */ +export const Default = () => ; diff --git a/.design-sync/previews/PacingSection.tsx b/.design-sync/previews/PacingSection.tsx new file mode 100644 index 00000000..0073aeee --- /dev/null +++ b/.design-sync/previews/PacingSection.tsx @@ -0,0 +1,6 @@ +import { PacingSection } from 'tradein-mvp-frontend'; + +/** Pacing-интервалы запросов по провайдерам. Фетчит /admin/scraper/pacing через + * useQuery (data-prop нет). В превью без сети — заголовок + hint + + * graceful-degradation. Fetch-coupled. */ +export const Default = () => ; diff --git a/.design-sync/previews/PhotoUpload.tsx b/.design-sync/previews/PhotoUpload.tsx new file mode 100644 index 00000000..51beff97 --- /dev/null +++ b/.design-sync/previews/PhotoUpload.tsx @@ -0,0 +1,8 @@ +import { PhotoUpload } from 'tradein-mvp-frontend'; + +/** Загрузка фото квартиры (#394). Карточка с заголовком, счётчиком N/12 и + * файл-инпутом. Реальный UUID → активный аплоадер «0 / 12» (фетч списка фото + * офлайн молча игнорируется). */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/PlacementHistoryCard.tsx b/.design-sync/previews/PlacementHistoryCard.tsx new file mode 100644 index 00000000..c0e142f9 --- /dev/null +++ b/.design-sync/previews/PlacementHistoryCard.tsx @@ -0,0 +1,8 @@ +import { PlacementHistoryCard } from 'tradein-mvp-frontend'; + +/** История продаж в доме. Fetch-coupled: данные тянет useEstimatePlacementHistory + * по estimateId; при пустом query-кэше isPending → компонент возвращает null + * (нет loading/empty-вёрстки). Передать данные пропом нельзя. */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/PriceHistoryChart.tsx b/.design-sync/previews/PriceHistoryChart.tsx new file mode 100644 index 00000000..a3a25d59 --- /dev/null +++ b/.design-sync/previews/PriceHistoryChart.tsx @@ -0,0 +1,8 @@ +import { PriceHistoryChart } from 'tradein-mvp-frontend'; +import { FIXTURE_ANALYTICS } from './_fixtures'; + +/** «История цен в этом доме» — recharts line-chart медианы ₽/м² по годам + * (Avito-серия; фикстура без Яндекс-точек → одна линия). */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/PriceRangeBar.tsx b/.design-sync/previews/PriceRangeBar.tsx new file mode 100644 index 00000000..7f336e9e --- /dev/null +++ b/.design-sync/previews/PriceRangeBar.tsx @@ -0,0 +1,24 @@ +import { PriceRangeBar } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +const wrap = (node: React.ReactNode) => ( +
{node}
+); + +/** Типовой коридор asking-цены с медианой по центру. */ +export const Default = () => + wrap( + , + ); + +/** Широкий разброс — медиана смещена влево. */ +export const WideSpread = () => + wrap(); + +/** Узкий коридор — высокая достоверность. */ +export const Tight = () => + wrap(); diff --git a/.design-sync/previews/PriceTrendCard.tsx b/.design-sync/previews/PriceTrendCard.tsx new file mode 100644 index 00000000..f9143ec4 --- /dev/null +++ b/.design-sync/previews/PriceTrendCard.tsx @@ -0,0 +1,5 @@ +import { PriceTrendCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** «Динамика ₽/м²» — линия recharts по 6 месяцам (168k → 178k), дельта в шапке. */ +export const Default = () => ; diff --git a/.design-sync/previews/ProviderProxySection.tsx b/.design-sync/previews/ProviderProxySection.tsx new file mode 100644 index 00000000..206f1546 --- /dev/null +++ b/.design-sync/previews/ProviderProxySection.tsx @@ -0,0 +1,6 @@ +import { ProviderProxySection } from 'tradein-mvp-frontend'; + +/** Прокси-блок внутри вкладки провайдера. Фетчит /admin/scraper/health через + * useScraperHealth (по source), data-prop нет. В превью без сети — заголовок + * «Прокси» + loading/error. Fetch-coupled. */ +export const Default = () => ; diff --git a/.design-sync/previews/RecentSoldList.tsx b/.design-sync/previews/RecentSoldList.tsx new file mode 100644 index 00000000..efa37506 --- /dev/null +++ b/.design-sync/previews/RecentSoldList.tsx @@ -0,0 +1,8 @@ +import { RecentSoldList } from 'tradein-mvp-frontend'; +import { FIXTURE_ANALYTICS } from './_fixtures'; + +/** «Недавно снятые (12 мес)» — таблица лотов: комнаты/площадь/этаж, цена, торг, + * дата снятия, экспозиция. */ +export const Default = () => ( + +); diff --git a/.design-sync/previews/ResultPanel.tsx b/.design-sync/previews/ResultPanel.tsx new file mode 100644 index 00000000..a21667db --- /dev/null +++ b/.design-sync/previews/ResultPanel.tsx @@ -0,0 +1,10 @@ +import { ResultPanel } from 'tradein-mvp-frontend'; + +/** + * ResultPanel (v2) — центральная панель результата оценки на `/trade-in/v2`: + * одна цифра-герой (ожидаемая цена продажи / «оценка»), тиры median + ДКП, + * доверительные диапазоны, источники и мини-гистограмма распределения. + * Данные по умолчанию берутся из встроенной `RESULT_FIXTURE` компонента, + * поэтому карточке достаточно передать `onNavigate` (навигация по секциям). + */ +export const Default = () => {}} />; diff --git a/.design-sync/previews/RouteGuard.tsx b/.design-sync/previews/RouteGuard.tsx new file mode 100644 index 00000000..2e27ed39 --- /dev/null +++ b/.design-sync/previews/RouteGuard.tsx @@ -0,0 +1,15 @@ +import { RouteGuard } from 'tradein-mvp-frontend'; + +/** RBAC-обёртка. useMe() читает /api/v1/me — в превью-окружении сети нет, + * поэтому guard рендерит свой gated-state (NoAccessScreen "Доступа нет" при + * ошибке /me, либо ничего во время загрузки). Fetch-coupled на useMe. */ +export const Default = () => ( + +
+

Защищённый раздел

+

+ Этот контент виден только при разрешённой роли. +

+
+
+); diff --git a/.design-sync/previews/RunsTable.tsx b/.design-sync/previews/RunsTable.tsx new file mode 100644 index 00000000..ecb73cee --- /dev/null +++ b/.design-sync/previews/RunsTable.tsx @@ -0,0 +1,6 @@ +import { RunsTable } from 'tradein-mvp-frontend'; + +/** История прогонов скраппера. Фетчит /admin/scrape/runs через useQuery + * (data-prop нет), но заголовок + hint + фильтры (источник / статус-чипы) + * рендерятся всегда — это видимый styled-каркас. Таблица fetch-coupled. */ +export const Default = () => ; diff --git a/.design-sync/previews/SaleShareControls.tsx b/.design-sync/previews/SaleShareControls.tsx new file mode 100644 index 00000000..cca3fa75 --- /dev/null +++ b/.design-sync/previews/SaleShareControls.tsx @@ -0,0 +1,54 @@ +import { SaleShareControls } from 'tradein-mvp-frontend'; + +// Панель «Порог и фильтры» страницы /trade-in/sale-share. Controlled-компонент: +// состояние живёт в page.tsx и приходит через props. Сводка (гистограмма +// распределения sale_share_pct) — реалистичные значения по покрытию ЕКБ +// (view v_building_sale_share ≈ 6.7k домов вторички, знаменатель ГАР у ~92%). +const summary = { + total_secondary_buildings: 6667, + buildings_with_denominator: 6143, + coverage_pct: 92.1, + max_pct: 42.0, + p95_pct: 11.3, + histogram: [ + { bucket: '0-5', count: 4980 }, + { bucket: '5-10', count: 642 }, + { bucket: '10-20', count: 298 }, + { bucket: '20-30', count: 121 }, + { bucket: '30-50', count: 57 }, + { bucket: '50-100', count: 18 }, + { bucket: '100+', count: 6 }, + ], +}; + +const HOUSE_TYPES = ['panel', 'brick', 'monolith', 'monolith_brick', 'block', 'stalin']; + +/** Порог 8% на слайдере: корзины ниже порога приглушены (opacity), тепловая + * окраска столбцов green→red повторяет цвет маркеров карты. Фильтры: город, + * цена, год постройки, тип дома, мин. объявлений, сортировка. */ +export const Default = () => ( + {}} + city="Екатеринбург" + onCity={() => {}} + priceMin="" + onPriceMin={() => {}} + priceMax="" + onPriceMax={() => {}} + yearMin="" + onYearMin={() => {}} + yearMax="" + onYearMax={() => {}} + houseType="" + onHouseType={() => {}} + houseTypeOptions={HOUSE_TYPES} + sort="share_desc" + onSort={() => {}} + window="now" + onWindow={() => {}} + minCount={3} + onMinCount={() => {}} + /> +); diff --git a/.design-sync/previews/SaleShareList.tsx b/.design-sync/previews/SaleShareList.tsx new file mode 100644 index 00000000..69c1fc9f --- /dev/null +++ b/.design-sync/previews/SaleShareList.tsx @@ -0,0 +1,50 @@ +import { SaleShareList } from 'tradein-mvp-frontend'; + +// Список домов /trade-in/sale-share, отсортирован по доле в продаже (server-side +// share_desc). Реалистичные дома ЕКБ (view v_building_sale_share): адрес, +// тепловой бейдж доли, «N из M квартир», медиана + ₽/м², экспозиция, +// год/тип/этажность/серия. Включены оба edge-бейджа: «аварийный» и +// «возможно, несколько корпусов» (over_100 — ГАР-коллизия адреса). +const buildings = [ + { house_id: 4212, address: 'ул. Космонавтов, 52', lat: 56.8890, lon: 60.6132, sale_share_pct: 128.0, sale_share_pct_45d: 131.5, listings_45d: 46, over_100: true, active_secondary: 32, flat_count_effective: 25, gar_match_method: 'address', median_price_rub: 3_650_000, median_price_per_m2: 121_700, avg_days_on_market: 96, year_built: 1961, house_type: 'brick', total_floors: 5, series_name: null, is_emergency: false }, + { house_id: 3387, address: 'пер. Сапёров, 5', lat: 56.8271, lon: 60.6198, sale_share_pct: 48.9, sale_share_pct_45d: 52.4, listings_45d: 24, over_100: false, active_secondary: 22, flat_count_effective: 45, gar_match_method: 'cadastre', median_price_rub: 2_990_000, median_price_per_m2: 98_400, avg_days_on_market: 148, year_built: 1957, house_type: 'brick', total_floors: 3, series_name: null, is_emergency: true }, + { house_id: 3101, address: 'ул. Викулова, 46', lat: 56.8412, lon: 60.5556, sale_share_pct: 34.6, sale_share_pct_45d: 41.2, listings_45d: 33, over_100: false, active_secondary: 27, flat_count_effective: 78, gar_match_method: 'cadastre', median_price_rub: 4_950_000, median_price_per_m2: 158_500, avg_days_on_market: 74, year_built: 1972, house_type: 'panel', total_floors: 9, series_name: '1-468', is_emergency: false }, + { house_id: 2874, address: 'ул. Бебеля, 138', lat: 56.8664, lon: 60.5721, sale_share_pct: 22.4, sale_share_pct_45d: 25.0, listings_45d: 20, over_100: false, active_secondary: 17, flat_count_effective: 76, gar_match_method: 'cadastre', median_price_rub: 5_400_000, median_price_per_m2: 149_200, avg_days_on_market: 61, year_built: 1978, house_type: 'panel', total_floors: 9, series_name: '141', is_emergency: false }, + { house_id: 5530, address: 'ул. Малышева, 84', lat: 56.8380, lon: 60.6203, sale_share_pct: 12.1, sale_share_pct_45d: 13.8, listings_45d: 16, over_100: false, active_secondary: 14, flat_count_effective: 116, gar_match_method: 'cadastre', median_price_rub: 7_850_000, median_price_per_m2: 172_300, avg_days_on_market: 47, year_built: 1954, house_type: 'stalin', total_floors: 6, series_name: null, is_emergency: false }, + { house_id: 6119, address: 'ул. Щербакова, 20', lat: 56.7791, lon: 60.6120, sale_share_pct: 6.8, sale_share_pct_45d: 8.1, listings_45d: 22, over_100: false, active_secondary: 18, flat_count_effective: 264, gar_match_method: 'cadastre', median_price_rub: 8_900_000, median_price_per_m2: 164_800, avg_days_on_market: 38, year_built: 2016, house_type: 'monolith', total_floors: 25, series_name: null, is_emergency: false }, +]; + +/** Полная таблица (6 домов, выбран «ул. Викулова, 46» — строка is-selected), + * сортировка по доле, кнопка «Объявления →» ведёт в drawer. */ +export const Default = () => ( + {}} + selectedHouseId={3101} + hoveredHouseId={null} + onSelect={() => {}} + onHover={() => {}} + isLoading={false} + isError={false} + minPct={5} + window="now" + /> +); + +/** Пустой результат — фильтры отсекли все дома (честный empty-state). */ +export const Empty = () => ( + {}} + selectedHouseId={null} + hoveredHouseId={null} + onSelect={() => {}} + onHover={() => {}} + isLoading={false} + isError={false} + minPct={25} + window="now" + /> +); diff --git a/.design-sync/previews/SaleShareMap.tsx b/.design-sync/previews/SaleShareMap.tsx new file mode 100644 index 00000000..dd840e90 --- /dev/null +++ b/.design-sync/previews/SaleShareMap.tsx @@ -0,0 +1,30 @@ +import { SaleShareMap } from 'tradein-mvp-frontend'; + +// Тепловая карта домов /trade-in/sale-share (Leaflet + OSM с CDN, как +// MapCard/MapPicker). Цвет и радиус circleMarker растут с sale_share_pct +// (green → red, over_100 → тёмно-красный). 8 домов ЕКБ с координатами; +// выбранный дом (selectedHouseId) получает открытый popup. OSM-тайлы — +// внешний fetch: в офлайн-capture подложка может не прогрузиться, +// маркеры/popup/контролы рендерятся всегда. +const buildings = [ + { house_id: 4212, address: 'ул. Космонавтов, 52', lat: 56.8890, lon: 60.6132, sale_share_pct: 128.0, sale_share_pct_45d: 131.5, listings_45d: 46, over_100: true, active_secondary: 32, flat_count_effective: 25, gar_match_method: 'address', median_price_rub: 3_650_000, median_price_per_m2: 121_700, avg_days_on_market: 96, year_built: 1961, house_type: 'brick', total_floors: 5, series_name: null, is_emergency: false }, + { house_id: 3387, address: 'пер. Сапёров, 5', lat: 56.8271, lon: 60.6198, sale_share_pct: 48.9, sale_share_pct_45d: 52.4, listings_45d: 24, over_100: false, active_secondary: 22, flat_count_effective: 45, gar_match_method: 'cadastre', median_price_rub: 2_990_000, median_price_per_m2: 98_400, avg_days_on_market: 148, year_built: 1957, house_type: 'brick', total_floors: 3, series_name: null, is_emergency: true }, + { house_id: 3101, address: 'ул. Викулова, 46', lat: 56.8412, lon: 60.5556, sale_share_pct: 34.6, sale_share_pct_45d: 41.2, listings_45d: 33, over_100: false, active_secondary: 27, flat_count_effective: 78, gar_match_method: 'cadastre', median_price_rub: 4_950_000, median_price_per_m2: 158_500, avg_days_on_market: 74, year_built: 1972, house_type: 'panel', total_floors: 9, series_name: '1-468', is_emergency: false }, + { house_id: 2874, address: 'ул. Бебеля, 138', lat: 56.8664, lon: 60.5721, sale_share_pct: 22.4, sale_share_pct_45d: 25.0, listings_45d: 20, over_100: false, active_secondary: 17, flat_count_effective: 76, gar_match_method: 'cadastre', median_price_rub: 5_400_000, median_price_per_m2: 149_200, avg_days_on_market: 61, year_built: 1978, house_type: 'panel', total_floors: 9, series_name: '141', is_emergency: false }, + { house_id: 5530, address: 'ул. Малышева, 84', lat: 56.8380, lon: 60.6203, sale_share_pct: 12.1, sale_share_pct_45d: 13.8, listings_45d: 16, over_100: false, active_secondary: 14, flat_count_effective: 116, gar_match_method: 'cadastre', median_price_rub: 7_850_000, median_price_per_m2: 172_300, avg_days_on_market: 47, year_built: 1954, house_type: 'stalin', total_floors: 6, series_name: null, is_emergency: false }, + { house_id: 6119, address: 'ул. Щербакова, 20', lat: 56.7791, lon: 60.6120, sale_share_pct: 6.8, sale_share_pct_45d: 8.1, listings_45d: 22, over_100: false, active_secondary: 18, flat_count_effective: 264, gar_match_method: 'cadastre', median_price_rub: 8_900_000, median_price_per_m2: 164_800, avg_days_on_market: 38, year_built: 2016, house_type: 'monolith', total_floors: 25, series_name: null, is_emergency: false }, + { house_id: 5871, address: 'ул. Крауля, 44', lat: 56.8443, lon: 60.5610, sale_share_pct: 17.9, sale_share_pct_45d: 19.6, listings_45d: 14, over_100: false, active_secondary: 12, flat_count_effective: 67, gar_match_method: 'cadastre', median_price_rub: 5_150_000, median_price_per_m2: 152_900, avg_days_on_market: 58, year_built: 1980, house_type: 'panel', total_floors: 9, series_name: '141', is_emergency: false }, + { house_id: 6402, address: 'ул. 8 Марта, 190', lat: 56.8043, lon: 60.6094, sale_share_pct: 3.4, sale_share_pct_45d: 4.0, listings_45d: 11, over_100: false, active_secondary: 9, flat_count_effective: 262, gar_match_method: 'cadastre', median_price_rub: 9_300_000, median_price_per_m2: 176_400, avg_days_on_market: 33, year_built: 2019, house_type: 'monolith', total_floors: 26, series_name: null, is_emergency: false }, +]; + +/** Карта ЕКБ: 8 тепловых маркеров, выбран «ул. Викулова, 46» — открыт popup + * (адрес, % в продаже, N из M квартир). */ +export const Default = () => ( + {}} + onHover={() => {}} + /> +); diff --git a/.design-sync/previews/ScheduleControl.tsx b/.design-sync/previews/ScheduleControl.tsx new file mode 100644 index 00000000..b92bd3fc --- /dev/null +++ b/.design-sync/previews/ScheduleControl.tsx @@ -0,0 +1,25 @@ +import { ScheduleControl } from 'tradein-mvp-frontend'; + +/** Расписание + ручной запуск city-sweep. Фетчит /admin/scrape/schedules через + * useSchedules (статус-блок fetch-coupled), но форма настроек (окно МСК, + * pages/anchor, detail top-N, delay, радиус, enrich) рендерится всегда — + * полный styled-каркас формы. paramConfig — avito-дефолты (с detail-параметрами). */ +const AVITO_PARAMS = { + hasDetailParams: true, + hasEnrichAddress: false, + defaults: { + pages_per_anchor: 3, + radius_m: 1500, + request_delay_sec: 4, + detail_top_n: 12, + enrich_houses: true, + }, +}; + +export const Default = () => ( + +); diff --git a/.design-sync/previews/SectionOverlay.tsx b/.design-sync/previews/SectionOverlay.tsx new file mode 100644 index 00000000..cdf122de --- /dev/null +++ b/.design-sync/previews/SectionOverlay.tsx @@ -0,0 +1,39 @@ +import { SectionOverlay } from 'tradein-mvp-frontend'; + +// Glass/blur оверлей секций HUD «МЕРА Оценка» (/trade-in/v2): скобки-уголки, +// шапка «номер секции + заголовок + ← К ОЦЕНКЕ», скроллируемое тело со +// сменной view. Позиционируется absolute относительно артборда — в preview +// рендерим contained внутри relative-обёртки с фоном v2-страницы. +// Данные не передаём: каждая view падает на свой встроенный fixture-набор +// (HISTORY_FIXTURE / ANALYTICS_FIXTURE …) — как storybook/unwired usage. + +const Artboard = ({ children }: { children?: unknown }) => ( +
+ {children as React.ReactNode} +
+); + +/** Секция 04 «ПРОДАЖИ В ДОМЕ» (active=1 → HistoryView на fixture-данных): + * таблица ДКП-продаж дома с ценами и датами. */ +export const HistorySection = () => ( + + {}} onNavigate={() => {}} /> + +); + +/** Секция 06 «АНАЛИТИКА ДОМА» (active=3 → AnalyticsView): KPI дома, + * динамика цены, недавние продажи. */ +export const AnalyticsSection = () => ( + + {}} onNavigate={() => {}} /> + +); diff --git a/.design-sync/previews/SellTimeSensitivity.tsx b/.design-sync/previews/SellTimeSensitivity.tsx new file mode 100644 index 00000000..804720c9 --- /dev/null +++ b/.design-sync/previews/SellTimeSensitivity.tsx @@ -0,0 +1,6 @@ +import { SellTimeSensitivity } from 'tradein-mvp-frontend'; +import { FIXTURE_SELLTIME } from './_fixtures'; + +/** «Срок продажи в зависимости от цены» — 4 бакета премии (−5% / медиана / +5% / + * +10%) с медианой экспозиции и p25–p75. Принимает data-пропом напрямую. */ +export const Default = () => ; diff --git a/.design-sync/previews/SourcesProgress.tsx b/.design-sync/previews/SourcesProgress.tsx new file mode 100644 index 00000000..83121a85 --- /dev/null +++ b/.design-sync/previews/SourcesProgress.tsx @@ -0,0 +1,6 @@ +import { SourcesProgress } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** Шаг B «Агрегация» — 7 строк-источников. Циан/Авито/Росреестр = done (с лотами), + * ДомКлик/Restate/Я.Недв/N1 = idle. isPending=false → финальное состояние, бар 100%. */ +export const Default = () => ; diff --git a/.design-sync/previews/StreetDealsCard.tsx b/.design-sync/previews/StreetDealsCard.tsx new file mode 100644 index 00000000..a0b51402 --- /dev/null +++ b/.design-sync/previews/StreetDealsCard.tsx @@ -0,0 +1,8 @@ +import { StreetDealsCard } from 'tradein-mvp-frontend'; +import { FIXTURE_ESTIMATE } from './_fixtures'; + +/** «По вашей улице» — ДКП-сделки Росреестра + историч. ASK. + * FETCH-COUPLED: данные ТОЛЬКО из useSalesVsListings (нет data-prop). Глобальный + * query-cache в preview пуст → isLoading/isError → `return null`. В офлайн-capture + * карточка рендерится пусто; на реальной странице — таблица сделок с привязкой к ASK. */ +export const Default = () => ; diff --git a/.design-sync/previews/SystemHealthSection.tsx b/.design-sync/previews/SystemHealthSection.tsx new file mode 100644 index 00000000..c74952c2 --- /dev/null +++ b/.design-sync/previews/SystemHealthSection.tsx @@ -0,0 +1,6 @@ +import { SystemHealthSection } from 'tradein-mvp-frontend'; + +/** Статус системы скраппера: fetch-режим, browser-сервис, прокси провайдеров. + * Фетчит /admin/scraper/health через useScraperHealth (data-prop нет). В превью + * без сети — заголовок + hint + loading/error. Fetch-coupled. */ +export const Default = () => ; diff --git a/.design-sync/previews/TestPresets.tsx b/.design-sync/previews/TestPresets.tsx new file mode 100644 index 00000000..506d9c35 --- /dev/null +++ b/.design-sync/previews/TestPresets.tsx @@ -0,0 +1,5 @@ +import { TestPresets } from 'tradein-mvp-frontend'; + +/** Быстрое заполнение формы тестовыми квартирами ЕКБ — сетка из 6 preset-чипов + * (адрес + подсказка по покрытию). Self-contained (`