gendesign/frontend/src/components/site-finder/CadInput.tsx
lekss361 a4e8fff5ac feat(site-finder): POST /parcels/{cad}/analyze endpoint + UI
Backend:
- analyze endpoint: cad → geom lookup → POI within 1km → district
  context → competitors within 3km. Tram_stop carries negative weight.

Frontend /site-finder:
- CadInput regex-validated (default 66:41:0204016:10)
- SiteMap (Leaflet via next/dynamic) with parcel polygon + legend
- ScoreCard color-coded (>5 green, 2-5 yellow, <2 red) with tram-warning
- CompetitorTable top-20 with same-district highlight
- useSiteAnalysis hook + TS types
2026-05-11 18:11:58 +03:00

93 lines
2.6 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";
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("66:41:0204016:10");
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 #ef4444" : "1px solid #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 ? "#9ca3af" : "#1d4ed8",
color: "#fff",
border: "none",
borderRadius: 8,
cursor: loading ? "default" : "pointer",
whiteSpace: "nowrap",
}}
>
{loading ? "Анализ…" : "Анализировать"}
</button>
</div>
{error && (
<p style={{ marginTop: 6, fontSize: 12, color: "#ef4444" }}>{error}</p>
)}
<p style={{ marginTop: 4, fontSize: 11, color: "#9ca3af" }}>
Примеры: <code>66:41:0204016:10</code> (участок) ·{" "}
<code>66:41:0204016</code> (квартал)
</p>
</form>
);
}