The ПТИЦА dark "operator terminal" skin was applied to the main
/site-finder map page in b3fd053 ("dark Site Finder"), but the
2026-06-21 rollback (#1865) only reverted the analysis route — the
entry-landing stayed dark.
Точечно откатывает 8 файлов entry-landing (page.tsx + 7 entry-компонентов)
до состояния b3fd053~1 и удаляет осиротевший site-finder.module.css.
НЕ трогает: PticaMapInner / ptica.module.css (используются и старым ptica,
и Site Finder v2 cockpit) — карта аналитики остаётся glyph-pin как была.
375 lines
9.8 KiB
TypeScript
375 lines
9.8 KiB
TypeScript
"use client";
|
||
|
||
import Link from "next/link";
|
||
import { X, MapPin, Maximize2, ArrowRight } from "lucide-react";
|
||
import type { ParcelBboxItem } from "@/lib/site-finder-api";
|
||
import { addLocalRecentParcel } from "@/lib/site-finder-api";
|
||
import { STATUS_COLORS, STATUS_LABELS } from "./ParcelLegend";
|
||
|
||
interface ParcelDrawerProps {
|
||
parcel: ParcelBboxItem | null;
|
||
onClose: () => void;
|
||
}
|
||
|
||
const VRI_LABELS: Record<string, string> = {
|
||
multistory: "Многоэтажный",
|
||
mixed: "Смешанный",
|
||
individual: "ИЖС",
|
||
office: "Офисный",
|
||
other: "Иное",
|
||
};
|
||
|
||
export function ParcelDrawer({ parcel, onClose }: ParcelDrawerProps) {
|
||
if (!parcel) return null;
|
||
|
||
const statusColor = STATUS_COLORS[parcel.status] ?? "#73767E";
|
||
const statusLabel = STATUS_LABELS[parcel.status] ?? parcel.status;
|
||
|
||
return (
|
||
// Drawer panel — absolute within the map container (parent has position:relative).
|
||
// This keeps the sidebar (CadInput/RecentParcels/ParcelLegend) always visible
|
||
// and only overlaps the map area on parcel select.
|
||
<aside
|
||
style={{
|
||
position: "absolute",
|
||
top: 16,
|
||
right: 16,
|
||
bottom: 16,
|
||
width: 360,
|
||
zIndex: 20,
|
||
background: "var(--bg-card)",
|
||
borderLeft: "1px solid var(--border-card)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 12,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
boxShadow: "-4px 0 24px rgba(0,0,0,0.10)",
|
||
}}
|
||
role="dialog"
|
||
aria-label="Карточка участка"
|
||
>
|
||
{/* Header */}
|
||
<div
|
||
style={{
|
||
padding: "16px 20px 12px",
|
||
borderBottom: "1px solid var(--border-soft)",
|
||
display: "flex",
|
||
alignItems: "flex-start",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<MapPin
|
||
size={16}
|
||
strokeWidth={1.5}
|
||
style={{
|
||
color: "var(--fg-secondary)",
|
||
marginTop: 2,
|
||
flexShrink: 0,
|
||
}}
|
||
/>
|
||
<div style={{ flex: 1, minWidth: 0 }}>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-tertiary)",
|
||
fontVariantNumeric: "tabular-nums",
|
||
}}
|
||
>
|
||
{parcel.cad_num}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 13,
|
||
color: "var(--fg-secondary)",
|
||
marginTop: 2,
|
||
overflow: "hidden",
|
||
textOverflow: "ellipsis",
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{parcel.address}
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={onClose}
|
||
aria-label="Закрыть"
|
||
style={{
|
||
background: "none",
|
||
border: "none",
|
||
cursor: "pointer",
|
||
padding: 4,
|
||
color: "var(--fg-tertiary)",
|
||
flexShrink: 0,
|
||
}}
|
||
>
|
||
<X size={18} strokeWidth={1.5} />
|
||
</button>
|
||
</div>
|
||
|
||
{/* KPI grid */}
|
||
<div
|
||
style={{
|
||
padding: "16px 20px",
|
||
display: "grid",
|
||
gridTemplateColumns: "1fr 1fr",
|
||
gap: 12,
|
||
}}
|
||
>
|
||
{/* Status */}
|
||
<div
|
||
style={{
|
||
background: "var(--bg-card-alt)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 8,
|
||
padding: "10px 12px",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-secondary)",
|
||
marginBottom: 4,
|
||
}}
|
||
>
|
||
Статус
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: "50%",
|
||
background: statusColor,
|
||
flexShrink: 0,
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
color: "var(--fg-primary)",
|
||
}}
|
||
>
|
||
{statusLabel}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Area */}
|
||
<div
|
||
style={{
|
||
background: "var(--bg-card-alt)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 8,
|
||
padding: "10px 12px",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-secondary)",
|
||
marginBottom: 4,
|
||
}}
|
||
>
|
||
Площадь
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 18,
|
||
fontWeight: 600,
|
||
color: "var(--fg-primary)",
|
||
fontVariantNumeric: "tabular-nums",
|
||
}}
|
||
>
|
||
{parcel.area_ha != null ? parcel.area_ha.toFixed(2) : "—"}{" "}
|
||
<span
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 400,
|
||
color: "var(--fg-secondary)",
|
||
}}
|
||
>
|
||
га
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* District */}
|
||
<div
|
||
style={{
|
||
background: "var(--bg-card-alt)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 8,
|
||
padding: "10px 12px",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-secondary)",
|
||
marginBottom: 4,
|
||
}}
|
||
>
|
||
Район
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
color: "var(--fg-primary)",
|
||
}}
|
||
>
|
||
{parcel.district}
|
||
</div>
|
||
</div>
|
||
|
||
{/* VRI */}
|
||
<div
|
||
style={{
|
||
background: "var(--bg-card-alt)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 8,
|
||
padding: "10px 12px",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-secondary)",
|
||
marginBottom: 4,
|
||
}}
|
||
>
|
||
ВРИ
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
color: "var(--fg-primary)",
|
||
}}
|
||
>
|
||
{VRI_LABELS[parcel.vri] ?? parcel.vri}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Coords */}
|
||
<div
|
||
style={{
|
||
background: "var(--bg-card-alt)",
|
||
border: "1px solid var(--border-card)",
|
||
borderRadius: 8,
|
||
padding: "10px 12px",
|
||
gridColumn: "1 / -1",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.04em",
|
||
color: "var(--fg-secondary)",
|
||
marginBottom: 4,
|
||
}}
|
||
>
|
||
Координаты
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 12,
|
||
color: "var(--fg-secondary)",
|
||
fontVariantNumeric: "tabular-nums",
|
||
}}
|
||
>
|
||
{parcel.lat.toFixed(5)}, {parcel.lon.toFixed(5)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Spacer */}
|
||
<div style={{ flex: 1 }} />
|
||
|
||
{/* CTA */}
|
||
<div
|
||
style={{
|
||
padding: "16px 20px",
|
||
borderTop: "1px solid var(--border-soft)",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<Link
|
||
href={`/site-finder/analysis/${encodeURIComponent(parcel.cad_num)}`}
|
||
onClick={() => {
|
||
// Record the visit so this entry path also lands in RecentParcels (#1477).
|
||
addLocalRecentParcel({
|
||
cad_num: parcel.cad_num,
|
||
address: parcel.address,
|
||
area_ha: parcel.area_ha,
|
||
district: parcel.district,
|
||
visited_at: new Date().toISOString(),
|
||
});
|
||
}}
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 8,
|
||
background: "var(--accent)",
|
||
color: "#fff",
|
||
borderRadius: 8,
|
||
padding: "10px 20px",
|
||
fontSize: 14,
|
||
fontWeight: 500,
|
||
textDecoration: "none",
|
||
}}
|
||
>
|
||
Открыть анализ
|
||
<ArrowRight size={16} strokeWidth={1.5} />
|
||
</Link>
|
||
<a
|
||
href={`https://pkk.rosreestr.ru/#/search/cadastralNumber/${encodeURIComponent(parcel.cad_num)}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 8,
|
||
background: "none",
|
||
color: "var(--fg-secondary)",
|
||
borderRadius: 8,
|
||
padding: "8px 20px",
|
||
fontSize: 13,
|
||
textDecoration: "none",
|
||
}}
|
||
>
|
||
<Maximize2 size={14} strokeWidth={1.5} />
|
||
ПКК Росреестр
|
||
</a>
|
||
</div>
|
||
</aside>
|
||
);
|
||
}
|