# 画面設計書 30-タスク編集フォーム

## 概要

本ドキュメントは、Fat Free CRMシステムにおけるタスク編集フォームの設計を記述する。既存タスクの情報更新、期日変更、担当者変更、リスケジュール機能などを提供するモーダル/Ajax形式のフォームである。

### 本画面の処理概要

タスク編集フォームは、登録済みタスクの内容修正や期日変更を行うためのフォームである。クイックリスケジュール機能により、迅速な期日変更も可能。

**業務上の目的・背景**：営業活動中にタスクの優先度や期日が変更になることは頻繁にある。本フォームにより、柔軟なタスク管理と状況変化への迅速な対応を実現する。

**画面へのアクセス方法**：
- タスク一覧画面のタスク名をクリック後、編集リンクをクリック
- ダッシュボードのタスクセクションからタスク名をクリック後、編集リンクをクリック
- 各エンティティ詳細画面のタスクセクションから編集リンクをクリック

**主要な操作・処理内容**：
1. タスク名の修正
2. 期日の変更（バケット選択または特定日時）
3. 担当者の変更
4. カテゴリの変更
5. 背景情報の更新
6. クイックリスケジュール（ASAP/Today/Tomorrow/This Week/Next Week/Later）
7. タスクの保存

**画面遷移**：
- 遷移元：タスク一覧画面、ダッシュボード、各エンティティ詳細画面
- 遷移先（成功時）：タスク一覧画面（一覧からの場合）、遷移元画面

**権限による表示制御**：タスクの作成者または割り当て先のみ編集可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 37 | タスク編集 | 主機能 | タスク情報の編集、期日変更、担当者変更 |
| 41 | タスクフィルタリング | 補助機能 | 更新後のバケット移動に伴うフィルター更新 |

## 画面種別

編集フォーム（モーダル/Ajax）

## URL/ルーティング

### 表示
- URL: `/tasks/:id/edit`
- HTTPメソッド: GET
- コントローラ: `TasksController#edit`

### 更新実行
- URL: `/tasks/:id`
- HTTPメソッド: PATCH/PUT
- コントローラ: `TasksController#update`

## 入出力項目

### 入力パラメータ（GET /tasks/:id/edit）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| id | Integer | 必須 | タスクID |
| view | String | 任意 | 現在のビュー種別 |
| previous | Integer | 任意 | 前のタスクID（連続編集用） |

### 入力パラメータ（PATCH /tasks/:id）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| task[name] | String | 必須 | タスク名 |
| task[assigned_to] | Integer | 任意 | 担当者ユーザーID |
| task[bucket] | String | 任意 | 期日バケット |
| task[calendar] | String | 条件付き | 特定日時（bucketがspecific_timeの場合必須） |
| task[category] | String | 任意 | カテゴリ |
| task[asset_id] | Integer | 任意 | 関連エンティティID |
| task[asset_type] | String | 任意 | 関連エンティティ種別 |
| task[background_info] | String | 任意 | 背景情報 |
| view | String | 任意 | 現在のビュー種別 |

### 出力データ（インスタンス変数）

| 変数名 | 型 | 説明 |
|--------|-----|------|
| @task | Task | 編集対象タスクオブジェクト |
| @view | String | 現在のビュー種別 |
| @bucket | Array | 期日バケット選択肢 |
| @category | Array | カテゴリ選択肢 |
| @asset | Object | 関連エンティティ |
| @previous | Task/Integer | 前のタスク（連続編集用） |

## 表示項目

### 基本情報セクション

| 項目名 | フィールド名 | 入力形式 | 必須 | 説明 |
|--------|------------|---------|------|------|
| タスク名 | name | テキスト | 必須 | タスクの名称 |
| 期日 | bucket | 選択 | - | バケット選択またはカレンダー入力 |
| 特定日時 | calendar | 日時 | 条件付き | bucketがspecific_timeの場合（既存値を表示） |
| 担当者 | assigned_to | 選択 | - | ユーザー選択 |
| カテゴリ | category | 選択 | - | タスクカテゴリ |
| 背景情報 | background_info | テキストエリア | - | 補足情報 |

### 期日バケット選択肢

| 選択肢 | 説明 |
|--------|------|
| Due ASAP | 至急（期日なし） |
| Due Today | 本日中 |
| Due Tomorrow | 明日中 |
| Due This Week | 今週中 |
| Due Next Week | 来週中 |
| Due Later | それ以降 |
| On Specific Date... | 特定日時を指定 |

### クイックリスケジュールリンク

| リンク | 説明 |
|--------|------|
| ASAP | 至急に変更 |
| Today | 本日に変更 |
| Tomorrow | 明日に変更 |
| This Week | 今週中に変更 |
| Next Week | 来週中に変更 |
| Later | それ以降に変更 |

## イベント仕様

### 1-保存ボタンクリック

- **トリガー**: 「Save Task」ボタンをクリック
- **処理**:
  1. フォームがAjaxでPATCH送信される
  2. サーバー側でバリデーション実行
  3. 期日の再計算（bucketに応じてdue_atを設定）
  4. タスクレコードの更新
  5. バケット移動判定（更新前後でバケットが変わった場合）
  6. サイドバー更新（一覧からの場合）
- **成功時**: 一覧画面更新または関連画面更新
- **失敗時**: エラーメッセージ表示、フォーム再表示

### 2-キャンセルリンククリック

- **トリガー**: 「Cancel」リンクをクリック
- **処理**: Ajax経由でフォームを閉じ、元の表示に戻す
- **遷移先**: フォーム表示前の状態に戻る

### 3-クイックリスケジュールリンククリック

- **トリガー**: フッター部のリスケジュールリンク（ASAP/Today等）をクリック
- **処理**:
  1. `crm.reschedule_task()`関数でAjaxリクエスト送信
  2. タスクのbucketを即座に更新
  3. 一覧画面の場合はバケット間移動を反映
- **備考**: フォーム保存せずに直接期日変更が可能

### 4-期日バケット選択変更

- **トリガー**: バケットドロップダウンの選択変更
- **処理**:
  - 「On Specific Date...」選択時: カレンダー入力フィールドを表示
  - その他選択時: カレンダー入力フィールドを非表示
- **備考**: `crm.flip_calendar()`関数で切り替え

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| タスク更新 | tasks | UPDATE | タスクレコード更新 |
| バージョン記録 | versions | INSERT | 変更履歴記録 |

### テーブル別更新項目詳細

#### tasks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | フォーム入力値 | タスク名 |
| UPDATE | assigned_to | フォーム入力値 | 担当者 |
| UPDATE | bucket | フォーム入力値 | 期日バケット |
| UPDATE | due_at | bucketから計算 | 期日（set_due_dateで再計算） |
| UPDATE | category | フォーム入力値 | カテゴリ |
| UPDATE | background_info | フォーム入力値 | 背景情報 |
| UPDATE | updated_at | 現在日時 | 更新日時 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 |
|------|---------------|---------|
| エラー | :missing_task_name | タスク名が未入力 |
| エラー | :invalid_date | 特定日時の形式不正 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| タスクが見つからない | RecordNotFound例外 |
| 権限なし（tracked_byに該当しない） | アクセス拒否 |
| バリデーションエラー | フォーム再表示 |
| 日付解析エラー | :invalid_dateエラー追加 |

## 備考

- フォームはremote: trueでAjax送信される
- 既存のasset_id, asset_typeはhidden_fieldで保持
- 更新前のバケットを保持し、更新後と比較してバケット移動を判定
- specific_time選択時、既存のdue_atをカレンダーフィールドに表示
- クイックリスケジュール機能により、フォーム保存なしで期日変更可能
- PaperTrailによる変更履歴追跡
- @task_before_updateで更新前状態を保持

---

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

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

### 推奨読解順序

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

タスク更新時に関連するデータ構造とバリデーションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | task.rb | `app/models/polymorphic/task.rb` | バリデーション（116-119行目）、before_update :set_due_date（122行目） |
| 1-2 | task.rb | `app/models/polymorphic/task.rb` | computed_bucket（157-173行目）、set_due_date（217-234行目） |

**読解のコツ**: `before_update :set_due_date, unless: :completed?`で完了していないタスクのみ期日が再計算される点に注目。

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

コントローラーのeditとupdateアクションを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tasks_controller.rb | `app/controllers/tasks_controller.rb` | editアクション（54-64行目）、updateアクション（81-101行目） |

**主要処理フロー（edit）**:
1. **55行目**: `@view = view` でビュー種別を取得
2. **56行目**: `Task.tracked_by(current_user).find(params[:id])` で編集対象取得
3. **57行目**: `@bucket` でバケット選択肢を設定
4. **58行目**: `@category` でカテゴリ選択肢を設定
5. **59行目**: `@asset = @task.asset if @task.asset_id?` で関連エンティティ取得
6. **61行目**: `@previous` で前のタスクを取得（連続編集用）

**主要処理フロー（update）**:
1. **82-83行目**: @viewとTaskオブジェクト取得
2. **84行目**: @task_before_update = @task.dup で更新前状態を保持
3. **86-90行目**: 更新前バケットの計算（overdue判定含む）
4. **93行目**: @task.update(task_params)で更新
5. **94行目**: @task.bucket = @task.computed_bucket でバケット再計算
6. **95-98行目**: 一覧ページからの場合、空バケット判定とサイドバー更新

#### Step 3: ビューテンプレートを理解する

フォームの構造とクイックリスケジュール機能を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _edit.html.haml | `app/views/tasks/_edit.html.haml` | フォーム構造、hidden_field、クイックリスケジュールリンク |
| 3-2 | _top_section.html.haml | `app/views/tasks/_top_section.html.haml` | 入力項目、specific_time時の既存値表示（15-18行目） |

**主要処理フロー**:
- **3行目**: link_to_close edit_task_path(@task) でクローズリンク
- **5-6行目**: asset_id, asset_typeをhidden_fieldでセット
- **8行目**: _top_section.html.hamlをedit: trueで呼び出し
- **15-18行目**: クイックリスケジュールリンクの生成

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

```
TasksController#edit
    |
    +-- @view = view
    |
    +-- @task = Task.tracked_by(current_user).find(id)
    |
    +-- @bucket = Setting.unroll(:task_bucket)[1..-1] + [specific_time]
    |
    +-- @category = Setting.unroll(:task_category)
    |
    +-- @asset = @task.asset (if asset_id?)
    |
    +-- @previous = Task.tracked_by(current_user).find_by_id(detect_previous_id)
    |
    +-- respond_with(@task)

TasksController#update
    |
    +-- @view = view
    |
    +-- @task = Task.tracked_by(current_user).find(id)
    |
    +-- @task_before_update = @task.dup
    |       +-- bucket計算（overdue判定）
    |
    +-- @task.update(task_params)
    |       |
    |       +-- before_update :set_due_date (unless completed?)
    |       |       +-- bucketからdue_atを計算
    |       |
    |       +-- [tasks] UPDATE
    |       |
    |       +-- [versions] INSERT (PaperTrail)
    |
    +-- @task.bucket = @task.computed_bucket
    |
    +-- called_from_index_page?
    |       +-- @empty_bucket判定
    |       +-- update_sidebar
    |
    +-- respond_with(@task)
```

### データフロー図

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

フォーム入力
    |
    +-- task[name] ---------> TasksController#update
    |                              |
    +-- task[bucket] -------------+
    |                              |
    +-- task[calendar] -----------+
    |                              |
    +-- task[assigned_to] --------+
    |                              |
    +-- task[category] -----------+
                                   |
                                   +---> @task_before_update保持
                                   |
                                   +---> set_due_date (before_update)
                                   |         |
                                   |         +---> bucketからdue_at計算
                                   |
                                   +---> Task#update
                                   |         |
                                   |         +---> [tasks] UPDATE
                                   |         |
                                   |         +---> [versions] INSERT
                                   |
                                   +---> computed_bucket計算
                                   |
                                   +---> @empty_bucket判定
                                   |         |
                                   |         +---> 更新前バケットが空か判定
                                   |
                                   +-------------------------> JS Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| task.rb | `app/models/polymorphic/task.rb` | モデル | タスクエンティティ、期日計算、バリデーション |
| tasks_controller.rb | `app/controllers/tasks_controller.rb` | コントローラ | edit/updateアクション |
| _edit.html.haml | `app/views/tasks/_edit.html.haml` | テンプレート | 編集フォーム、クイックリスケジュール |
| _top_section.html.haml | `app/views/tasks/_top_section.html.haml` | テンプレート | 入力項目、specific_time処理 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（144行目） |
| crm.js | `app/assets/javascripts/crm.js` | JavaScript | reschedule_task, flip_calendar関数 |
