gendesign/frontend/src/components/site-finder/ptica/cockpit/InsolationCard.tsx
Light1YT 8c43410ad2
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 57s
CI / openapi-codegen-check (pull_request) Successful in 1m59s
feat(ptica): live 3D massing in Инсоляция card + honest Recommended Product verdict
- 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.
2026-06-21 03:11:15 +05:00

55 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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>
);
}