void;
- isEntitySubmitting: boolean;
onCreateEntityOnly: () => void;
onUpdateEntity?: (entityId: string, payload: { name: string; description: string | null }) => void;
- entityFormStatus: string | null;
- selectedGeometryEntityIds?: string[];
hasSelectedGeometry?: boolean;
onToggleBindEntityForSelectedGeometry?: (entityId: string, nextChecked: boolean) => void;
};
export default function ProjectEntityRefsPanel({
- entityRefs,
- entityForm,
- onEntityFormChange,
- isEntitySubmitting,
onCreateEntityOnly,
onUpdateEntity,
- entityFormStatus,
- selectedGeometryEntityIds,
hasSelectedGeometry,
onToggleBindEntityForSelectedGeometry,
}: Props) {
+ const {
+ snapshotEntities,
+ entityForm,
+ setEntityForm,
+ isEntitySubmitting,
+ entityFormStatus,
+ selectedGeometryEntityIds,
+ } = useEditorStore(
+ useShallow((state) => ({
+ snapshotEntities: state.snapshotEntities,
+ entityForm: state.entityForm,
+ setEntityForm: state.setEntityForm,
+ isEntitySubmitting: state.isEntitySubmitting,
+ entityFormStatus: state.entityFormStatus,
+ selectedGeometryEntityIds: state.selectedGeometryEntityIds,
+ }))
+ );
const canBindToggle =
Boolean(hasSelectedGeometry) &&
Array.isArray(selectedGeometryEntityIds) &&
@@ -43,6 +49,16 @@ export default function ProjectEntityRefsPanel({
() => new Set((selectedGeometryEntityIds || []).map(String)),
[selectedGeometryEntityIds]
);
+ const entityRefs = useMemo(() => {
+ const byId = new globalThis.Map
();
+ for (const ref of snapshotEntities || []) {
+ const id = String(ref?.id || "").trim();
+ if (!id || byId.has(id)) continue;
+ if (ref.operation === "delete") continue;
+ byId.set(id, ref);
+ }
+ return Array.from(byId.values());
+ }, [snapshotEntities]);
const sortedEntityRefs = useMemo(() => {
const rows = [...(entityRefs || [])];
rows.sort((a, b) => {
@@ -68,6 +84,9 @@ export default function ProjectEntityRefsPanel({
setEditName(typeof entity.name === "string" ? entity.name : "");
setEditDescription(entity.description == null ? "" : String(entity.description));
};
+ const handleEntityFormChange = (key: "name" | "description", value: string) => {
+ setEntityForm((prev) => ({ ...prev, [key]: value }));
+ };
return (
onEntityFormChange("name", event.target.value)}
+ onChange={(event) => handleEntityFormChange("name", event.target.value)}
placeholder="Tên entity mới"
disabled={isEntitySubmitting}
style={entityInputStyle}
/>
onEntityFormChange("description", event.target.value)}
+ onChange={(event) => handleEntityFormChange("description", event.target.value)}
placeholder="Description"
disabled={isEntitySubmitting}
style={entityInputStyle}
diff --git a/src/uhm/components/editor/SelectedGeometryPanel.tsx b/src/uhm/components/editor/SelectedGeometryPanel.tsx
index 250144b..212bef3 100644
--- a/src/uhm/components/editor/SelectedGeometryPanel.tsx
+++ b/src/uhm/components/editor/SelectedGeometryPanel.tsx
@@ -1,23 +1,20 @@
"use client";
import { type CSSProperties, useMemo, useState } from "react";
+import { useShallow } from "zustand/react/shallow";
import { Feature } from "@/uhm/lib/editor/state/useEditorState";
import {
+ GEOMETRY_TYPE_OPTIONS,
GeometryPreset,
GeometryTypeGroupId,
- GeometryTypeOption,
findGeometryTypeOption,
groupGeometryTypeOptions,
} from "@/uhm/lib/map/geo/geometryTypeOptions";
import { normalizeGeoTypeKey } from "@/uhm/lib/map/geo/geoTypeMap";
-import type { GeometryMetaFormState } from "@/uhm/lib/editor/session/sessionTypes";
+import { useEditorStore } from "@/uhm/store/editorStore";
type Props = {
selectedFeatures: Feature[];
- entityTypeOptions: GeometryTypeOption[];
- geometryMetaForm: GeometryMetaFormState;
- onGeometryMetaFormChange: (key: keyof GeometryMetaFormState, value: string) => void;
- isEntitySubmitting: boolean;
onApplyGeometryMetadata: () => Promise<{ ok: boolean; error?: string }>;
changeCount: number;
onReplayEdit?: (id: string | number) => void;
@@ -25,14 +22,21 @@ type Props = {
export default function SelectedGeometryPanel({
selectedFeatures,
- entityTypeOptions,
- geometryMetaForm,
- onGeometryMetaFormChange,
- isEntitySubmitting,
onApplyGeometryMetadata,
changeCount,
onReplayEdit,
}: Props) {
+ const {
+ geometryMetaForm,
+ setGeometryMetaForm,
+ isEntitySubmitting,
+ } = useEditorStore(
+ useShallow((state) => ({
+ geometryMetaForm: state.geometryMetaForm,
+ setGeometryMetaForm: state.setGeometryMetaForm,
+ isEntitySubmitting: state.isEntitySubmitting,
+ }))
+ );
const [collapsed, setCollapsed] = useState(false);
const [geoApplyFeedback, setGeoApplyFeedback] = useState<
| {
@@ -73,7 +77,7 @@ export default function SelectedGeometryPanel({
if (!selectedFeatures || selectedFeatures.length === 0) return null;
const representativeFeature = selectedFeatures[0];
- const groupedGeometryTypeOptions = groupGeometryTypeOptions(entityTypeOptions);
+ const groupedGeometryTypeOptions = groupGeometryTypeOptions(GEOMETRY_TYPE_OPTIONS);
const featureGeometryPreset = resolveFeatureGeometryPreset(representativeFeature);
const allowedGroupIds = getAllowedGroupIdsForPreset(featureGeometryPreset);
const groupedGeoTypeOptions = groupedGeometryTypeOptions.filter((group) =>
@@ -143,7 +147,12 @@ export default function SelectedGeometryPanel({