gendesign/frontend/src/components/site-finder/ptica/InvestScoreBlock.tsx
Light1YT 95f93da83b feat(ptica): right-slider detail drawer system + 17 drawers wired (PR#4)
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.
2026-06-20 16:16:39 +05:00

83 lines
2.7 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";
/**
* InvestScoreBlock — hero score panel (col 3): big /10 invest-score number + a
* buy-signal badge. In INCREMENT 1 the invest-score and buy-signal are
* PLACEHOLDERS (surfaced after the §22 forecast / Scenarios), so the big number
* renders muted "—" with a caption instead of a fabricated value.
*/
import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";
import type { ParcelAnalysis } from "@/types/site-finder";
import type { ForecastReport } from "@/types/forecast";
import { adaptInvestScore } from "@/components/site-finder/ptica/ptica-adapt";
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
interface Props {
analysis: ParcelAnalysis;
/** §22 forecast report — when ready, surfaces the REAL invest-score / buy-signal. */
forecastReport?: ForecastReport;
onOpenDrawer: (key: DrawerKey) => void;
}
export function InvestScoreBlock({
analysis,
forecastReport,
onOpenDrawer,
}: Props) {
const inv = adaptInvestScore(analysis, forecastReport);
return (
<div className={styles.scoreStack}>
<div>
<div className={styles.statLine}>
<span className={styles.k}>Инвест-скор</span>
</div>
<div className={styles.investScore}>
{inv.score.isReal ? (
<>
<span className={styles.investBig}>{inv.score.value}</span>
<span className={styles.investSmall}>/ 10</span>
</>
) : (
<span className={`${styles.investBig} ${styles.investPlaceholder}`}>
</span>
)}
</div>
{inv.score.caption && (
<span className={styles.fieldCaption}>{inv.score.caption}</span>
)}
</div>
<div className={styles.statLine}>
<span className={styles.k}>Потенциал</span>
<span className={styles.v} title={inv.potential.caption}>
{inv.potential.value}
</span>
</div>
<div className={styles.statLine}>
<span className={styles.k}>Риск</span>
<span className={styles.v} title={inv.risk.caption}>
{inv.risk.value}
</span>
</div>
<div className={styles.statLine}>
<span className={styles.k}>Buy Signal</span>
<span className={styles.badge} title={inv.buySignal.caption}>
{inv.buySignal.isReal ? inv.buySignal.value : "после прогноза"}
</span>
</div>
<button
type="button"
className={styles.detailBtn}
onClick={() => onOpenDrawer("potential")}
title="Открыть расчёт ёмкости"
>
Подробнее
</button>
</div>
);
}