# 画面設計書 6-エントリ編集画面

## 概要

本ドキュメントは、Decap CMS のエントリ編集画面（Editor）の画面設計書である。既存エントリの編集、保存、削除、公開等の操作を行う画面の仕様を定義する。コントロールペインとプレビューペインの分割表示、ツールバーによる操作を提供する。

### 本画面の処理概要

**業務上の目的・背景**：コンテンツ管理者がMarkdownやYAML等で構成されたコンテンツエントリを視覚的に編集するための画面である。WYSIWYG的なプレビュー表示によりコンテンツの最終的な見た目を確認しながら編集でき、Editorial Workflow有効時にはドラフト・レビュー・公開のステータス管理も行える。また、ローカルバックアップ機能により編集中のデータ消失を防止する。

**画面へのアクセス方法**：コレクション一覧画面のエントリをクリック、検索結果画面のエントリをクリック、ワークフロー画面のエントリカードをクリックにより遷移する。URLは`/collections/:name/entries/*`。

**主要な操作・処理内容**：
1. コントロールペインでフィールド値を編集する（テキスト、マークダウン、画像、日付等のウィジェット）
2. プレビューペインでリアルタイムプレビューを確認する
3. ツールバーの保存ボタンでエントリを保存する
4. ツールバーの公開ボタンでエントリを公開する（Editorial Workflow時）
5. ステータス変更（Draft -> Review -> Ready）を行う（Editorial Workflow時）
6. エントリを削除する
7. エントリを複製して新規作成する
8. 非公開化（Unpublish）を行う
9. デプロイプレビューを確認する
10. i18n対応のデュアルペイン表示で多言語編集を行う

**画面遷移**：
- 遷移元：コレクション一覧画面（エントリ選択）、コレクション検索結果画面（エントリ選択）、ワークフロー画面（エントリカードクリック）、エントリ新規作成画面（保存成功後）
- 遷移先：コレクション一覧画面（戻るリンク、削除後）、エントリ新規作成画面（保存して新規作成、複製）、メディアライブラリ画面（ファイル/画像ウィジェット）

**権限による表示制御**：コレクション設定の`create`フラグにより複製ボタンの表示が制御される。`delete`フラグ（デフォルトtrue）により削除ボタンの表示が制御される。`publish`フラグによりPublish操作の可否が制御される。Open Authoring使用時はPublishボタンが非表示になる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | エントリ作成・編集 | 主機能 | 既存エントリからのドラフト作成とフィールド値の変更管理 |
| 7 | エントリ読み込み | 主機能 | 既存エントリのバックエンドからの読み込み |
| 9 | エントリ保存（永続化） | 主機能 | バリデーション後のエントリ保存処理 |
| 10 | エントリ削除 | 補助機能 | エントリの削除処理。確認ダイアログ表示後に実行 |
| 11 | エントリ複製 | 補助機能 | 既存エントリの複製処理 |
| 12 | ローカルバックアップ | 補助機能 | 編集中ドラフトのlocalForageへの自動保存（2秒デバウンス） |
| 16 | バリデーション | 補助機能 | フィールド入力値の検証処理 |
| 17 | ワークフロー管理 | 補助機能 | Editorial Workflow有効時のステータス変更 |
| 19 | エントリ公開 | 補助機能 | Editorial Workflow有効時の公開処理 |
| 20 | エントリ非公開 | 補助機能 | 公開済みエントリの非公開化処理 |
| 21 | 未公開エントリ削除 | 補助機能 | 未公開エントリの削除処理 |
| 47 | エディタインターフェース | 主機能 | コントロールペインとプレビューペインのSplitPaneによる分割表示 |
| 48 | エディタツールバー | 補助機能 | 保存・公開・ステータス変更等のツールバー |
| 49 | プレビューテンプレート | 補助機能 | カスタムプレビューテンプレートの表示 |
| 51 | デプロイプレビュー | 補助機能 | エントリのデプロイプレビューURL取得と表示 |
| 69 | i18n（多言語）サポート | 補助機能 | 多言語エントリ編集のデュアルペイン表示とロケール切替 |
| 77 | イベントフック | API連携 | preSave・postSaveイベントフックの実行 |

## 画面種別

編集

## URL/ルーティング

| パス | 説明 |
|------|------|
| `/collections/:name/entries/*` | エントリ編集画面。`*`はエントリのslug（ネストされたパス対応） |

## 入出力項目

### 入力項目

コレクションのfields定義に基づいて動的に生成される。代表的なウィジェットタイプ：

| ウィジェットタイプ | 入力形式 | 説明 |
|------------------|---------|------|
| string | テキスト | 短いテキスト入力 |
| text | テキストエリア | 長いテキスト入力 |
| markdown | リッチエディタ | Markdownエディタ |
| image | ファイル選択 | 画像ファイル選択（メディアライブラリ経由） |
| file | ファイル選択 | ファイル選択（メディアライブラリ経由） |
| datetime | 日時選択 | 日時選択入力 |
| select | ドロップダウン | 選択肢入力 |
| boolean | チェックボックス | true/false入力 |
| list | リスト | 複数アイテムのリスト入力 |
| object | ネストフォーム | ネストされたオブジェクト入力 |

### URLパラメータ

| パラメータ名 | 型 | 説明 |
|-------------|-----|------|
| name | string | コレクション名 |
| * (slug) | string | エントリのslug（ファイルパスベース） |

### クエリパラメータ

| パラメータ名 | 型 | 説明 |
|-------------|-----|------|
| ref | string | 遷移元識別子。`workflow`の場合、戻るリンクがワークフロー画面に設定される |

## 表示項目

| 項目名 | 表示条件 | 説明 |
|--------|---------|------|
| ツールバー | 常時 | 戻るリンク、保存ボタン、ステータス表示、公開ボタン、削除ボタン等 |
| 戻るリンク | 常時 | コレクション名と変更状態（Unsaved Changes / Changes Saved）を表示 |
| 保存ボタン | hasWorkflow時 | ドラフトの保存ボタン（変更がない場合はdisabled） |
| ステータスドロップダウン | hasWorkflow && currentStatus | Draft / In Review / Ready の切り替え |
| 公開ボタン/ドロップダウン | 条件による | Publish Now / Publish and Create New / Publish and Duplicate |
| 削除ボタン | showDelete=true | エントリの削除 |
| Published表示 | 公開済みエントリ | Published状態の表示とUnpublish/Duplicateドロップダウン |
| デプロイプレビューリンク | deployPreview.status === 'SUCCESS' | デプロイプレビューへの外部リンク |
| コントロールペイン | 常時 | フィールド入力フォーム |
| プレビューペイン | previewEnabled時 | プレビュー表示（トグルで表示/非表示） |
| i18nトグル | collectionI18nEnabled時 | デュアルペイン表示の切り替え |
| スクロール同期トグル | プレビューまたはi18n表示時 | スクロール同期の切り替え |
| SettingsDropdown | 常時 | displayUrlとログアウトボタン |

## イベント仕様

### 1-保存ボタン押下（Save）

1. EditorInterface.handleOnPersistが呼ばれる（EditorInterface.js 行177-182）
2. デフォルトロケールに切り替え、バリデーション実行
3. Editor.handlePersistEntryが呼ばれる（Editor.js 行224-247）
4. `persistEntry(collection)`アクションをdispatch
5. ローカルバックアップを削除
6. createNew=trueの場合、新規作成画面に遷移
7. Editorial Workflow時で未公開の場合、エントリを再読み込み

### 2-公開ボタン押下（Publish）

1. EditorInterface.handleOnPublishが呼ばれる（EditorInterface.js 行184-189）
2. Editor.handlePublishEntryが呼ばれる（Editor.js 行249-279）
3. ステータスがPENDING_PUBLISHでない場合、アラートで拒否
4. 未保存変更がある場合、アラートで拒否
5. 確認ダイアログ表示後、`publishUnpublishedEntry`アクションをdispatch
6. ローカルバックアップを削除

### 3-削除ボタン押下（Delete）

1. Editor.handleDeleteEntryが呼ばれる（Editor.js 行297-315）
2. 未保存変更がある場合、確認ダイアログ表示
3. 確認後、`deleteEntry(collection, slug)`アクションをdispatch
4. ローカルバックアップを削除
5. コレクション一覧画面に遷移

### 4-ステータス変更

1. EditorToolbar.renderWorkflowStatusControlsのDropdownItemをクリック（EditorToolbar.js 行364-407）
2. Editor.handleChangeStatusが呼ばれる（Editor.js 行207-216）
3. 未保存変更がある場合、アラートで拒否
4. `updateUnpublishedEntryStatus`アクションをdispatch

### 5-複製

1. EditorToolbar.onDuplicateをクリック
2. Editor.handleDuplicateEntryが呼ばれる（Editor.js 行290-295）
3. 新規作成画面に遷移し、`createDraftDuplicateFromEntry`アクションをdispatch

### 6-非公開化（Unpublish）

1. EditorToolbar.unPublishをクリック
2. Editor.handleUnpublishEntryが呼ばれる（Editor.js 行281-288）
3. 確認ダイアログ後、`unpublishPublishedEntry`アクションをdispatch
4. コレクション一覧画面に遷移

### 7-フィールド変更

1. コントロールペインでフィールド値を変更
2. Editor.handleChangeDraftFieldが呼ばれる（Editor.js 行202-205）
3. `changeDraftField({ field, value, metadata, entries, i18n })`アクションをdispatch
4. 2秒デバウンスでローカルバックアップが自動保存される（Editor.js 行198-200）

### 8-ローカルバックアップ復元

1. componentDidUpdateでlocalBackupの存在を検出（Editor.js 行170-177）
2. `window.confirm`でバックアップ復元の確認ダイアログ表示
3. 承認時: `loadLocalBackup`アクションをdispatch
4. 拒否時: ローカルバックアップを削除

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存（Save） | Gitリポジトリ / Redux State | UPDATE | エントリファイルをGitリポジトリにコミット |
| 公開（Publish） | Gitリポジトリ / Redux State | UPDATE | ブランチをメインブランチにマージ |
| 削除（Delete） | Gitリポジトリ / Redux State | DELETE | エントリファイルをGitリポジトリから削除 |
| ステータス変更 | Redux State (editorialWorkflow) | UPDATE | 未公開エントリのステータスを更新 |
| 非公開化 | Gitリポジトリ / Redux State | DELETE/INSERT | メインブランチから削除、未公開ブランチに移動 |
| フィールド変更 | Redux State (entryDraft) | UPDATE | ドラフトのフィールド値を更新 |
| ローカルバックアップ | localForage | INSERT/UPDATE | ブラウザのlocalForageにドラフトを保存 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| editor.editor.onLeavePage | 警告 | ページ離脱確認メッセージ | 未保存変更がある場合のbeforeunloadイベント |
| editor.editor.onUpdatingWithUnsavedChanges | 警告 | 未保存変更ありでのステータス変更拒否 | ステータス変更時にhasChanged=true |
| editor.editor.onPublishingNotReady | 警告 | 公開準備未完了 | ステータスがPENDING_PUBLISHでない場合の公開試行 |
| editor.editor.onPublishingWithUnsavedChanges | 警告 | 未保存変更ありでの公開拒否 | 公開時にhasChanged=true |
| editor.editor.onPublishing | 確認 | 公開確認ダイアログ | 公開ボタン押下時 |
| editor.editor.onUnpublishing | 確認 | 非公開化確認ダイアログ | 非公開化ボタン押下時 |
| editor.editor.onDeleteWithUnsavedChanges | 確認 | 未保存変更ありでの削除確認 | 削除時にhasChanged=true |
| editor.editor.onDeletePublishedEntry | 確認 | 公開済みエントリ削除確認 | 公開済みエントリの削除時 |
| editor.editor.confirmLoadBackup | 確認 | ローカルバックアップ復元確認 | ローカルバックアップが存在する場合 |
| editor.editor.loadingEntry | 情報 | エントリ読み込み中 | エントリ読み込み中のローダー表示 |
| editor.editorToolbar.save | 情報 | 保存ボタンラベル「Save」 | 保存可能時 |
| editor.editorToolbar.saving | 情報 | 保存中ラベル「Saving...」 | 保存処理中 |
| editor.editorToolbar.publish | 情報 | 公開ボタンラベル「Publish」 | 公開可能時 |
| editor.editorToolbar.publishing | 情報 | 公開中ラベル「Publishing...」 | 公開処理中 |
| editor.editorToolbar.published | 情報 | 公開済みラベル「Published」 | 公開済みエントリ |
| editor.editorToolbar.unsavedChanges | 警告 | 「Unsaved Changes」バッジ | hasChanged=true |
| editor.editorToolbar.changesSaved | 成功 | 「Changes Saved」バッジ | hasChanged=false |

## 例外処理

| 例外条件 | 処理内容 | 画面表示 |
|---------|---------|---------|
| エントリ読み込みエラー | entry.get('error')をチェック | エラーメッセージを表示（Editor.js 行366-371） |
| エントリ読み込み中 | entry.get('isFetching')をチェック | Loaderコンポーネントを表示 |
| 未保存変更ありでページ離脱 | beforeunloadイベントとhistory.block | ブラウザの離脱確認ダイアログ |
| バリデーションエラー | changeDraftFieldValidationで管理 | フィールドごとにエラーメッセージ表示 |

## 備考

- ローカルバックアップは2秒デバウンスで自動保存される（Editor.js 行198-200）。
- SplitPaneの位置はlocalStorage（`cms.split-pane-position`）に永続化される。
- プレビュー表示のON/OFFはlocalStorage（`cms.preview-visible`）に永続化される。
- スクロール同期のON/OFFはlocalStorage（`cms.scroll-sync-enabled`）に永続化される。
- i18n表示のON/OFFはlocalStorage（`cms.i18n-visible`）に永続化される。
- EditorToolbarの戻るリンクは、ワークフロー画面からの遷移時（`?ref=workflow`）はワークフロー画面に設定される（Editor.js 行439-441）。
- ネストコレクションの場合、戻るリンクは親フォルダのフィルター画面に設定される（Editor.js 行443-448）。

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

エントリドラフトのRedux state構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Editor.js | `packages/decap-cms-core/src/components/Editor/Editor.js` | mapStateToProps（行417-473）でのstate取得。entryDraft, entry, fields等のデータ構造 |

#### Step 2: エントリーポイントを理解する

Editor.jsのcomponentDidMountとrenderメソッドを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Editor.js | `packages/decap-cms-core/src/components/Editor/Editor.js` | componentDidMount（行84-167）のエントリ読み込み・バックアップ確認・離脱ブロック設定 |

**主要処理フロー**:
1. **行100**: retrieveLocalBackupでバックアップ確認
2. **行102-106**: newEntry判定でcreateEmptyDraftまたはloadEntry
3. **行108-118**: beforeunloadイベントの離脱ブロック設定
4. **行120-162**: history.blockとhistory.listenでナビゲーション制御

#### Step 3: EditorInterfaceの描画を理解する

コントロールペインとプレビューペインの分割表示を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EditorInterface.js | `packages/decap-cms-core/src/components/Editor/EditorInterface.js` | renderメソッド（行213-409）のSplitPane構造。プレビュー/i18n/スクロール同期のトグル |

#### Step 4: EditorToolbarの操作を理解する

ツールバーのボタン群と操作フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | EditorToolbar.js | `packages/decap-cms-core/src/components/Editor/EditorToolbar.js` | renderWorkflowControls/renderSimpleControlsの分岐。ボタンの条件付き表示 |

### プログラム呼び出し階層図

```
App.js (Route: /collections/:name/entries/*)
    |
    +-- Editor (connect + withWorkflow + translate)
            |
            +-- componentDidMount
            |       +-- retrieveLocalBackup
            |       +-- loadEntry / createEmptyDraft
            |       +-- window.addEventListener('beforeunload')
            |       +-- history.block / history.listen
            |
            +-- EditorInterface
                    |
                    +-- EditorToolbar
                    |       +-- Save -> handlePersistEntry -> persistEntry
                    |       +-- Publish -> handlePublishEntry -> publishUnpublishedEntry
                    |       +-- Delete -> handleDeleteEntry -> deleteEntry
                    |       +-- Status -> handleChangeStatus -> updateUnpublishedEntryStatus
                    |       +-- Unpublish -> handleUnpublishEntry -> unpublishPublishedEntry
                    |       +-- Duplicate -> handleDuplicateEntry -> createDraftDuplicateFromEntry
                    |
                    +-- SplitPane
                            +-- EditorControlPane (入力フォーム)
                            +-- EditorPreviewPane (プレビュー)
```

### データフロー図

```
[入力]                    [処理]                              [出力]

フィールド値変更    ──▶  changeDraftField                   ──▶  Redux State (entryDraft)
                         |                                       |
                         +-- 2秒デバウンス                       +-- localForage (バックアップ)
                              persistLocalBackup

保存ボタン押下      ──▶  persistEntry                       ──▶  Gitリポジトリ
                         |                                       |
                         +-- バリデーション                      +-- Redux State (entries)
                         +-- serializeValues                     +-- deleteLocalBackup
                         +-- backend.persistEntry

公開ボタン押下      ──▶  publishUnpublishedEntry             ──▶  Gitリポジトリ (メインブランチ)
                         |
                         +-- backend.publishUnpublishedEntry
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Editor.js | `packages/decap-cms-core/src/components/Editor/Editor.js` | ソース | エディタのメインコンテナコンポーネント |
| EditorInterface.js | `packages/decap-cms-core/src/components/Editor/EditorInterface.js` | ソース | ツールバー+SplitPaneの画面レイアウト |
| EditorToolbar.js | `packages/decap-cms-core/src/components/Editor/EditorToolbar.js` | ソース | 保存・公開・削除等のツールバー |
| EditorControlPane.js | `packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControlPane.js` | ソース | フィールド入力フォーム |
| EditorPreviewPane.js | `packages/decap-cms-core/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js` | ソース | プレビュー表示 |
| withWorkflow.js | `packages/decap-cms-core/src/components/Editor/withWorkflow.js` | ソース | Editorial Workflowの高階コンポーネント |
| entries.ts | `packages/decap-cms-core/src/actions/entries.ts` | ソース | エントリ関連のReduxアクション |
| editorialWorkflow.js | `packages/decap-cms-core/src/actions/editorialWorkflow.js` | ソース | ワークフロー関連のReduxアクション |
| deploys.js | `packages/decap-cms-core/src/actions/deploys.js` | ソース | デプロイプレビュー関連のアクション |
| App.js | `packages/decap-cms-core/src/components/App/App.js` | ソース | ルーティング定義 |
