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 как была.
111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
|
||
// Validates cadastral number formats:
|
||
// 3-part: 66:41:0204016 (quarter)
|
||
// 4-part: 66:41:0204016:10 (parcel)
|
||
const CAD_REGEX = /^\d+:\d+:\d+(?::\d+)?$/;
|
||
|
||
interface Props {
|
||
onSubmit: (cadNum: string) => void;
|
||
loading: boolean;
|
||
}
|
||
|
||
export function CadInput({ onSubmit, loading }: Props) {
|
||
const [value, setValue] = useState("");
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
const trimmed = value.trim();
|
||
if (!CAD_REGEX.test(trimmed)) {
|
||
setError(
|
||
"Неверный формат. Ожидается: 66:41:0204016:10 (участок) или 66:41:0204016 (квартал)",
|
||
);
|
||
return;
|
||
}
|
||
setError(null);
|
||
onSubmit(trimmed);
|
||
}
|
||
|
||
return (
|
||
<form onSubmit={handleSubmit}>
|
||
<label
|
||
htmlFor="cad-input"
|
||
style={{
|
||
display: "block",
|
||
fontWeight: 600,
|
||
marginBottom: 6,
|
||
fontSize: 14,
|
||
}}
|
||
>
|
||
Кадастровый номер
|
||
</label>
|
||
<div style={{ display: "flex", gap: 8 }}>
|
||
<input
|
||
id="cad-input"
|
||
type="text"
|
||
value={value}
|
||
onChange={(e) => {
|
||
setValue(e.target.value);
|
||
setError(null);
|
||
}}
|
||
placeholder="66:41:0204016:10"
|
||
style={{
|
||
flex: 1,
|
||
padding: "8px 12px",
|
||
fontSize: 14,
|
||
border: error
|
||
? "1px solid var(--danger, #B3261E)"
|
||
: "1px solid var(--border-strong, #D1D5DB)",
|
||
borderRadius: 8,
|
||
outline: "none",
|
||
fontFamily: "monospace",
|
||
}}
|
||
disabled={loading}
|
||
/>
|
||
<button
|
||
type="submit"
|
||
disabled={loading || !value.trim()}
|
||
style={{
|
||
padding: "8px 18px",
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
background: loading
|
||
? "var(--fg-tertiary, #73767E)"
|
||
: "var(--accent, #1D4ED8)",
|
||
color: "var(--fg-on-dark, #E2E8F0)",
|
||
border: "none",
|
||
borderRadius: 8,
|
||
cursor: loading ? "default" : "pointer",
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{loading ? "Анализ…" : "Анализировать"}
|
||
</button>
|
||
</div>
|
||
{error && (
|
||
<p
|
||
style={{
|
||
marginTop: 8,
|
||
fontSize: 12,
|
||
color: "var(--danger, #B3261E)",
|
||
}}
|
||
>
|
||
{error}
|
||
</p>
|
||
)}
|
||
<p
|
||
style={{
|
||
marginTop: 4,
|
||
fontSize: 11,
|
||
color: "var(--fg-tertiary, #73767E)",
|
||
}}
|
||
>
|
||
Примеры: <code>66:41:0204016:10</code> (участок) ·{" "}
|
||
<code>66:41:0204016</code> (квартал)
|
||
</p>
|
||
</form>
|
||
);
|
||
}
|