操作
バグ #148
未完了【設計】Redmineカンバンビュー拡張機能
Redmine Admin さんが5日前に追加. 5日前に更新.
ステータス:
解決
優先度:
通常
担当者:
-
開始日:
2025-06-02
期日:
進捗率:
0%
予定工数:
説明
概要¶
Redmineカンバンビュー拡張機能の設計を行う。
設計項目¶
-
アーキテクチャ設計
- プラグイン構造
- クラス設計
- データフロー
-
詳細ボタン実装設計
- HTML/CSS/JavaScript設計
- イベントハンドリング設計
- レスポンシブデザイン対応
-
担当者別カンバンビュー設計
- コントローラー設計
- モデル設計
- ビュー設計
- クエリ最適化設計
-
日次/週次/月次フィルター設計
- UI設計
- フィルタリングロジック設計
- 日付計算アルゴリズム設計
-
データベース設計
- 既存テーブルへの影響分析
- クエリパフォーマンス設計
成果物¶
- 詳細設計書
- クラス図
- シーケンス図
- データベース設計図(必要な場合)
- API設計書(必要な場合)
参考情報¶
- Redmineのバージョン: 最新版
- 対象環境: task2.call2arm.com
- 既存プラグイン: Kanban (Agile) board または redmine_agile
現状¶
設計前の初期段階。仕様検討の完了を待っている状態。
Redmine Admin さんが5日前に更新
【設計】Redmineカンバンビュー拡張機能¶
1. アーキテクチャ設計¶
1.1 全体構成¶
+-----------------------------------+
| Redmine API サーバー |
| (call2arm.com) |
+-----------------------------------+
↑↓
+-----------------------------------+
| React カスタムUI |
| (task2.call2arm.com) |
| |
| +-------------------------------+ |
| | コンポーネント | |
| | | |
| | - KanbanBoard | |
| | - KanbanCard | |
| | - AssigneeKanbanView | |
| | - DateRangeFilter | |
| +-------------------------------+ |
| |
| +-------------------------------+ |
| | サービス | |
| | | |
| | - RedmineApiService | |
| | - KanbanDataService | |
| | - DateFilterService | |
| +-------------------------------+ |
+-----------------------------------+
1.2 データフロー¶
User Action → React Component → API Service → Redmine API →
API Response → State Update → Component Re-render
2. コンポーネント設計¶
2.1 コンポーネント階層¶
App
└── MainLayout
└── TicketsPage
├── TicketsHeader
│ ├── ViewToggle (List/Kanban/AssigneeKanban)
│ └── DateRangeFilter
│
├── TicketsList (既存)
├── KanbanBoard (新規)
│ ├── KanbanColumn
│ │ └── KanbanCard
│ │ └── DetailButton
│ └── DragDropContext
│
└── AssigneeKanbanBoard (新規)
├── AssigneeRow
│ └── StatusCell
│ └── KanbanCard
│ └── DetailButton
└── DateRangeContext
2.2 各コンポーネントの責務¶
KanbanBoard¶
- 責務: 標準的なカンバンボードを表示(列がステータス)
-
Props:
-
tickets
: チケット配列 -
statuses
: ステータス配列 -
dateRange
: 日付範囲 -
onTicketMove
: チケット移動時のコールバック
-
-
状態:
-
groupedTickets
: ステータス別にグループ化されたチケット
-
AssigneeKanbanBoard¶
- 責務: 担当者別カンバンボードを表示(行が担当者、列がステータス)
-
Props:
-
tickets
: チケット配列 -
statuses
: ステータス配列 -
users
: ユーザー配列 -
dateRange
: 日付範囲 -
onTicketMove
: チケット移動時のコールバック
-
-
状態:
-
matrixTickets
: 担当者×ステータスのマトリックス形式のチケット
-
KanbanCard¶
- 責務: チケット情報をカード形式で表示
-
Props:
-
ticket
: チケット情報 -
isDraggable
: ドラッグ可能かどうか -
showDetailButton
: 詳細ボタンを表示するかどうか
-
-
機能:
- チケット情報表示
- ドラッグ&ドロップ対応
- 詳細ボタンの表示
DetailButton¶
- 責務: チケット詳細画面へのリンクボタン
-
Props:
-
ticketId
: チケットID -
style
: ボタンスタイル
-
-
機能:
- クリック時にチケット詳細画面に遷移
DateRangeFilter¶
- 責務: 日付範囲フィルターの表示と制御
-
Props:
-
dateRange
: 現在の日付範囲 -
onDateRangeChange
: 日付範囲変更時のコールバック
-
-
状態:
-
filterType
: 'daily', 'weekly', 'monthly', 'custom' -
customStartDate
: カスタム開始日 -
customEndDate
: カスタム終了日
-
3. データモデル設計¶
3.1 基本データ構造¶
// チケット
interface Ticket {
id: number;
subject: string;
status_id: number;
assigned_to_id: number | null;
start_date: string | null;
due_date: string | null;
project_id: number;
priority_id: number;
done_ratio: number;
// その他の属性
}
// ステータス
interface Status {
id: number;
name: string;
is_closed: boolean;
}
// ユーザー
interface User {
id: number;
name: string;
avatar_url: string | null;
}
// 日付範囲
interface DateRange {
startDate: Date | null;
endDate: Date | null;
type: 'daily' | 'weekly' | 'monthly' | 'custom';
}
3.2 状態管理設計¶
// アプリケーション状態
interface AppState {
tickets: Ticket[];
statuses: Status[];
users: User[];
projects: Project[];
currentView: 'list' | 'kanban' | 'assigneeKanban';
dateRange: DateRange;
loading: boolean;
error: string | null;
}
// アクション
enum ActionType {
FETCH_TICKETS_REQUEST = 'FETCH_TICKETS_REQUEST',
FETCH_TICKETS_SUCCESS = 'FETCH_TICKETS_SUCCESS',
FETCH_TICKETS_FAILURE = 'FETCH_TICKETS_FAILURE',
UPDATE_TICKET_STATUS = 'UPDATE_TICKET_STATUS',
CHANGE_VIEW = 'CHANGE_VIEW',
SET_DATE_RANGE = 'SET_DATE_RANGE',
// その他のアクション
}
4. API設計¶
4.1 Redmine APIとの連携¶
// API サービス
class RedmineApiService {
// チケット一覧取得
async getTickets(params: {
project_id?: number;
status_id?: number;
assigned_to_id?: number;
start_date?: string;
due_date?: string;
}): Promise<Ticket[]> {
// APIリクエスト実装
}
// チケット更新
async updateTicket(id: number, data: {
status_id?: number;
assigned_to_id?: number;
// その他の更新可能フィールド
}): Promise<Ticket> {
// APIリクエスト実装
}
// ステータス一覧取得
async getStatuses(): Promise<Status[]> {
// APIリクエスト実装
}
// ユーザー一覧取得
async getUsers(): Promise<User[]> {
// APIリクエスト実装
}
}
4.2 日付フィルター処理¶
// 日付フィルターサービス
class DateFilterService {
// 日次フィルター
static getDaily(): DateRange {
const today = new Date();
return {
startDate: today,
endDate: today,
type: 'daily'
};
}
// 週次フィルター
static getWeekly(): DateRange {
const today = new Date();
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() - today.getDay());
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 6);
return {
startDate: startOfWeek,
endDate: endOfWeek,
type: 'weekly'
};
}
// 月次フィルター
static getMonthly(): DateRange {
const today = new Date();
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0);
return {
startDate: startOfMonth,
endDate: endOfMonth,
type: 'monthly'
};
}
// チケットのフィルタリング
static filterTicketsByDateRange(tickets: Ticket[], dateRange: DateRange): Ticket[] {
if (!dateRange.startDate || !dateRange.endDate) {
return tickets;
}
return tickets.filter(ticket => {
const ticketStartDate = ticket.start_date ? new Date(ticket.start_date) : null;
const ticketDueDate = ticket.due_date ? new Date(ticket.due_date) : null;
// 開始日も終了日も未設定のチケットは常に表示
if (!ticketStartDate && !ticketDueDate) {
return true;
}
// チケット開始日 <= フィルター終了日 かつ
// (チケット終了日がない または チケット終了日 >= フィルター開始日)
return (!ticketStartDate || ticketStartDate <= dateRange.endDate) &&
(!ticketDueDate || ticketDueDate >= dateRange.startDate);
});
}
}
5. UI設計¶
5.1 チケットパネル詳細ボタン¶
// DetailButton コンポーネント
const DetailButton: React.FC<{ ticketId: number }> = ({ ticketId }) => {
return (
<Link
to={`/redmine-ui/tickets/${ticketId}`}
className="detail-button"
title="チケット詳細を表示"
>
詳細
</Link>
);
};
// スタイル (CSS-in-JS または CSS)
const styles = {
detailButton: {
position: 'absolute',
bottom: '8px',
right: '8px',
padding: '2px 6px',
backgroundColor: '#f0f0f0',
border: '1px solid #ccc',
borderRadius: '3px',
fontSize: '0.8em',
textDecoration: 'none',
color: '#333',
'&:hover': {
backgroundColor: '#e0e0e0',
}
}
};
5.2 担当者別カンバンビュー¶
// AssigneeKanbanBoard コンポーネント(部分抜粋)
const AssigneeKanbanBoard: React.FC<AssigneeKanbanBoardProps> = ({
tickets,
statuses,
users,
dateRange,
}) => {
// チケットをマトリックス形式に変換
const ticketMatrix = useMemo(() => {
// マトリックス初期化ロジック
// ...
return matrix;
}, [tickets, statuses, users, dateRange]);
return (
<div className="assignee-kanban-board">
<table>
<thead>
<tr>
<th>担当者</th>
{statuses.map(status => (
<th key={status.id}>{status.name}</th>
))}
</tr>
</thead>
<tbody>
{/* ユーザー行の出力 */}
{/* ... */}
</tbody>
</table>
</div>
);
};
5.3 日次/週次/月次フィルター¶
// DateRangeFilter コンポーネント(部分抜粋)
const DateRangeFilter: React.FC<DateRangeFilterProps> = ({
dateRange,
onDateRangeChange,
}) => {
// 状態とイベントハンドラ
// ...
return (
<div className="date-range-filter">
<div className="filter-buttons">
<button
className={filterType === 'daily' ? 'active' : ''}
onClick={() => handleFilterTypeChange('daily')}
>
日次
</button>
{/* 他のボタン */}
</div>
{/* カスタム日付範囲入力フォーム */}
</div>
);
};
6. 実装計画¶
6.1 フェーズ分け¶
-
フェーズ1: 基本カンバンビュー
- 標準カンバンボード実装
- チケットカード実装
- 詳細ボタン実装
-
フェーズ2: 担当者別カンバンビュー
- 担当者×ステータスマトリックス実装
- API連携実装
-
フェーズ3: 日付フィルター
- 日次/週次/月次フィルター実装
- カスタム日付範囲実装
-
フェーズ4: 最適化
- パフォーマンス改善
- UI/UX改善
- バグ修正
6.2 開発期間見積もり¶
- フェーズ1: 3日
- フェーズ2: 4日
- フェーズ3: 2日
- フェーズ4: 1日
- 合計: 10日
7. まとめ¶
本設計書では、Redmineカンバンビュー拡張機能の詳細な設計を行いました。仕様検討フェーズでの要件に基づき、コンポーネント設計、データフロー設計、API設計、UI設計の詳細を定義しました。
この設計に基づいて実装フェーズに進むことで、以下の機能を備えたカンバンビュー拡張機能を実現します:
- チケットパネルに詳細ボタンを追加
- 担当者別カンバンビューの実装
- 日次/週次/月次でのフィルタリング機能
- 日付範囲に基づくチケット表示
操作