# 帳票設計書 10-ワークフローボード表示

## 概要

本ドキュメントは、Decap CMSにおける編集ワークフローの状態（Draft/In Review/Ready）をカンバンボード形式で表示する機能の設計を記述する。ドラッグ&ドロップによるステータス変更やエントリの公開・削除操作に対応するWorkflowListコンポーネントの仕組みを定義する。

### 本帳票の処理概要

本帳票は、CMSのエディトリアルワークフロー画面で、未公開エントリの状態を3列のカンバンボード形式で表示し、ドラッグ&ドロップによる直感的なステータス変更操作を提供する機能を定義する。

**業務上の目的・背景**：エディトリアルワークフローは、コンテンツの作成から公開までの承認フローを管理するための仕組みである。Draft（下書き）、In Review（レビュー中）、Ready（公開準備完了）の3段階のステータスを持ち、編集者・レビュアー・承認者が協調してコンテンツの品質を管理する。カンバンボード形式は、タスク管理ツール（Trello、Jira等）で広く採用されるUIパターンであり、ワークフロー全体の状況を一目で把握できる。

**帳票の利用シーン**：CMSの「Workflow」画面にアクセスした際に表示される。エディトリアルワークフローが有効（publish_mode: editorial_workflow）な場合にのみ利用可能。コンテンツのステータス遷移、公開操作、削除操作をこの画面から行う。Open Authoring（オープンオーサリング）モードでは、Ready列が非表示となる2列レイアウトになる。

**主要な出力内容**：
1. 3列のカンバンボード（Draft / In Review / Ready）
2. 各列のヘッダー（色分けされたステータス名）
3. 各列のエントリ数カウント表示
4. WorkflowCard（コレクション名、タイトル、更新日時、本文プレビュー）
5. 削除ボタン・公開ボタン（ホバー時に表示）
6. ドラッグ&ドロップによるステータス変更

**帳票の出力タイミング**：Workflow画面表示時。エントリのドラッグ&ドロップやボタン操作時にリアルタイムで更新される。

**帳票の利用者**：コンテンツ編集者、レビュアー、承認者

## 帳票種別

画面表示（カンバンボード）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | ワークフロー画面 | /workflow | ナビゲーションの「Workflow」リンク |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML（ブラウザ画面表示） |
| 用紙サイズ | N/A（ブラウザ画面内表示） |
| 向き | N/A |
| ファイル名 | N/A（画面表示） |
| 出力方法 | Reactコンポーネントによるレンダリング |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

3列（Draft / In Review / Ready）のカンバンボードレイアウト。CSS Gridで33.3%ずつの等幅3列構成。Open Authoringモードでは50% / 50% / 0%の2列構成になる。

```
┌─────────────────────────────────────────────────────────┐
│ WorkflowListContainer (CSS Grid: 33.3% x 3)             │
│                                                          │
│ ┌──────────┐    ┌──────────┐    ┌──────────┐           │
│ │ Draft    │    │In Review │    │  Ready   │           │
│ │ (緑背景) │    │ (黄背景) │    │ (青背景) │           │
│ ├──────────┤    ├──────────┤    ├──────────┤           │
│ │ N entries│    │ N entries│    │ N entries│           │
│ ├──────────┤    ├──────────┤    ├──────────┤           │
│ │┌────────┐│    │┌────────┐│    │┌────────┐│           │
│ ││WFCard  ││    ││WFCard  ││    ││WFCard  ││           │
│ ││ Coll   ││    ││ Coll   ││    ││ Coll   ││           │
│ ││ Title  ││    ││ Title  ││    ││ Title  ││           │
│ ││ Date   ││    ││ Date   ││    ││ Date   ││           │
│ ││ Body   ││    ││ Body   ││    ││ Body   ││           │
│ ││[Del][Pub]│   ││[Del][Pub]│   ││[Del][Pub]│          │
│ │└────────┘│    │└────────┘│    │└────────┘│           │
│ │          │    │          │    │          │           │
│ └──────────┘    └──────────┘    └──────────┘           │
│    │                 │                                   │
│    ├── DropTarget ───┤── DropTarget ─┤                  │
│    └── 列間ボーダー ─┘              └                   │
└─────────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 列ヘッダー（Draft） | 下書きステータスヘッダー | t('workflow.workflowList.draftHeader') | 緑背景のColumnHeader |
| 2 | 列ヘッダー（In Review） | レビュー中ステータスヘッダー | t('workflow.workflowList.inReviewHeader') | 黄背景のColumnHeader |
| 3 | 列ヘッダー（Ready） | 公開準備完了ステータスヘッダー | t('workflow.workflowList.readyHeader') | 青背景のColumnHeader |
| 4 | エントリ数 | 各列のエントリ件数 | currEntries.size | ColumnCount（大文字テキスト） |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | コレクションラベル | エントリが属するコレクション名 | collection.get('label') | CardCollection（大文字14px） | N/A |
| 2 | タイトル | エントリのタイトル | selectEntryCollectionTitle(collection, entry) | CardTitle（h2） | N/A |
| 3 | 更新日時 | 最終更新日時と更新者 | entry.get('updatedOn') + metaData.user | CardDate（dayjs.format） | N/A |
| 4 | 本文プレビュー | エントリ本文の冒頭部分 | entry.getIn(['data', 'body']) | CardBody（テキスト） | N/A |
| 5 | 投稿者 | エントリの著者 | entry.get('author') | テキスト | N/A |
| 6 | 削除ボタン | エントリ削除操作 | ホバー時に表示 | DeleteButton（赤背景） | N/A |
| 7 | 公開ボタン | エントリ公開操作 | ホバー時に表示（Ready時のみ有効） | PublishButton（teal背景） | N/A |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| エディトリアルワークフロー | publish_mode: editorial_workflowが有効であること | Yes |
| エントリデータ | ワークフロー上のエントリ（OrderedMap形式） | Yes |
| コレクション定義 | 全コレクションのMap | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ステータス列別（Draft / In Review / Ready） | OrderedMapの定義順 |
| 2 | 各列内のエントリ順序 | Redux storeの格納順 |

### 改ページ条件

N/A（スクロールベースの画面表示。各列は独立してスクロール可能）

## データベース参照仕様

### 参照テーブル一覧

N/A（Redux storeからデータ取得。バックエンドはGitリポジトリ）

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 更新日時フォーマット | `dayjs(entry.get('updatedOn')).format(t('workflow.workflow.dateFormat'))` | N/A | ロケール対応の日時フォーマット |
| 編集リンク | `collections/${collectionName}/entries/${slug}?ref=workflow` | N/A | ワークフローからの編集リンク（ref=workflowパラメータ付き） |
| 公開可否判定 | `ownStatus === status.last() && !entry.get('isPersisting', false)` | N/A | ステータスがReady（最後のステータス）かつ永続化中でない場合に公開可能 |
| 公開許可判定 | `collection?.get('publish')` | N/A | コレクション設定でpublishが許可されているか |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Workflow画面表示] --> B[WorkflowList render]
    B --> C{isOpenAuthoring?}
    C -->|Yes| D[2列レイアウト]
    C -->|No| E[3列レイアウト]
    D --> F[renderColumns - 列レンダリング]
    E --> F
    F --> G[entries.entrySeq で列ごとに反復]
    G --> H[DropTarget生成]
    H --> I[ColumnHeader表示]
    I --> J[ColumnCount表示]
    J --> K[renderColumns - カードレンダリング]
    K --> L[entries.map でカード反復]
    L --> M[DragSource + WorkflowCard生成]
    M --> N{ドラッグ操作?}
    N -->|Yes| O[handleChangeStatus]
    O --> P[handleChangeStatus props呼び出し]
    M --> Q{削除クリック?}
    Q -->|Yes| R[requestDelete - confirm後削除]
    M --> S{公開クリック?}
    S -->|Yes| T[requestPublish - Ready確認後公開]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 公開不可 | ステータスがReadyでないエントリの公開試行 | t('workflow.workflowList.onPublishingNotReadyEntry')（window.alert） | エントリをReady列に移動してから公開 |
| 削除確認 | 削除ボタン押下時 | t('workflow.workflowList.onDeleteEntry')（window.confirm） | 確認ダイアログでOKを選択 |
| 公開確認 | 公開ボタン押下時 | t('workflow.workflowList.onPublishEntry')（window.confirm） | 確認ダイアログでOKを選択 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数十〜数百の未公開エントリ（3列に分散） |
| 目標出力時間 | 数百ミリ秒（初回レンダリング） |
| 同時出力数上限 | 全ワークフローエントリを一括表示 |

## セキュリティ考慮事項

- 公開操作はwindow.confirmによる確認ダイアログを経由する
- 削除操作もwindow.confirmによる確認ダイアログを経由する
- ドラッグ&ドロップはDNDNamespace（'cms-workflow'）で名前空間が限定されており、外部からのドロップを防止
- Open Authoringモードでは公開（Ready）列が非表示となり、直接公開操作を制限

## 備考

- HTML5DragDropのHOCでドラッグ&ドロップ機能を提供
- react-polyglotのtranslate() HOCで国際化対応
- DragSourceはエントリカード、DropTargetは各列に設定
- DNDNamespace = 'cms-workflow'で名前空間を限定し、他のドラッグ&ドロップ操作との干渉を防止
- カラムヘッダーの色は、draft=緑系、pending_review=黄系、pending_publish=青系で視覚的に区別
- 列間には縦線のボーダーが表示される（中央列の::before/::afterで実装）
- isModificationフラグにより、既存エントリの変更か新規エントリかで削除/公開ボタンのラベルが変わる
- statusオブジェクトはOrderedMap（publishModes.ts）で定義され、status.last()でReadyステータスを取得

---

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

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

### 推奨読解順序

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

ワークフローのステータス定義とデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | publishModes.ts | `packages/decap-cms-core/src/constants/publishModes.ts` | Statues定数（DRAFT, PENDING_REVIEW, PENDING_PUBLISH）とstatus OrderedMapの定義を確認（7-14行目） |
| 1-2 | WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | static propTypes（132-140行目）で受け取るpropsの構造を確認。entries(OrderedMap), handleChangeStatus, handlePublish, handleDelete, isOpenAuthoring, collectionsを理解 |

**読解のコツ**: entriesはOrderedMapで、キーがステータス名（draft, pending_review, pending_publish）、値がそのステータスのエントリ群となっている。OrderedMapであるため、entrySeq()で挿入順序を保持した反復が可能。

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

renderメソッドとrenderColumnsメソッドの処理フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | renderメソッド（263-268行目）でisOpenAuthoringに応じたListContainerの選択を確認 |
| 2-2 | WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | renderColumnsメソッド（171-261行目）の2段階処理（列レベル/カードレベル）を確認 |

**主要処理フロー**:
1. **264行目**: `this.renderColumns(this.props.entries)` で列レンダリング開始
2. **265-267行目**: isOpenAuthoringに応じてListContainerを選択（3列 or 2列）
3. **176行目**: column引数なしの場合、entries.entrySeq().mapで列を反復
4. **177-208行目**: 各列にDropTargetを設定し、ColumnHeader/ColumnCountを表示
5. **202行目**: `this.renderColumns(currEntries, currColumn)` で列内カードを再帰呼び出し
6. **212-258行目**: column引数ありの場合、entries.mapで各カードを生成

#### Step 3: ドラッグ&ドロップを理解する

DragSource/DropTargetの設定と、ステータス変更ハンドラを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | handleChangeStatus（147-152行目）でドロップ時のステータス変更処理を確認 |
| 3-2 | WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | DropTarget（177行目）とDragSource（229行目）のnamespace設定を確認 |

**主要処理フロー**:
- **177-180行目**: DropTargetにDNDNamespace='cms-workflow'を設定し、onDropでhandleChangeStatusをバインド
- **229-235行目**: DragSourceにDNDNamespace、slug、collection、ownStatusをpropsとして設定
- **147行目**: `handleChangeStatus(newStatus, dragProps)` でdragPropsからslug/collection/oldStatusを取得
- **151行目**: `this.props.handleChangeStatus(collection, slug, oldStatus, newStatus)` を呼び出し

#### Step 4: WorkflowCardコンポーネントを理解する

個別のワークフローカードの表示ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | WorkflowCard.js | `packages/decap-cms-core/src/components/Workflow/WorkflowCard.js` | WorkflowCard関数コンポーネント（119-158行目）でカードのレイアウトと操作ボタンの表示を確認 |
| 4-2 | WorkflowCard.js | `packages/decap-cms-core/src/components/Workflow/WorkflowCard.js` | CardDate（110-117行目）で更新日時の条件付き表示を確認 |

**主要処理フロー**:
- **136行目**: WorkflowLinkでエントリ編集画面へのリンクを提供
- **137行目**: CardCollectionでコレクションラベル表示
- **139行目**: CardTitleでエントリタイトル表示
- **140行目**: CardDateで更新日時と更新者を表示
- **141行目**: CardBodyで本文プレビュー表示
- **143-156行目**: CardButtonContainerで削除/公開ボタンを表示（ホバー時にopacity: 1で表示）
- **149-155行目**: allowPublishとcanPublishの条件で公開ボタンの表示/非表示/無効化を制御

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

```
WorkflowList.render (WorkflowList.js:263)
    |
    +-- renderColumns (WorkflowList.js:171) [列レベル]
    |       |
    |       +-- entries.entrySeq().map
    |       |       +-- DropTarget (DNDNamespace='cms-workflow')
    |       |       +-- ColumnHeader (getColumnHeaderText)
    |       |       +-- ColumnCount
    |       |
    |       +-- renderColumns [カードレベル] (再帰呼び出し)
    |               |
    |               +-- entries.map
    |                       +-- DragSource (DNDNamespace='cms-workflow')
    |                       +-- WorkflowCard (WorkflowCard.js)
    |                               +-- WorkflowLink
    |                               +-- CardCollection
    |                               +-- CardTitle
    |                               +-- CardDate
    |                               +-- CardBody
    |                               +-- DeleteButton (requestDelete)
    |                               +-- PublishButton (requestPublish)
    |
    +-- handleChangeStatus (ドラッグ&ドロップ)
    +-- requestDelete (削除ボタン)
    +-- requestPublish (公開ボタン)
```

### データフロー図

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

Redux Store ────────────┐
  |                     |
  +-- entries           |
  |   (OrderedMap)      |
  |   +-- draft --------+-> renderColumns ---------> カンバンボード
  |   +-- pending_review +     |                      |
  |   +-- pending_publish+     +-- DropTarget         +-- Draft列
  |                     |     +-- DragSource          +-- In Review列
  +-- collections ------+     +-- WorkflowCard        +-- Ready列
                              |                        |
  props ────────────────+     +-- handleChangeStatus   +-- WorkflowCard x N
  +-- handleChangeStatus+     +-- requestDelete        |     +-- Title
  +-- handlePublish ----+     +-- requestPublish       |     +-- Date
  +-- handleDelete -----+                              |     +-- Body
  +-- isOpenAuthoring --+                              |     +-- [Del][Pub]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| WorkflowList.js | `packages/decap-cms-core/src/components/Workflow/WorkflowList.js` | ソース | ワークフローボードメインコンポーネント（列レンダリング、DnD処理） |
| WorkflowCard.js | `packages/decap-cms-core/src/components/Workflow/WorkflowCard.js` | ソース | ワークフローカードコンポーネント（タイトル、日時、ボタン表示） |
| publishModes.ts | `packages/decap-cms-core/src/constants/publishModes.ts` | ソース | ステータス定数（DRAFT, PENDING_REVIEW, PENDING_PUBLISH）とOrderedMap |
| UI/index.js | `packages/decap-cms-core/src/components/UI/index.js` | ソース | DragSource、DropTarget、HTML5DragDropコンポーネント |
| collections.js | `packages/decap-cms-core/src/reducers/collections.js` | ソース | selectEntryCollectionTitleセレクタ |
