PticaDrawer: controlled right slider (480px / full-screen <768), scrim+Esc close, focus-on-open, body-scroll-lock (restored on close/unmount), dialog a11y. Deep-link ?drawer= via useSearchParams + shallow router.replace (no render loop; unknown keys ignored). Enables the «Подробнее» buttons on scan + hero cards. 17 drawer contents (passport/buildability/urban/restrictions/engineering/ market[4 sub-tabs]/economy/risks/georisks/oks/potential/product/finance/legal/ environment/insolation/future3d) built from ParcelAnalysis + forecast product_tz. Many are REAL: environment (noise/air_quality/wind/weather), georisks (geology/ hydrology/geotech_risk), market (competitors/market_trend/velocity/pipeline), restrictions (zouit/red_lines/zoning), engineering, passport. Absent fields (КСИТ/ОКС/финмодель/insolation) show honest «—»/«скоро» via ptica-adapt. All field paths verified against types. Dark theme scoped. TS strict, no any. Follow-up: drawer focus-trap + focus-restore for full modal a11y.
78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
"use client";
|
||
|
||
/**
|
||
* ParcelPassportCard — passport KV-grid (hero col 2). Mono cad number; every
|
||
* value flows from the adapter view-model, so placeholders render muted "—"
|
||
* with their caption rather than fake live data.
|
||
*/
|
||
|
||
import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";
|
||
import type { ParcelAnalysis } from "@/types/site-finder";
|
||
import { adaptPassport } from "@/components/site-finder/ptica/ptica-adapt";
|
||
import type {
|
||
PticaField,
|
||
PticaPassport,
|
||
} from "@/components/site-finder/ptica/ptica-adapt";
|
||
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
|
||
|
||
interface Props {
|
||
analysis: ParcelAnalysis;
|
||
onOpenDrawer: (key: DrawerKey) => void;
|
||
}
|
||
|
||
function KvCell({
|
||
label,
|
||
field,
|
||
wide,
|
||
}: {
|
||
label: string;
|
||
field: PticaField;
|
||
wide?: boolean;
|
||
}) {
|
||
return (
|
||
<div className={`${styles.kv} ${wide ? styles.kvWide : ""}`}>
|
||
<span className={styles.kvK}>{label}</span>
|
||
<span
|
||
className={`${styles.kvV} ${field.isReal ? "" : styles.kvPlaceholder}`}
|
||
>
|
||
{field.value}
|
||
</span>
|
||
{field.caption && !field.isReal && (
|
||
<span className={styles.kvCaption}>{field.caption}</span>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function ParcelPassportCard({ analysis, onOpenDrawer }: Props) {
|
||
const p: PticaPassport = adaptPassport(analysis);
|
||
|
||
return (
|
||
<div className={styles.panel}>
|
||
<div className={styles.cardHead}>
|
||
<h3 className={styles.cardTitle}>Паспорт участка</h3>
|
||
<button
|
||
type="button"
|
||
className={styles.detailBtn}
|
||
onClick={() => onOpenDrawer("passport")}
|
||
title="Открыть детализацию"
|
||
>
|
||
Подробнее
|
||
</button>
|
||
</div>
|
||
<div className={styles.kvGrid}>
|
||
<div className={`${styles.kv} ${styles.kvWide}`}>
|
||
<span className={styles.kvK}>Кадастровый номер</span>
|
||
<span className={`${styles.kvV} ${styles.mono}`}>{p.cadNum}</span>
|
||
</div>
|
||
<KvCell label="Район" field={p.district} />
|
||
<KvCell label="Площадь" field={p.area} />
|
||
<KvCell label="Адрес" field={p.address} wide />
|
||
<KvCell label="Категория земель" field={p.landCategory} />
|
||
<KvCell label="ВРИ" field={p.vri} />
|
||
<KvCell label="Статус" field={p.status} />
|
||
<KvCell label="Обременения" field={p.encumbrances} />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|