Port the approved ptica-v2 prototype to a new non-destructive route
/site-finder/analysis/{cad}/v2 — dark/light cockpit shell, real Leaflet
map (reuses PticaMapInner), 6 scan cards, lower/bottom grids, 11 drawers
and grounded chat. All values flow through the existing ptica-adapt layer
(real /analyze data or honest «—»). One beta link added on the analysis page.
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
"use client";
|
||
|
||
/**
|
||
* V2Gauge — SVG progress ring (prototype `.gauge-wrap` / `.minigauge`). Renders a
|
||
* faint track + a colored arc whose length maps a 0..100 value. `null` value →
|
||
* muted "—" center (honest no-data, never a fake percentage). Tone selects the
|
||
* arc color via the scoped CSS tokens.
|
||
*
|
||
* Geometry mirrors the prototype: r=50 on a 120-box (big), r=30 on a 74-box
|
||
* (mini), r=26 on a 66-box (buy). dashoffset = circumference × (1 − value/100).
|
||
*/
|
||
|
||
import styles from "@/app/site-finder/analysis/[cad]/v2/v2.module.css";
|
||
import type { PticaGauge } from "@/components/site-finder/ptica/ptica-adapt";
|
||
|
||
type Size = "big" | "mini" | "buy";
|
||
|
||
const GEOM: Record<Size, { box: number; r: number; stroke: number }> = {
|
||
big: { box: 120, r: 50, stroke: 9 },
|
||
mini: { box: 74, r: 30, stroke: 7 },
|
||
buy: { box: 66, r: 26, stroke: 7 },
|
||
};
|
||
|
||
// Tone → stroke color (map exception: Leaflet/SVG can't read CSS custom props in
|
||
// stroke attr reliably across themes, so we reference the var() here directly).
|
||
const TONE_STROKE: Record<PticaGauge["tone"], string> = {
|
||
good: "var(--accent-green)",
|
||
warn: "var(--accent-yellow)",
|
||
bad: "var(--accent-red)",
|
||
neutral: "var(--accent-cyan)",
|
||
};
|
||
|
||
interface Props {
|
||
gauge: PticaGauge;
|
||
size?: Size;
|
||
}
|
||
|
||
export function V2Gauge({ gauge, size = "big" }: Props) {
|
||
const { box, r, stroke } = GEOM[size];
|
||
const c = box / 2;
|
||
const circ = 2 * Math.PI * r;
|
||
const pct = gauge.value == null ? 0 : Math.max(0, Math.min(100, gauge.value));
|
||
const offset = circ * (1 - pct / 100);
|
||
const color = gauge.value == null ? "var(--text-soft)" : TONE_STROKE[gauge.tone];
|
||
|
||
return (
|
||
<svg viewBox={`0 0 ${box} ${box}`} aria-hidden="true">
|
||
<circle
|
||
cx={c}
|
||
cy={c}
|
||
r={r}
|
||
fill="none"
|
||
stroke="var(--border-faint)"
|
||
strokeWidth={stroke}
|
||
/>
|
||
{gauge.value != null && (
|
||
<circle
|
||
cx={c}
|
||
cy={c}
|
||
r={r}
|
||
fill="none"
|
||
stroke={color}
|
||
strokeWidth={stroke}
|
||
strokeLinecap="round"
|
||
strokeDasharray={circ}
|
||
strokeDashoffset={offset}
|
||
style={{ filter: "drop-shadow(0 0 5px var(--glow))" }}
|
||
/>
|
||
)}
|
||
</svg>
|
||
);
|
||
}
|