diff --git a/ordinis-admin-ui/src/components/lineage/DictionaryHubView.tsx b/ordinis-admin-ui/src/components/lineage/DictionaryHubView.tsx
index 1656ab7..3478965 100644
--- a/ordinis-admin-ui/src/components/lineage/DictionaryHubView.tsx
+++ b/ordinis-admin-ui/src/components/lineage/DictionaryHubView.tsx
@@ -1,63 +1,99 @@
/**
- * DictionaryHubView — Связи tab per redesign/screens/04-editor-links.png.
+ * Hub view (View B из design handoff) — focused dictionary в центре + соседи
+ * по обе стороны.
*
- *
2-column split:
- *
- *
→ ССЫЛАЕТСЯ НА (N): outgoing FK — каждая row показывает
- * target_dict (link) + record_count + required/optional chip + source_field
- * (mono right).
- *
← ССЫЛАЮТСЯ НА НАС (M): incoming FK — source_dict (link) +
- * activeRecordsInSourceDict + onClose policy chip ("критично" для BLOCK).
- * Footer warning если total dependents > N — указывает что удаление
- * записи в этом dict сложно.
- {/* Footer warning если в this dict много incoming refs — удаление
- * записи будет сложным (cascade chain или blockers). */}
- {totalIncomingDeps > HEAVY_THRESHOLD && (
-
- {t('hub.heavyDeps', {
- count: totalIncomingDeps,
- defaultValue: `Удаление записи в этом справочнике сложно — ${totalIncomingDeps} ссылок`,
- })}
-
- )}
- >
+
+ {incomingDicts.map((d) => (
+
+ ))}
+
)}
-
-
+
+
)
}
diff --git a/ordinis-admin-ui/src/routes/dictionaries.$name.tsx b/ordinis-admin-ui/src/routes/dictionaries.$name.tsx
index e4e6cae..f07627d 100644
--- a/ordinis-admin-ui/src/routes/dictionaries.$name.tsx
+++ b/ordinis-admin-ui/src/routes/dictionaries.$name.tsx
@@ -577,6 +577,21 @@ function DictionaryDetail() {
const totalRecords = recordsResult.data?.length ?? 0
const scope = detailQuery.data?.scope
+ // Geo capability detection — для скрытия AOI button на non-geo dicts.
+ // Эвристика по schema properties: property name matches geom/location/
+ // bbox/polygon/coordinates, OR format=geojson. Не идеально (false negatives
+ // если backend хранит geometry separately в записи без schema mention),
+ // но в practice все geo-dicts имеют geometry property в schemaJson.
+ const hasGeoRecords = useMemo(() => {
+ const props = detailQuery.data?.schemaJson?.properties
+ if (!props) return false
+ for (const [key, p] of Object.entries(props)) {
+ if (/geom|location|bbox|polygon|coordinates|geo/i.test(key)) return true
+ if (p.format === 'geojson' || p.format === 'wkt') return true
+ }
+ return false
+ }, [detailQuery.data?.schemaJson])
+
return (
{scope && (
@@ -601,7 +616,11 @@ function DictionaryDetail() {
actions={{
onTimeTravel: () => setTimeTravelOpen((v) => !v),
timeTravelActive: Boolean(timeTravelAt),
- onAoi: () => setAoiOpen(true),
+ // AOI button показываем только если у этого dict есть geo data —
+ // хотя бы одна record имеет geometryWkt. На non-geo dicts кнопка
+ // лишняя. Per user feedback: "aoi там где нет полей гео лишний".
+ // Edge case: если active aoi уже set → показываем чтобы юзер мог clear.
+ onAoi: hasGeoRecords || aoi ? () => setAoiOpen(true) : undefined,
aoiActive: Boolean(aoi),
// Открываем ExportModal вместо immediate triggera — пользователь
// выбирает формат/scope/columns/encoding/delimiter.