カスタムフィールド 仕様書

プロジェクト固有の属性(ストーリーポイント・バージョン等)

ステータス: Draft / 作成日: 2026-05-27 PR #6 — 依存: コア


1. データモデル

1.1 project_custom_fields

pub struct Model {
    pub id: Uuid,
    pub project_id: Uuid,
    pub name: String,
    pub field_type: CustomFieldType,  // text | number | select | date | url | checkbox
    pub options: Option<Value>,       // select 型の選択肢 JSON
    pub is_required: bool,
    pub position: i16,
    pub created_at: DateTimeUtc,
}
カラム 制約 説明
id UUID PK  
project_id UUID NOT NULL, FK→projects CASCADE  
name VARCHAR(100) NOT NULL 例: "Story Points", "バージョン"
field_type VARCHAR NOT NULL text / number / select / date / url / checkbox
options JSONB NULLABLE select 型のみ使用: [{"label":"S","value":"s"},...]
is_required BOOLEAN NOT NULL DEFAULT false  
position SMALLINT NOT NULL 表示順
created_at TIMESTAMPTZ NOT NULL DEFAULT now()  
UNIQUE(project_id, name)  

1.2 task_custom_field_values

カラム 制約 説明
task_id UUID NOT NULL, FK→tasks CASCADE  
field_id UUID NOT NULL, FK→project_custom_fields CASCADE  
value TEXT NULLABLE 全型を文字列で保持。数値・日付はアプリ層で変換
PRIMARY KEY(task_id, field_id)  

型ごとの値フォーマット:

field_type value の形式
text 任意文字列 "メモ"
number 数値文字列 "5"
select optionsvalue "s"
date ISO 8601 日付 "2026-06-01"
url URL 文字列 "https://..."
checkbox "true" / "false" "true"

2. マイグレーション

CREATE TABLE project_custom_fields (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
    name VARCHAR(100) NOT NULL,
    field_type VARCHAR NOT NULL,
    options JSONB,
    is_required BOOLEAN NOT NULL DEFAULT false,
    position SMALLINT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (project_id, name)
);

CREATE TABLE task_custom_field_values (
    task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
    field_id UUID NOT NULL REFERENCES project_custom_fields(id) ON DELETE CASCADE,
    value TEXT,
    PRIMARY KEY (task_id, field_id)
);

3. API

フィールド定義(プロジェクトスコープ)

メソッド パス 説明
GET /custom-fields 定義一覧(position 順)
POST /custom-fields 作成(テナントオーナーのみ)
PUT /custom-fields/{id} 更新
PUT /custom-fields/reorder 並び順一括更新
DELETE /custom-fields/{id} 削除(値も CASCADE)

POST /custom-fields リクエスト:

{
  "name": "Story Points",
  "field_type": "select",
  "options": [
    { "label": "XS (1)", "value": "1" },
    { "label": "S (2)", "value": "2" },
    { "label": "M (3)", "value": "3" },
    { "label": "L (5)", "value": "5" },
    { "label": "XL (8)", "value": "8" }
  ],
  "is_required": false
}

フィールド値(タスクスコープ)

メソッド パス 説明
GET /tasks/{id}/custom-fields タスクのフィールド値一覧
PUT /tasks/{id}/custom-fields 値を一括更新(UPSERT)

PUT リクエスト:

{
  "values": [
    { "field_id": "uuid", "value": "5" },
    { "field_id": "uuid2", "value": "2026-07-01" }
  ]
}

is_required=true のフィールドが未入力のままタスクのステータスを is_done_state=true に変更しようとすると 400 Bad Request

GET /tasks/{id}/custom-fields レスポンス:

{
  "values": [
    {
      "field": { "id": "uuid", "name": "Story Points", "field_type": "select" },
      "value": "5",
      "display_value": "L (5)"
    }
  ]
}

4. フロントエンド(Phase B)

タスク詳細(右ペイン)

── カスタムフィールド ───────────────────
Story Points   [L (5) ▼]
バージョン     [v2.0    ]
対応環境       [本番     ]

管理画面

/tenants/{tid}/projects/{pid}/custom-fields

フィールド一覧 + ドラッグで並び替え。select 型は選択肢をインラインで追加・削除。

コンポーネント

コンポーネント ファイル
CustomFieldValues components/tasks/CustomFieldValues.vue
CustomFieldInput components/tasks/CustomFieldInput.vue
CustomFieldManager pages/custom-fields/+Page.vue