"use client"; import { type CSSProperties, useMemo, useState } from "react"; import { Entity } from "@/uhm/api/entities"; import { Feature } from "@/uhm/lib/useEditorState"; import { EntityGeometryPreset, EntityTypeGroupId, EntityTypeOption, findEntityTypeOption, groupEntityTypeOptions, } from "@/uhm/lib/entityTypeOptions"; import type { GeometryMetaFormState } from "@/uhm/lib/editor/session/sessionTypes"; type Props = { selectedFeature: Feature | null; selectedFeatureEntitySummary: string; selectedFeatureBindingSummary: string; entities: Entity[]; selectedGeometryEntityIds: string[]; onEntityIdsChange: (values: string[]) => void; entityTypeOptions: EntityTypeOption[]; geometryMetaForm: GeometryMetaFormState; onGeometryMetaFormChange: (key: keyof GeometryMetaFormState, value: string) => void; isEntitySubmitting: boolean; onApplyGeometryMetadata: () => Promise<{ ok: boolean; error?: string }>; changeCount: number; }; export default function SelectedGeometryPanel({ selectedFeature, selectedFeatureEntitySummary, selectedFeatureBindingSummary, entities, selectedGeometryEntityIds, onEntityIdsChange, entityTypeOptions, geometryMetaForm, onGeometryMetaFormChange, isEntitySubmitting, onApplyGeometryMetadata, changeCount, }: Props) { const [collapsed, setCollapsed] = useState(false); const [geoApplyFeedback, setGeoApplyFeedback] = useState< | { kind: "ok" | "error"; text: string; signature: string; } | null >(null); const geoMetaSignature = useMemo(() => { return [ geometryMetaForm.type_key, geometryMetaForm.time_start, geometryMetaForm.time_end, geometryMetaForm.binding, ].join("|"); }, [ geometryMetaForm.binding, geometryMetaForm.time_end, geometryMetaForm.time_start, geometryMetaForm.type_key, ]); const handleApplyGeoMeta = async () => { setGeoApplyFeedback(null); const result = await onApplyGeometryMetadata(); if (result.ok) { setGeoApplyFeedback({ kind: "ok", text: "đã apply thành công", signature: geoMetaSignature }); } else if (result.error) { setGeoApplyFeedback({ kind: "error", text: result.error, signature: geoMetaSignature }); } }; const visibleGeoApplyFeedback = geoApplyFeedback && geoApplyFeedback.signature === geoMetaSignature ? geoApplyFeedback : null; if (!selectedFeature) return null; const groupedEntityTypeOptions = groupEntityTypeOptions(entityTypeOptions); const featureGeometryPreset = resolveFeatureGeometryPreset(selectedFeature); const allowedGroupIds = getAllowedGroupIdsForPreset(featureGeometryPreset); const groupedGeoTypeOptions = groupedEntityTypeOptions.filter((group) => allowedGroupIds.includes(group.id) ); const selectedTypeOption = findEntityTypeOption(geometryMetaForm.type_key); const hasCurrentVisibleTypeOption = groupedGeoTypeOptions.some((group) => group.options.some((option) => option.value === geometryMetaForm.type_key) ); return (
Entity & Geometry
{collapsed ? null : (
ID: {String(selectedFeature.properties.id)}
Entities hiện tại: {selectedFeatureEntitySummary}
Binding hiện tại: {selectedFeatureBindingSummary}
Geometry preset: {formatGeometryPresetLabel(featureGeometryPreset)}
Entities đã chọn:
{selectedGeometryEntityIds.length ? (
{selectedGeometryEntityIds.map((entityId) => { const entity = entities.find((item) => item.id === entityId) || null; const label = entity?.name ? `${entity.name} (${entityId})` : entityId; return (
{label}
); })}
) : (
Chưa có entity nào được gắn.
)}
Thuộc tính GEO
Các giá trị này thuộc về GEO đang chọn, không phụ thuộc entity.
Loại GEO
{selectedTypeOption ? (
Đang chọn: {selectedTypeOption.label} ({selectedTypeOption.groupLabel})
) : geometryMetaForm.type_key ? (
Đang chọn: {geometryMetaForm.type_key}
) : null} onGeometryMetaFormChange("time_start", event.target.value)} placeholder="time_start" disabled={isEntitySubmitting} style={entityInputStyle} /> onGeometryMetaFormChange("time_end", event.target.value)} placeholder="time_end" disabled={isEntitySubmitting} style={entityInputStyle} /> {/* onGeometryMetaFormChange("binding", event.target.value)}*/} {/* placeholder="binding (geometry ids, comma separated)"*/} {/* disabled={isEntitySubmitting}*/} {/* style={entityInputStyle}*/} {/*/>*/} {visibleGeoApplyFeedback ? (
{visibleGeoApplyFeedback.text}
) : null}
{changeCount > 0 ? (
Thay đổi sẽ vào lịch sử khi Commit.
) : null}
)}
); } const entityInputStyle: CSSProperties = { width: "100%", borderRadius: "6px", border: "1px solid #334155", background: "#111827", color: "#f8fafc", padding: "6px 8px", fontSize: "13px", }; const removeButtonStyle: CSSProperties = { border: "none", borderRadius: "6px", padding: "4px 8px", cursor: "pointer", background: "#7f1d1d", color: "#ffffff", fontSize: "12px", }; const primaryGeometryButtonStyle: CSSProperties = { border: "none", borderRadius: "6px", padding: "7px 8px", cursor: "pointer", background: "#0f766e", color: "#ffffff", fontWeight: 600, }; function PlusIcon() { return ( ); } function MinusIcon() { return ( ); } function resolveFeatureGeometryPreset(feature: Feature): EntityGeometryPreset { const explicitPreset = normalizeGeometryPreset(feature.properties.geometry_preset); if (explicitPreset) return explicitPreset; const semanticType = normalizeTypeId(feature.properties.type) || normalizeTypeId(feature.properties.entity_type_id); if (semanticType) { const option = findEntityTypeOption(semanticType); if (option) return option.geometryPreset; } return mapGeometryTypeToPreset(feature.geometry.type); } function normalizeGeometryPreset(value: unknown): EntityGeometryPreset | null { if (typeof value !== "string") return null; const normalized = value.trim().toLowerCase(); if ( normalized === "point" || normalized === "line" || normalized === "polygon" || normalized === "circle-area" ) { return normalized; } return null; } function normalizeTypeId(value: unknown): string | null { if (typeof value !== "string") return null; const normalized = value.trim().toLowerCase(); return normalized.length ? normalized : null; } function mapGeometryTypeToPreset( geometryType: Feature["geometry"]["type"] ): EntityGeometryPreset { if (geometryType === "Point" || geometryType === "MultiPoint") { return "point"; } if (geometryType === "LineString" || geometryType === "MultiLineString") { return "line"; } return "polygon"; } function getAllowedGroupIdsForPreset( geometryPreset: EntityGeometryPreset ): EntityTypeGroupId[] { if (geometryPreset === "point") { return ["point"]; } if (geometryPreset === "line") { return ["line"]; } if (geometryPreset === "circle-area") { return ["circle"]; } return ["polygon"]; } function formatGeometryPresetLabel(preset: EntityGeometryPreset | null): string { if (preset === "point") return "point - Điểm"; if (preset === "line") return "line - Tuyến"; if (preset === "circle-area") return "circle - Tròn"; if (preset === "polygon") return "polygon - Đa giác"; return "unknown"; }