Compare commits
No commits in common. "721623bd0e44cce73328d3f6bf00e0aa4e18448b" and "5fb69b779eb24bb63a81561d8e67f2401a7b4d92" have entirely different histories.
721623bd0e
...
5fb69b779e
5 changed files with 4 additions and 306 deletions
|
|
@ -200,19 +200,6 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
/* Rail nav-foot help-dot (?) button (prototype .help-dot). */
|
|
||||||
.helpDot {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: var(--line) solid var(--border-strong);
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
font-size: 11px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-soft);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* main column */
|
/* main column */
|
||||||
.main {
|
.main {
|
||||||
|
|
@ -299,52 +286,15 @@
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
/* Topbar sysbar icon-buttons (prototype .icon-btn). */
|
|
||||||
.iconBtn {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border: var(--line) solid transparent;
|
|
||||||
border-radius: var(--radius-xs);
|
|
||||||
background: transparent;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
.iconBtn:hover {
|
|
||||||
color: var(--text);
|
|
||||||
background: var(--surface);
|
|
||||||
border-color: var(--border);
|
|
||||||
}
|
|
||||||
.sysbar .iconBtn + .iconBtn {
|
|
||||||
margin-left: -2px;
|
|
||||||
}
|
|
||||||
.iconBtn svg {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* canvas (scroll area) */
|
/* canvas (scroll area) */
|
||||||
.canvas {
|
.canvas {
|
||||||
padding: 14px 18px 8px;
|
padding: 14px 18px 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bottom disclaimer footer bar (prototype .app-footer). */
|
|
||||||
.appFooter {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 10px 20px 14px;
|
|
||||||
border-top: var(--line) solid var(--border);
|
|
||||||
color: var(--text-soft);
|
|
||||||
font-size: 9px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================== PANEL / CARD primitives ===================== */
|
/* ===================== PANEL / CARD primitives ===================== */
|
||||||
.panel,
|
.panel,
|
||||||
.card,
|
.card,
|
||||||
|
|
@ -613,27 +563,6 @@
|
||||||
.investPlaceholder {
|
.investPlaceholder {
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
}
|
}
|
||||||
/* Hero stat-line semantic value colors (prototype .v.good / .v.warn). */
|
|
||||||
.vGood {
|
|
||||||
color: var(--accent-green);
|
|
||||||
}
|
|
||||||
.vWarn {
|
|
||||||
color: var(--accent-yellow);
|
|
||||||
}
|
|
||||||
/* Hero invest-score sparkline (prototype .sparkline). */
|
|
||||||
.sparkline {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 3px;
|
|
||||||
height: 26px;
|
|
||||||
}
|
|
||||||
.sparkline span {
|
|
||||||
flex: 1;
|
|
||||||
background: linear-gradient(var(--accent-cyan), var(--accent-blue));
|
|
||||||
border-radius: 1px;
|
|
||||||
min-height: 3px;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
.badge {
|
.badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 9px;
|
padding: 2px 9px;
|
||||||
|
|
@ -783,46 +712,6 @@
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
/* Scan status-rows (prototype Инженерия / Экология .status-row): dot + label
|
|
||||||
+ state. Scoped under .scanCard so they never collide with the unrelated
|
|
||||||
DRAWER .statusRow variant (drDot / drLabel / drState). */
|
|
||||||
.scanCard .statusRow {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 3px 0;
|
|
||||||
}
|
|
||||||
.scanCard .dot {
|
|
||||||
width: 7px;
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--accent-green);
|
|
||||||
box-shadow: 0 0 6px currentColor;
|
|
||||||
color: var(--accent-green);
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
.scanCard .dotWarn {
|
|
||||||
background: var(--accent-yellow);
|
|
||||||
color: var(--accent-yellow);
|
|
||||||
}
|
|
||||||
.scanCard .dotBad {
|
|
||||||
background: var(--accent-red);
|
|
||||||
color: var(--accent-red);
|
|
||||||
}
|
|
||||||
.scanCard .label {
|
|
||||||
flex: 1;
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
.scanCard .state {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
.scanCard .stateOk {
|
|
||||||
color: var(--accent-green);
|
|
||||||
}
|
|
||||||
.scanCard .stateWarn {
|
|
||||||
color: var(--accent-yellow);
|
|
||||||
}
|
|
||||||
/* Risks card centres its gauge; title stays left-aligned like the other cards. */
|
/* Risks card centres its gauge; title stays left-aligned like the other cards. */
|
||||||
.scanCardCentered {
|
.scanCardCentered {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -2311,27 +2200,6 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.05fr 1.05fr 1.15fr 0.95fr;
|
grid-template-columns: 1.05fr 1.05fr 1.15fr 0.95fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
/* HEADLINE BUG FIX: default align-items:stretch made the sparse/empty ОКС
|
|
||||||
card (isoBox 84px + emptyState flex:1 + button) stretch to the tallest
|
|
||||||
sibling, and .emptyState's flex:1 then filled the void → huge empty box.
|
|
||||||
align-items:start lets each card size to its own content. */
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lower-grid квартирография bars match the prototype .bar-row (narrow label /
|
|
||||||
value columns, 2px track radius) — SCOPED so the shared base .hbar used by
|
|
||||||
the drawer (110px 1fr 46px, 3px radius) is NOT crushed. */
|
|
||||||
.lowerGrid .hbars {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.lowerGrid .hbar {
|
|
||||||
grid-template-columns: 46px 1fr 38px;
|
|
||||||
}
|
|
||||||
.lowerGrid .hbarTrack {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.lowerGrid .hbarFill {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shared detail-btn that needs top separation inside flow cards */
|
/* shared detail-btn that needs top separation inside flow cards */
|
||||||
|
|
@ -2412,7 +2280,7 @@
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
margin-bottom: 1px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Инсоляционная матрица — faint preview massing + ПРЕВЬЮ placeholder */
|
/* Инсоляционная матрица — faint preview massing + ПРЕВЬЮ placeholder */
|
||||||
|
|
@ -2473,9 +2341,6 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5fr 0.62fr 0.86fr 1.2fr;
|
grid-template-columns: 1.5fr 0.62fr 0.86fr 1.2fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
/* Same grid-stretch defect as .lowerGrid — sparse Legal Status / cards must
|
|
||||||
not stretch to the tallest sibling. Pin each card to the top. */
|
|
||||||
align-items: start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Investment Clearance — bignum row */
|
/* Investment Clearance — bignum row */
|
||||||
|
|
@ -2569,9 +2434,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* Prototype .verdict-card plaque gradient — scoped dark cockpit (authoritative
|
|
||||||
here over the global flat-token rule). */
|
|
||||||
background: linear-gradient(135deg, var(--surface), var(--surface-2));
|
|
||||||
}
|
}
|
||||||
.verdictSplit {
|
.verdictSplit {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
@ -2632,8 +2494,6 @@
|
||||||
}
|
}
|
||||||
.checkItemWarn .checkIc {
|
.checkItemWarn .checkIc {
|
||||||
color: var(--accent-yellow);
|
color: var(--accent-yellow);
|
||||||
/* Prototype removes the icon border ring on warn checklist items. */
|
|
||||||
border: none;
|
|
||||||
}
|
}
|
||||||
.checkItemBad .checkIc {
|
.checkItemBad .checkIc {
|
||||||
color: var(--accent-red);
|
color: var(--accent-red);
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,8 @@ import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";
|
||||||
import type { ParcelAnalysis } from "@/types/site-finder";
|
import type { ParcelAnalysis } from "@/types/site-finder";
|
||||||
import type { ForecastReport } from "@/types/forecast";
|
import type { ForecastReport } from "@/types/forecast";
|
||||||
import { adaptInvestScore } from "@/components/site-finder/ptica/ptica-adapt";
|
import { adaptInvestScore } from "@/components/site-finder/ptica/ptica-adapt";
|
||||||
import type { PticaValueTone } from "@/components/site-finder/ptica/ptica-adapt";
|
|
||||||
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
|
import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys";
|
||||||
|
|
||||||
/** Decorative hero sparkline shape (prototype data-spark="4,5,6,5,7,6,8,7,9,8"). */
|
|
||||||
const SPARK = [4, 5, 6, 5, 7, 6, 8, 7, 9, 8];
|
|
||||||
const SPARK_MAX = Math.max(...SPARK);
|
|
||||||
|
|
||||||
function toneClass(tone: PticaValueTone): string {
|
|
||||||
if (tone === "good") return styles.vGood;
|
|
||||||
if (tone === "warn") return styles.vWarn;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
analysis: ParcelAnalysis;
|
analysis: ParcelAnalysis;
|
||||||
/** §22 forecast report — when ready, surfaces the REAL invest-score / buy-signal. */
|
/** §22 forecast report — when ready, surfaces the REAL invest-score / buy-signal. */
|
||||||
|
|
@ -59,31 +48,17 @@ export function InvestScoreBlock({
|
||||||
{inv.score.caption && (
|
{inv.score.caption && (
|
||||||
<span className={styles.fieldCaption}>{inv.score.caption}</span>
|
<span className={styles.fieldCaption}>{inv.score.caption}</span>
|
||||||
)}
|
)}
|
||||||
<div className={styles.sparkline} aria-hidden="true">
|
|
||||||
{SPARK.map((v, i) => (
|
|
||||||
<span
|
|
||||||
key={i}
|
|
||||||
style={{ height: `${Math.round((v / SPARK_MAX) * 100)}%` }}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.statLine}>
|
<div className={styles.statLine}>
|
||||||
<span className={styles.k}>Потенциал</span>
|
<span className={styles.k}>Потенциал</span>
|
||||||
<span
|
<span className={styles.v} title={inv.potential.caption}>
|
||||||
className={`${styles.v} ${toneClass(inv.potentialTone)}`}
|
|
||||||
title={inv.potential.caption}
|
|
||||||
>
|
|
||||||
{inv.potential.value}
|
{inv.potential.value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statLine}>
|
<div className={styles.statLine}>
|
||||||
<span className={styles.k}>Риск</span>
|
<span className={styles.k}>Риск</span>
|
||||||
<span
|
<span className={styles.v} title={inv.risk.caption}>
|
||||||
className={`${styles.v} ${toneClass(inv.riskTone)}`}
|
|
||||||
title={inv.risk.caption}
|
|
||||||
>
|
|
||||||
{inv.risk.value}
|
{inv.risk.value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -215,16 +215,6 @@ const BirdMark = ({ className }: { className: string }) => (
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Toggle native fullscreen for the whole document (topbar fullscreen icon). */
|
|
||||||
function toggleFullscreen() {
|
|
||||||
if (typeof document === "undefined") return;
|
|
||||||
if (document.fullscreenElement) {
|
|
||||||
void document.exitFullscreen?.();
|
|
||||||
} else {
|
|
||||||
void document.documentElement.requestFullscreen?.();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
activeTab: PticaTab;
|
activeTab: PticaTab;
|
||||||
onTabChange: (tab: PticaTab) => void;
|
onTabChange: (tab: PticaTab) => void;
|
||||||
|
|
@ -304,15 +294,6 @@ export function PticaShell({ activeTab, onTabChange, children }: Props) {
|
||||||
<br />
|
<br />
|
||||||
{clock ?? "—"}
|
{clock ?? "—"}
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.helpDot}
|
|
||||||
title="Справка — скоро"
|
|
||||||
aria-label="Справка"
|
|
||||||
aria-disabled="true"
|
|
||||||
>
|
|
||||||
?
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
|
@ -349,76 +330,10 @@ export function PticaShell({ activeTab, onTabChange, children }: Props) {
|
||||||
<div className={styles.clock} suppressHydrationWarning>
|
<div className={styles.clock} suppressHydrationWarning>
|
||||||
<b>{clock ?? "—"}</b>
|
<b>{clock ?? "—"}</b>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.iconBtn}
|
|
||||||
title="Экспорт / отчёты"
|
|
||||||
aria-label="Экспорт — открыть отчёты"
|
|
||||||
onClick={() => onTabChange("reports")}
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
||||||
<path
|
|
||||||
d="M12 3v12m0 0l-4-4m4 4l4-4M4 17v3h16v-3"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.6"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.iconBtn}
|
|
||||||
title="Настройки — скоро"
|
|
||||||
aria-label="Настройки"
|
|
||||||
aria-disabled="true"
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
||||||
<circle
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="3.2"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.6"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M12 2.5l1.4 2.2 2.5-.7.6 2.6 2.6.6-.7 2.5 2.2 1.4-2.2 1.4.7 2.5-2.6.6-.6 2.6-2.5-.7L12 21.5l-1.4-2.2-2.5.7-.6-2.6-2.6-.6.7-2.5L3.4 13l2.2-1.4-.7-2.5 2.6-.6.6-2.6 2.5.7L12 2.5z"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.4"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.iconBtn}
|
|
||||||
title="Полный экран"
|
|
||||||
aria-label="Полный экран"
|
|
||||||
onClick={toggleFullscreen}
|
|
||||||
>
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
||||||
<path
|
|
||||||
d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.6"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={styles.canvas}>{children}</div>
|
<div className={styles.canvas}>{children}</div>
|
||||||
|
|
||||||
<footer className={styles.appFooter}>
|
|
||||||
<span>
|
|
||||||
ПТИЦА · платформа территориального информационно-цифрового анализа
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
Не является публичной офертой · информация носит оценочный характер
|
|
||||||
</span>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -36,30 +36,6 @@ export function ScanCard({ card, drawerKey, onOpen }: Props) {
|
||||||
<div className={styles.emptyState}>{emptyState}</div>
|
<div className={styles.emptyState}>{emptyState}</div>
|
||||||
) : (
|
) : (
|
||||||
rows.map((row) => {
|
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;
|
const placeholder = !row.field.isReal;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -49,18 +49,9 @@ export interface PticaScanCard {
|
||||||
emptyState?: string;
|
emptyState?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tone for a status-row dot + state label (prototype .dot / .state). */
|
|
||||||
export type PticaStatusTone = "ok" | "warn" | "bad";
|
|
||||||
|
|
||||||
export interface PticaScanRow {
|
export interface PticaScanRow {
|
||||||
key: string;
|
key: string;
|
||||||
field: PticaField;
|
field: PticaField;
|
||||||
/**
|
|
||||||
* When set, the row renders as a prototype status-row (dot + label + state)
|
|
||||||
* instead of the default kvrow. `field.value` is the state text. Used by the
|
|
||||||
* Инженерия card (Экология stays honest kvrow placeholders, no status dots).
|
|
||||||
*/
|
|
||||||
status?: PticaStatusTone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PLACEHOLDER = "—";
|
const PLACEHOLDER = "—";
|
||||||
|
|
@ -243,18 +234,11 @@ export function adaptRiskGauge(a: ParcelAnalysis): PticaGauge {
|
||||||
|
|
||||||
// ── Invest score block (PLACEHOLDER — после прогноза) ─────────────────────────
|
// ── Invest score block (PLACEHOLDER — после прогноза) ─────────────────────────
|
||||||
|
|
||||||
/** Semantic value tone for a hero stat-line (prototype .v.good / .v.warn). */
|
|
||||||
export type PticaValueTone = "good" | "warn" | "none";
|
|
||||||
|
|
||||||
export interface PticaInvestScore {
|
export interface PticaInvestScore {
|
||||||
score: PticaField;
|
score: PticaField;
|
||||||
potential: PticaField;
|
potential: PticaField;
|
||||||
risk: PticaField;
|
risk: PticaField;
|
||||||
buySignal: PticaField;
|
buySignal: PticaField;
|
||||||
/** Hero stat-line value color for «Потенциал». */
|
|
||||||
potentialTone: PticaValueTone;
|
|
||||||
/** Hero stat-line value color for «Риск». */
|
|
||||||
riskTone: PticaValueTone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -291,9 +275,6 @@ export function adaptInvestScore(
|
||||||
risk.value != null
|
risk.value != null
|
||||||
? { value: risk.label, isReal: false, caption: "предв." }
|
? { value: risk.label, isReal: false, caption: "предв." }
|
||||||
: placeholder("предв.");
|
: placeholder("предв.");
|
||||||
// Hero «Риск» value color (prototype .v.warn): green when low, yellow otherwise.
|
|
||||||
const riskTone: PticaValueTone =
|
|
||||||
risk.value == null ? "none" : risk.tone === "good" ? "good" : "warn";
|
|
||||||
|
|
||||||
const overall =
|
const overall =
|
||||||
report?.scoring?.overall ?? report?.exec_summary.key_numbers.overall_score;
|
report?.scoring?.overall ?? report?.exec_summary.key_numbers.overall_score;
|
||||||
|
|
@ -313,9 +294,6 @@ export function adaptInvestScore(
|
||||||
potential: placeholder("после финмодели"),
|
potential: placeholder("после финмодели"),
|
||||||
risk: riskField,
|
risk: riskField,
|
||||||
buySignal,
|
buySignal,
|
||||||
// Потенциал stays muted until the financial model surfaces a real value.
|
|
||||||
potentialTone: "none",
|
|
||||||
riskTone,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,8 +302,6 @@ export function adaptInvestScore(
|
||||||
potential: placeholder("после прогноза (Scenarios)"),
|
potential: placeholder("после прогноза (Scenarios)"),
|
||||||
risk: riskField,
|
risk: riskField,
|
||||||
buySignal,
|
buySignal,
|
||||||
potentialTone: "none",
|
|
||||||
riskTone,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -428,13 +404,9 @@ export function adaptEngineeringCard(a: ParcelAnalysis): PticaScanCard {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
title: "Инженерия",
|
title: "Инженерия",
|
||||||
// Prototype Инженерия renders status-rows (dot + label + state). The state
|
|
||||||
// text is the nearest-node distance; tone is «ok» when the узел is within
|
|
||||||
// ~1 km, «warn» when further (ограниченная доступность).
|
|
||||||
rows: summary.map((u) => ({
|
rows: summary.map((u) => ({
|
||||||
key: utilityLabel(u.subtype),
|
key: utilityLabel(u.subtype),
|
||||||
field: real(`${formatInt(u.nearest_m)} м`),
|
field: real(`${formatInt(u.nearest_m)} м`),
|
||||||
status: (u.nearest_m <= 1000 ? "ok" : "warn") as PticaStatusTone,
|
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue