# Commit Snapshot (`commits.snapshot_json`) - Chuẩn Hiện Tại (FrontEndAdmin)

Tài liệu này mô tả **commit snapshot** được `FrontEndAdmin` tạo ra khi bấm **Commit** trong `/editor`, và được lưu vào `BackEndGo.commits.snapshot_json` (JSONB).

Nguồn tham chiếu trong code:

- Type snapshot: `FrontEndAdmin/src/uhm/types/sections.ts` (`EditorSnapshot`)
- Build snapshot: `FrontEndAdmin/src/uhm/lib/editor/snapshot/editorSnapshot.ts` (`buildEditorSnapshot`)

## 1) Tổng Quan Schema

Snapshot hiện tại:

- Không có `schema_version`.
- Không lưu `section` (project/section được xác định bằng context record `commits.project_id`).
- Không dùng `ref:{id}` nữa: **`id` là canonical**, `source:"ref"` nghĩa là tham chiếu theo `id`.

```ts
export type CommitSnapshot = {
  editor_feature_collection?: FeatureCollection;

  entities?: EntitySnapshot[];
  geometries?: GeometrySnapshot[];
  wikis?: WikiSnapshot[];

  geometry_entity?: GeometryEntitySnapshot[]; // geometry ↔ entity (many-to-many)
  entity_wiki?: EntityWikiLinkSnapshot[];     // entity ↔ wiki
};
```

## 2) Quy Ước `source` và `operation`

### 2.1 `source` (bắt buộc)

`source` bắt buộc là một trong:

- `inline`: dữ liệu được embed trong snapshot_json.
- `ref`: dữ liệu là tham chiếu (theo `id`), cần fetch bên ngoài nếu muốn đầy đủ.

FE hiện tại luôn ghi `source` cho `entities[]`, `geometries[]`, `wikis[]`.

### 2.2 `operation` (tùy chọn)

`operation` là tùy chọn. Khi **không có** `operation` thì hiểu là:

- row được đưa vào snapshot như **project context** (hoặc không đổi trong commit này),
- commit này không sửa record, và cũng không cần đánh dấu là `"reference"` để làm “đầu mối nối”.

`operation` có thể xuất hiện ở:

- `entities[].operation`: `create` | `update` | `delete` | `reference`
- `geometries[].operation`: `create` | `update` | `delete` | `reference`
- `wikis[].operation`: `create` | `update` | `delete` | `reference`

`geometry_entity[]` không có `operation` (join table state).

`entity_wiki[]` dùng `operation:"reference"|"delete"` để biểu diễn link/unlink **trong snapshot** (không phải delete trong DB).

## 3) Ý Nghĩa Từng Phần

### 3.1 `editor_feature_collection`

GeoJSON `FeatureCollection` là nguồn để:

- render map trong editor,
- làm cơ sở build `geometries[]` và join table `geometry_entity[]`.

Lưu ý quan trọng:

- Snapshot persist **không lưu** các field entity denormalize trên `feature.properties`:
  `entity_id/entity_ids/entity_name/entity_names/entity_type_id`.
- Quan hệ geometry ↔ entity nằm ở `geometry_entity[]`.
- Khi load commit vào editor, FE có thể rehydrate `entity_ids/entity_id` lên features từ `geometry_entity[]` để UI hoạt động, nhưng đó không phải dữ liệu persist.

### 3.2 `entities[]`

`entities[]` là danh sách entity liên quan tới project/commit. Mỗi row có `source` và có thể có/không có `operation`.

FE build `entities[]` từ:

1. Pending entities tạo mới trong editor:
`source:"inline"`, `operation:"create"`.

2. Entity được user “pin” vào project từ search (không gắn geometry, không link wiki):
`source:"ref"`, không có `operation`.

3. Entities xuất hiện trong `geometry_entity[]`:
`source:"ref"`, `operation:"reference"`.

4. Entities xuất hiện trong `entity_wiki[]`:
`source:"ref"`, `operation:"reference"`.

### 3.3 `geometries[]`

Mỗi `Feature` trong `editor_feature_collection.features[]` sinh 1 `GeometrySnapshot` row:

- `id = String(feature.properties.id)`
- `source:"inline"`
- `draw_geometry = feature.geometry`
- kèm `type`, `binding`, `time_start/time_end`, `bbox` (nếu tính được)

`operation` cho geometry:

- `create`: feature mới
- `update`: feature thay đổi
- (không có `operation`): feature không đổi (không delta trong commit)

Nếu feature bị xoá khỏi draft, FE thêm 1 delete row:

```json
{ "id": "g_1", "source": "ref", "operation": "delete" }
```

Lưu ý: geometry `operation:"delete"` **không xuất hiện trên map**, vì map render theo `editor_feature_collection.features[]`.

### 3.4 `geometry_entity[]` (join table Geometry ↔ Entity)

Join table many-to-many giữa geometry và entity. Mỗi cặp geometry↔entity là một row:

```ts
{ geometry_id: string; entity_id: string }
```

### 3.5 `wikis[]`

Danh sách wiki của project tại thời điểm commit:

- Wiki tạo mới: `source:"inline"`, `operation:"create"`, `doc` là tiptap JSON.
- Wiki sửa: `source:"inline"`, `operation:"update"`, `doc` là tiptap JSON.
- Wiki không đổi: thường không có `operation`.
- Wiki add từ search (wiki đã có trong DB): `source:"ref"`, `operation:"reference"`, `doc` có thể là `null`.

### 3.6 `entity_wiki[]` (join table Entity ↔ Wiki)

```ts
export type EntityWikiLinkSnapshot = {
  entity_id: string;
  wiki_id: string;
  operation?: "reference" | "delete";
};
```

Toggle link trong UI:

- Tick checkbox: `{ operation: "reference" }`
- Untick checkbox: `{ operation: "delete" }`

## 4) Ví Dụ JSON (rút gọn)

```json
{
  "editor_feature_collection": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "properties": {
          "id": "g_1",
          "type": "city",
          "time_start": 1200,
          "time_end": 1300,
          "binding": []
        },
        "geometry": { "type": "Point", "coordinates": [105.8, 21.0] }
      }
    ]
  },
  "entities": [
    { "id": "e_2", "source": "ref", "name": "Pinned Entity" },
    { "id": "e_1", "source": "ref", "operation": "reference", "name": "Ha Noi", "type_id": "city", "status": 1 }
  ],
  "geometries": [
    {
      "id": "g_1",
      "source": "inline",
      "operation": "update",
      "type": "city",
      "draw_geometry": { "type": "Point", "coordinates": [105.8, 21.0] },
      "binding": [],
      "time_start": 1200,
      "time_end": 1300,
      "bbox": { "min_lng": 105.8, "min_lat": 21.0, "max_lng": 105.8, "max_lat": 21.0 }
    }
  ],
  "geometry_entity": [
    { "geometry_id": "g_1", "entity_id": "e_1" }
  ],
  "wikis": [
    {
      "id": "w_inline_1",
      "source": "inline",
      "operation": "create",
      "title": "Overview",
      "doc": { "type": "doc", "content": [{ "type": "paragraph" }] }
    },
    {
      "id": "019d...wiki_from_db",
      "source": "ref",
      "operation": "reference",
      "title": "Existing Wiki (DB)",
      "doc": null
    }
  ],
  "entity_wiki": [
    { "entity_id": "e_1", "wiki_id": "w_inline_1", "operation": "reference" }
  ]
}
```
