- MassingScene: preview prop — viewport-only, hands-off autorotate (OrbitControls input disabled, autoRotate on), canvas pointer-events:none; teardown preserved. - InsolationCard: replaces the abstract SVG cube with a live autorotating massing built from real parcel area + НСПД max_far (farIsReal). «Открыть 3D» still opens the full interactive drawer. - RecommendedProductCard: degenerate mix no longer shows «сигнал недоступен» — it shows the real advisory verdict «Избегать» + reason (затоварка · индекс дефицита из mix.deficit_index) + product_tz.gate_caveat. Meaningful mixes still render quartirography bars. «Подробнее» → product drawer (full reasons). - drop dead iso-cube CSS (pticaIsoSpin/.isoBoxPreview/.isoPreview*); add .massingPreviewViewport + verdict styles (tokens, dark-scoped). - forecast type: product_tz.gate_caveat. Clickability sweep: no dead controls found; cockpit already cleanly wired.
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
"use client";
|
||
|
||
/**
|
||
* InsolationCard — lower-grid «Инсоляционная матрица» preview card. Renders a
|
||
* LIVE compact autorotating 3D massing model (a hands-off mini version of the
|
||
* «Открыть 3D» drawer) instead of the old abstract SVG cube, fed by the REAL
|
||
* parcel area (geometry) + НСПД КСИТ (max_far). «Открыть 3D» opens the
|
||
* insolation drawer (3D massing + shadow module).
|
||
*/
|
||
|
||
import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";
|
||
import type { ParcelAnalysis } from "@/types/site-finder";
|
||
import { parcelAreaM2 } from "@/components/site-finder/ptica/ptica-adapt";
|
||
import { MassingViewer } from "@/components/site-finder/ptica/massing/MassingViewer";
|
||
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
|
||
|
||
interface Props {
|
||
analysis: ParcelAnalysis;
|
||
onOpenDrawer: (key: DrawerKey) => void;
|
||
}
|
||
|
||
export function InsolationCard({ analysis, onOpenDrawer }: Props) {
|
||
const areaM2 = parcelAreaM2(analysis) ?? undefined;
|
||
const maxFar = analysis.nspd_zoning?.max_far ?? undefined;
|
||
const farIsReal = maxFar != null;
|
||
|
||
return (
|
||
<div className={styles.card}>
|
||
<div className={styles.cardHead}>
|
||
<h3 className={styles.cardTitle}>Инсоляционная матрица</h3>
|
||
<span className={styles.openTag}>превью</span>
|
||
</div>
|
||
|
||
<MassingViewer
|
||
preview
|
||
areaM2={areaM2}
|
||
maxFar={maxFar}
|
||
farIsReal={farIsReal}
|
||
/>
|
||
|
||
<div className={styles.placeholder}>
|
||
<div className={styles.placeholderSoon}>ПРЕВЬЮ</div>
|
||
<p>Тени по времени суток — в 3D-модуле массы застройки.</p>
|
||
<button
|
||
type="button"
|
||
className={styles.detailBtn}
|
||
onClick={() => onOpenDrawer("insolation")}
|
||
title="Открыть 3D-модуль"
|
||
>
|
||
Открыть 3D
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|