gendesign/frontend/src/components/site-finder/ptica/ScanCard.tsx
Light1YT fcaa4d197b
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 59s
CI / openapi-codegen-check (pull_request) Successful in 1m57s
fix(ptica): visual parity 1:1 with prototype (incl. ОКС empty-card stretch)
Multi-region audit vs the prototype styles.css/dashboard.html → 22 CSS fixes +
markup. Headline: .lowerGrid/.bottomGrid align-items:start so the sparse ОКС
«нет данных» card no longer stretches into a huge void. Plus restore missing
prototype bits: topbar icon-buttons (Экспорт→Отчёты, Полный экран→fullscreen,
Настройки aria-disabled «скоро»), rail help-dot, app-footer (honest disclaimer,
no fabricated model/timestamp), invest-score sparkline (decorative, aria-hidden),
green/amber value tones, engineering status-row dots (from real nearest_m),
verdict-card gradient, scan-card status-rows, .canvas/.hbar/.mixHead spacing.

All new CSS scoped under .pticaRoot[data-theme=dark] (status-row classes
descendant-scoped under .scanCard — no collision with the drawer's). Environment
card stays honest kvrow placeholders (no fake dots). a11y: aria-labels on icon
buttons. TS strict, no any. tsc/lint/prettier/build green. code-reviewer .
2026-06-21 01:10:36 +05:00

96 lines
3.2 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";
/**
* ScanCard — generic dark cockpit card: title (+ optional badge), metric rows,
* and a "Подробнее" button that opens the card's detail drawer.
*
* When `drawerKey` + `onOpen` are provided the button is ENABLED and opens the
* matching drawer; without them it stays disabled (no drill-down wired). Each
* row's value flows from a {value, isReal, caption} view-model: placeholder
* fields are rendered muted with their caption so they never read as live data.
*/
import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";
import type { PticaScanCard } from "@/components/site-finder/ptica/ptica-adapt";
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
interface Props {
card: PticaScanCard;
/** When set (with onOpen) the "Подробнее" button opens this drawer. */
drawerKey?: DrawerKey;
onOpen?: (key: DrawerKey) => void;
}
export function ScanCard({ card, drawerKey, onOpen }: Props) {
const { title, badge, rows, emptyState } = card;
const canOpen = Boolean(drawerKey && onOpen);
return (
<div className={`${styles.card} ${styles.scanCard}`}>
<h3 className={styles.cardTitle}>
{title}
{badge && <span className={styles.cardTitleSub}>{badge}</span>}
</h3>
{emptyState ? (
<div className={styles.emptyState}>{emptyState}</div>
) : (
rows.map((row) => {
// Status-row variant (prototype .status-row): dot + label + state.
if (row.status) {
const dotClass =
row.status === "warn"
? styles.dotWarn
: row.status === "bad"
? styles.dotBad
: "";
const stateClass =
row.status === "ok" ? styles.stateOk : styles.stateWarn;
return (
<div key={row.key} className={styles.statusRow}>
<span className={`${styles.dot} ${dotClass}`} />
<span className={styles.label}>{row.key}</span>
<span
className={`${styles.state} ${stateClass}`}
title={row.field.caption}
>
{row.field.value}
</span>
</div>
);
}
const placeholder = !row.field.isReal;
return (
<div
key={row.key}
className={`${styles.kvrow} ${
placeholder ? styles.kvrowPlaceholder : ""
}`}
>
<span className={styles.k}>{row.key}</span>
<span className={styles.v} title={row.field.caption}>
{row.field.value}
</span>
</div>
);
})
)}
<button
type="button"
className={styles.detailBtn}
disabled={!canOpen}
aria-disabled={!canOpen}
title={
canOpen ? "Открыть детализацию" : "Детализация — в следующем релизе"
}
onClick={
canOpen && drawerKey && onOpen ? () => onOpen(drawerKey) : undefined
}
>
Подробнее
</button>
</div>
);
}