# 機能設計書 42-ステップ作成

## 概要

本ドキュメントは、カードにチェックリスト形式のステップを追加する機能の設計を定義するものである。

### 本機能の処理概要

**業務上の目的・背景**：カンバンボードでタスク管理を行う際、1つのカード（タスク）が複数のサブタスクで構成されることが多い。ステップ機能は、カード内にチェックリスト形式でサブタスクを追加し、進捗を可視化することで、タスクの完了状況を細かく管理できるようにする。これにより、大きなタスクを小さな作業単位に分割して管理でき、作業漏れの防止と進捗把握の効率化を実現する。

**機能の利用シーン**：
- カードに複数のサブタスクを設定したいとき
- タスクの進捗を段階的に管理したいとき
- チームでタスクの完了状況を共有したいとき
- 作業手順をチェックリストとして記録したいとき

**主要な処理内容**：
1. POSTリクエストでステップの内容と初期完了状態を受け取る
2. 対象カードへのアクセス権限を確認する
3. ステップをデータベースに新規登録する
4. Turbo Streamまたは JSONレスポンスで結果を返却する

**関連システム・外部連携**：特になし（内部処理のみ）

**権限による制御**：カードへのアクセス権限を持つユーザーのみがステップを作成可能。CardScopedモジュールにより、`Current.user.accessible_cards`でアクセス可能なカードのみ操作対象となる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | カード詳細画面 | 主機能 | カードにチェックリスト形式のステップを追加 |
| 28 | カードステップ詳細画面 | 参照画面 | カードのステップ詳細を表示 |

## 機能種別

CRUD操作（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| card_id | Integer | Yes | 対象カードのID（URLパラメータ） | 存在確認、アクセス権限確認 |
| step[content] | String | Yes | ステップの内容 | presence: true（空文字不可） |
| step[completed] | Boolean | No | 完了状態（デフォルト: false） | - |

### 入力データソース

- URLパラメータからカードID
- フォームデータまたはJSONからステップの内容と完了状態

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @step | Step | 作成されたステップオブジェクト |

### 出力先

- Turbo Stream: 画面の動的更新（フラグメント挿入）
- JSON: API利用時のレスポンス（201 Created + Locationヘッダー）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ POST /cards/:card_id/steps へのリクエストを受信

2. 認証・認可確認
   └─ CardScopedモジュールのset_cardでカードアクセス権限を確認

3. ステップ作成
   └─ @card.steps.create!(step_params) でステップを新規作成

4. レスポンス生成
   ├─ Turbo Stream: create.turbo_stream.erb をレンダリング
   └─ JSON: 201 Created + Locationヘッダーを返却
```

### フローチャート

```mermaid
flowchart TD
    A[POST /cards/:card_id/steps] --> B{認証済み?}
    B -->|No| C[401 Unauthorized]
    B -->|Yes| D[set_card: カード取得]
    D --> E{カードアクセス可能?}
    E -->|No| F[404 Not Found]
    E -->|Yes| G[step_paramsでパラメータ抽出]
    G --> H[steps.create!でステップ作成]
    H --> I{作成成功?}
    I -->|No| J[422 Unprocessable Entity]
    I -->|Yes| K{リクエスト形式}
    K -->|Turbo Stream| L[create.turbo_stream.erb]
    K -->|JSON| M[201 Created + Location]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-42-01 | カードスコープ | ステップはカードに紐づいて作成される | 常時 |
| BR-42-02 | アカウント自動設定 | ステップのaccount_idはカードのaccount_idが自動設定される | ステップ作成時 |
| BR-42-03 | 内容必須 | ステップの内容（content）は必須項目 | ステップ作成時 |
| BR-42-04 | 初期完了状態 | ステップの初期完了状態はfalse（未完了） | デフォルト値 |
| BR-42-05 | カード更新日時 | ステップ作成時にカードのupdated_atが更新される | touch: trueにより |

### 計算ロジック

特になし

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | 対象カードの存在確認とアクセス権限確認 |
| ステップ作成 | steps | INSERT | 新しいステップレコードを登録 |
| カード更新 | cards | UPDATE | touch: trueによりupdated_atを更新 |

### テーブル別操作詳細

#### steps

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID自動生成 | 主キー |
| INSERT | account_id | card.account_id | 自動設定 |
| INSERT | card_id | params[:card_id] | URLパラメータから |
| INSERT | content | params[:step][:content] | リクエストボディから |
| INSERT | completed | params[:step][:completed] または false | デフォルトfalse |
| INSERT | created_at | 現在時刻 | 自動設定 |
| INSERT | updated_at | 現在時刻 | 自動設定 |

#### cards

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | updated_at | 現在時刻 | touch: trueにより |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未認証状態でアクセス | ログイン画面へリダイレクト |
| 404 | Not Found | 指定カードが存在しないまたはアクセス権限なし | エラーメッセージ表示 |
| 422 | Unprocessable Entity | バリデーションエラー（contentが空など） | エラーメッセージ表示 |

### リトライ仕様

特になし

## トランザクション仕様

- `create!` メソッドにより、ステップ作成とカードのtouch更新が同一トランザクション内で実行される
- 失敗時は自動的にロールバックされる

## パフォーマンス要件

- ステップ作成のレスポンス時間: 300ms以内

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

- 認証必須: ログイン済みユーザーのみアクセス可能
- 認可: CardScopedモジュールにより、アクセス可能なカードのみ操作可能
- Strong Parameters: `params.expect(step: [ :content, :completed ])` でパラメータをホワイトリスト制御
- XSS対策: ステップ内容表示時にエスケープ処理

## 備考

- ステップはカードとの紐付けにより、カードと同じアカウントに自動的に所属する
- `touch: true` により、ステップの変更がカードの更新日時に反映される

---

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

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

### 推奨読解順序

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

まず、ステップのデータ構造とカードとの関連を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | step.rb | `app/models/step.rb` | Stepモデルの定義、belongs_to関連、バリデーション |
| 1-2 | schema.rb | `db/schema.rb` | stepsテーブルの構造（705-715行目） |

**読解のコツ**:
- `belongs_to :account, default: -> { card.account }` により、ステップは親カードと同じアカウントに自動的に所属する
- `belongs_to :card, touch: true` により、ステップ更新時にカードの更新日時も更新される

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

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | steps_controller.rb | `app/controllers/cards/steps_controller.rb` | createアクションの実装（6-13行目） |
| 2-2 | card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | カードスコープの共通処理 |
| 2-3 | routes.rb | `config/routes.rb` | ルーティング定義（96行目） |

**主要処理フロー**:
1. **6行目**: `include CardScoped` によりカードアクセス権限確認を共通化
2. **7行目**: `@step = @card.steps.create!(step_params)` でステップ作成
3. **9-12行目**: レスポンス形式に応じて Turbo Stream または JSON を返却
4. **44-46行目**: `step_params` でStrong Parametersを定義

#### Step 3: ビューレンダリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create.turbo_stream.erb | `app/views/cards/steps/create.turbo_stream.erb` | Turbo Streamレスポンス |
| 3-2 | _step.html.erb | `app/views/cards/steps/_step.html.erb` | ステップの部分テンプレート |

**主要処理フロー**:
- **create.turbo_stream.erb 1-3行目**: `turbo_stream.before` でnew_step位置の前にステップを挿入
- **_step.html.erb 3-5行目**: チェックボックスとフォームで完了状態を管理

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

```
POST /cards/:card_id/steps
    │
    ├─ CardScoped#set_card
    │      └─ Current.user.accessible_cards.find_by!(number: params[:card_id])
    │
    ├─ CardScoped#set_board
    │      └─ @board = @card.board
    │
    └─ Cards::StepsController#create
           │
           ├─ @card.steps.create!(step_params)
           │      └─ Step.create!
           │             ├─ validates :content, presence: true
           │             └─ belongs_to :card, touch: true
           │
           └─ respond_to
                  ├─ format.turbo_stream
                  │      └─ create.turbo_stream.erb
                  │             └─ _step.html.erb
                  │
                  └─ format.json
                         └─ head :created, location: ...
```

### データフロー図

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

POST リクエスト ───────▶ Cards::StepsController#create
  ├─ card_id                    │
  └─ step[content]              ├─▶ CardScoped#set_card
     step[completed]            │      └─▶ @card
                                │
                                ├─▶ step_params
                                │      └─▶ { content:, completed: }
                                │
                                └─▶ @card.steps.create!
                                       │
                                       ▼
                                    @step (DB保存)
                                       │
                                       ▼
                            ┌──────────┴──────────┐
                            │                     │
                     Turbo Stream              JSON
                     (create.turbo_stream.erb) (201 Created)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| steps_controller.rb | `app/controllers/cards/steps_controller.rb` | コントローラー | ステップCRUD処理 |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | Concern | カードスコープ共通処理 |
| step.rb | `app/models/step.rb` | モデル | ステップのデータモデル定義 |
| create.turbo_stream.erb | `app/views/cards/steps/create.turbo_stream.erb` | ビュー | Turbo Streamレスポンス |
| _step.html.erb | `app/views/cards/steps/_step.html.erb` | ビュー | ステップ部分テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | スキーマ | stepsテーブル定義 |
