FE-2/3/4 on the FE-1 mappers architecture. Adds mapHistory/mapAnalytics/mapSources (+helpers) to mappers.ts; HistoryView/AnalyticsView/SourcesView accept composite data props (fixture default); SectionOverlay forwards overlay data; page.tsx adds useEstimatePlacementHistory/useSalesVsListings/useEstimateSellTimeSensitivity and feeds mapped data. FE-1 main dashboard untouched. - History: house sales (placement-history); ДКП KPI/header (street-deals + sales-vs-listings); per-deal rows with REAL addr/date/listing-source/discount. - Analytics: KPIs (экспозиция/торг/доля снятых), 4 sell-time tiers, price-history polylines computed from year×source, detail scatter. - Sources: ad rows from analogs (real source/distance/url via safeUrl), deal rows from actual_deals (real source/tier/date), market KPIs. - Un-hardcoded fabricated cells: DKP addr/date/source, deal date, deals footer count. - FE-approx (TODO BE-1 #2043): CV, per-source counts, days_on_market->listing_date. Verified: next build green (/v2 37.8 kB); tsx harness ran the 3 overlay mappers vs real estimate 701795d8 (houseSales/KPIs/sellTime/priceHistory/scatter/ad+deal rows correct). code-reviewer APPROVE after honesty fixes.
162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
"use client";
|
||
|
||
// Overlay wrapper for the nav-driven section panels of the /trade-in/v2
|
||
// "МЕРА Оценка" design. Faithful port of the design overlay container
|
||
// (МЕРА Оценка.dc.html lines 443-453 + 615-616): a glass/blur absolute layer
|
||
// with HUD corner brackets, a header (section number + title + "← К ОЦЕНКЕ"
|
||
// button) and a scrollable body that swaps the active view.
|
||
|
||
import { tokens } from "./tokens";
|
||
import { overlayNums, overlayTitles } from "./fixtures";
|
||
import HistoryView from "./HistoryView";
|
||
import SourcesView from "./SourcesView";
|
||
import AnalyticsView from "./AnalyticsView";
|
||
import { CacheView } from "./CacheView";
|
||
import type { HistoryData, SourcesData } from "./mappers";
|
||
import type { Analytics } from "./types";
|
||
|
||
interface SectionOverlayProps {
|
||
active: number;
|
||
onClose: () => void;
|
||
onNavigate: (i: number) => void;
|
||
// Mapped overlay data, fed by page.tsx (mapHistory / mapSources / mapAnalytics).
|
||
// Optional so unwired/storybook usage falls back to each view's fixture default.
|
||
history?: HistoryData;
|
||
sources?: SourcesData;
|
||
analytics?: Analytics;
|
||
}
|
||
|
||
export default function SectionOverlay({
|
||
active,
|
||
onClose,
|
||
onNavigate,
|
||
history,
|
||
sources,
|
||
analytics,
|
||
}: SectionOverlayProps) {
|
||
return (
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: 28,
|
||
right: 28,
|
||
top: 80,
|
||
bottom: 30,
|
||
zIndex: 30,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
background:
|
||
"linear-gradient(180deg,rgba(244,248,252,.985),rgba(235,242,250,.985))",
|
||
backdropFilter: "blur(12px)",
|
||
border: `1px solid ${tokens.line3}`,
|
||
borderRadius: 8,
|
||
boxShadow: "0 22px 60px rgba(40,80,130,.18)",
|
||
overflow: "hidden",
|
||
fontFamily: tokens.font.sans,
|
||
}}
|
||
>
|
||
<style>{`
|
||
.tiv2-so-back { transition: all .15s; }
|
||
.tiv2-so-back:hover { border-color: ${tokens.accent}; color: ${tokens.accent}; }
|
||
`}</style>
|
||
|
||
{/* HUD corner brackets */}
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: 12,
|
||
top: 12,
|
||
width: 15,
|
||
height: 15,
|
||
borderLeft: `1.5px solid ${tokens.accent}`,
|
||
borderTop: `1.5px solid ${tokens.accent}`,
|
||
pointerEvents: "none",
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
right: 12,
|
||
bottom: 12,
|
||
width: 15,
|
||
height: 15,
|
||
borderRight: `1.5px solid ${tokens.accent}`,
|
||
borderBottom: `1.5px solid ${tokens.accent}`,
|
||
pointerEvents: "none",
|
||
}}
|
||
/>
|
||
|
||
{/* overlay header */}
|
||
<div
|
||
style={{
|
||
flex: "0 0 auto",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
padding: "16px 24px",
|
||
borderBottom: "1px solid #cdd9e6",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", alignItems: "baseline", gap: 11 }}>
|
||
<span
|
||
style={{
|
||
fontFamily: tokens.font.mono,
|
||
fontSize: 15,
|
||
color: tokens.accent,
|
||
}}
|
||
>
|
||
{overlayNums[active]}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
letterSpacing: 2.5,
|
||
color: tokens.ink2,
|
||
}}
|
||
>
|
||
{overlayTitles[active]}
|
||
</span>
|
||
</div>
|
||
<div
|
||
onClick={onClose}
|
||
className="tiv2-so-back"
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
cursor: "pointer",
|
||
border: `1px solid ${tokens.line}`,
|
||
borderRadius: 6,
|
||
padding: "6px 12px",
|
||
background: tokens.surface.w60,
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 13 }}>←</span>
|
||
<span
|
||
style={{ fontSize: 10, letterSpacing: 1.5, color: tokens.muted }}
|
||
>
|
||
К ОЦЕНКЕ
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* overlay body */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
minHeight: 0,
|
||
overflowY: "auto",
|
||
padding: "22px 24px 26px",
|
||
}}
|
||
>
|
||
{active === 1 && <HistoryView data={history} />}
|
||
{active === 2 && <SourcesView data={sources} />}
|
||
{active === 3 && (
|
||
<AnalyticsView data={analytics} onNavigate={onNavigate} />
|
||
)}
|
||
{active === 4 && <CacheView />}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|