203 lines
6.6 KiB
Markdown
203 lines
6.6 KiB
Markdown
# 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
|
|
|
|
## 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 đó
|