# UHM Editor - developer guide thực dụng

Tài liệu này dành cho người sửa editor hiện tại, không phải mô tả kiến trúc lý tưởng.

## 1. Entry points quan trọng

- `src/app/editor/[id]/page.tsx`
  - orchestration chính của project editor
- `src/uhm/components/Map.tsx`
  - container cho map và các hook map
- `src/uhm/lib/editor/state/useEditorState.ts`
  - draft geometry + diff + undo
- `src/uhm/lib/editor/state/useEditorSessionState.ts`
  - session/UI/project/wiki/entity state
- `src/uhm/lib/editor/snapshot/editorSnapshot.ts`
  - normalize snapshot từ backend và build snapshot gửi ngược lại backend

Nếu chưa đọc 5 file này, chưa nên sửa behavior lớn của editor.

Docs nên đọc trước khi sửa editor:

- `src/uhm/doc/editor_operations.md`
- `src/uhm/doc/editor_data_roles.md`
- `src/uhm/doc/editor_snapshot_contract.md`
- `src/uhm/doc/editor_manual_test_checklist.md`
- `src/uhm/doc/editor_replay_actions.md`

## 2. Cấu trúc thư mục nên ưu tiên hiểu

- `src/uhm/components/editor/`
  - panel UI bên trái/phải
- `src/uhm/components/wiki/`
  - wiki editor và wiki viewer/sidebar
- `src/uhm/components/map/`
  - hooks tích hợp MapLibre
- `src/uhm/lib/map/engines/`
  - logic interaction theo mode
- `src/uhm/lib/editor/session/`
  - các nhóm session state
- `src/uhm/lib/editor/draft/`
  - draft diff và undo
- `src/uhm/lib/editor/snapshot/`
  - schema conversion / snapshot semantics

## 3. Cách editor thật sự vận hành

Editor có 3 tầng dữ liệu:

1. `baselineSnapshot`
   - snapshot gốc của session
2. `baselineFeatureCollection`
   - `FeatureCollection` rehydrate từ snapshot đó
   - seed/reset cho `useEditorState()`
3. `mainDraft`
   - working copy để user sửa trên map

Map không render trực tiếp `mainDraft` mọi lúc. Page tạo `mapRenderDraft` từ `mainDraft`/`replayDraft`/preview draft sau khi áp timeline/replay filter, rồi truyền xuống `Map` dưới prop `renderDraft`. `labelContextDraft` chỉ dùng để lookup label, không được dùng để quyết định geometry nào hiện trên map.

Khi commit:

- geometry đi từ `mainDraft`
- entity/wiki/link đi từ snapshot collections
- `buildEditorSnapshot()` quyết định operation nào là `reference`, `binding`, `update`, `delete`

Đừng tự build payload ở component nếu chưa hiểu file `editorSnapshot.ts`.

## 4. Khi thêm mode/tool mới

Checklist an toàn:

1. Thêm mode vào `sessionTypes.ts`.
2. Thêm button vào `ToolsPanel.tsx`.
3. Nếu mode cần preview source/layer mới, thêm vào `setupMapLayers()`.
4. Nối mode với engine trong `useMapInteraction.ts`.
5. Nếu tool tạo geometry mới, chọn default:
   - `type`
   - `geometry_preset`
   - `entity_ids`
   - `bound_with`
6. Kiểm tra interaction cleanup khi chuyển mode.

Nếu mode chưa được cleanup đúng, map rất dễ giữ preview cũ hoặc event listener cũ.

## 5. Khi thêm geotype mới

Checklist ngắn:

1. Cập nhật `geoTypeMap` nếu cần mapping backend code <-> key.
2. Cập nhật `geometryTypeOptions.ts`.
3. Tạo style file trong `styles/geotypes/`.
4. Register ở `geotypeLayers.ts`.
5. Kiểm tra point icon hoặc label pipeline nếu type mới là point/route/polygon label.

Nếu chỉ sửa `geometryTypeOptions.ts` mà quên style registry, UI sẽ cho chọn type nhưng map không render đúng.

## 6. Khi sửa snapshot semantics

File quan trọng nhất là `editorSnapshot.ts`.

Ở đó đang có hai hướng xử lý khác nhau:

- `normalizeEditorSnapshot(raw)`
  - đọc payload từ backend
  - rehydrate fields UI như `entity_ids`, `entity_name`, `bound_with`, `time_start`, `time_end`
- `buildEditorSnapshot(options)`
  - strip các field generate-only khỏi `editor_feature_collection`
  - build `geometry_entity[]` và `entity_wiki[]`
  - tính operation phù hợp

Nguyên tắc:

- feature trong editor có thể mang field denormalized để UI dễ dùng
- payload gửi backend thì không nên mang những field denormalized đó

## 7. Khi sửa wiki editor

Wiki project editor hiện là Quill, không phải Tiptap.

Các file nên đọc trước:

- `WikiSidebarPanel.tsx`
- `PublicWikiSidebar.tsx`

Các điểm dễ làm hỏng:

- sanitize link của Quill
- compatibility với doc dạng HTML/plain text
- slug links nội bộ
- sentinel `__missing__`

Nếu thay storage format, phải sửa cả editor lẫn viewer compatibility path.

## 8. Những key localStorage thật sự đang dùng

- `uhm.backgroundLayerVisibility.v1`
- `uhm:mapProjection`

Hiện không có local draft autosave toàn editor.
Đừng dựa vào doc cũ hoặc giả định rằng F5 sẽ hồi lại draft geometry/wiki/entity.

## 9. Restore commit hiện là FE-only

`CommitHistoryPanel -> Restore`:

- load snapshot từ commit cũ
- reset editor state ở frontend
- không đổi head commit trên backend

Nếu muốn restore server-side thật, cần dùng endpoint backend riêng và sửa cả UI wording.

## 10. Pending submission lock là rule thật

`openSectionEditor()` chủ động chặn project có `PENDING` submission.

Nghĩa là:

- không nên "lách" UI để cho sửa tiếp
- nếu đổi behavior này, phải thống nhất với backend contract

## 11. Performance và state hygiene

Một số nguyên tắc nên giữ:

- dùng `renderDraftRef`/refs trong map engines để tránh rebind handler vô ích
- giữ component panel càng dumb càng tốt, logic patch state đặt ở page/hooks
- khi cần undo cho entity/wiki/link, đi qua `editor.setSnapshot*()` để undo stack biết
- khi cần undo cho replay script, đi qua `editor.mutateActiveReplay()` hoặc replay collection helper hiện có
- hạn chế thêm `JSON.stringify` compare ở chỗ nóng nếu chưa đo hiệu năng
- khi thiết kế các chế độ preview, đảm bảo khôi phục camera view state & projection (Globe/Flat) về trạng thái gốc của editor bằng cách dùng `editorOriginalMapViewStateRef` và calling `restoreEditorOriginalMapState()`.

## 12. Chỗ dễ gây hiểu nhầm khi debug

### Geometry biến mất

Có thể do:

- timeline filter
- geometry visibility theo type
- bound_with filter

Không phải lúc nào cũng là bug render layer.

### Commit count lạ

`Commit (N)` là `pendingSaveCount`, không phải số mutation backend.

### Selection mất

Selection hiện bám theo `editor.draft`, không theo `mapRenderDraft`. Vì vậy geometry đang chọn có thể bị timeline filter ẩn khỏi map nhưng panel metadata vẫn đọc được draft gốc.

## 13. Nên test gì sau khi sửa

Ít nhất nên test thủ công:

1. mở project có commit cũ
2. tạo geometry mới bằng mode liên quan
3. sửa metadata geometry
4. bind entity và geometry
5. tạo/sửa wiki
6. link entity-wiki
7. commit
8. restore từ commit cũ
9. mở project có pending submission nếu đang debug flow đó
