プロジェクト

全般

プロフィール

機能 #193

未完了

Redmine左ナビ AIチャット機能追加 - チャット一覧・1ペイン表示・履歴管理

Redmine Admin さんが4日前に追加. 4日前に更新.

ステータス:
新規
優先度:
高め
担当者:
開始日:
2025-06-04
期日:
進捗率:

0%

予定工数:

説明

機能要望概要

対象箇所: Redmine左ナビゲーションメニュー
要望内容: AIチャット機能をメインナビゲーションに追加
配置位置: カレンダーの位置(カレンダー削除後)

機能仕様

1. 左ナビゲーション追加

メニュー構成:

📊 ダッシュボード
🎫 チケット  
🏗️ プロジェクト管理
📰 ニュース
📄 ドキュメント
🤖 AIチャット ← 新規追加(カレンダー位置)

2. AIチャット機能構成

A. チャット一覧表示

  • 既存チャット履歴の表示
  • チャット件名(タイトル)一覧
  • 最終更新日時
  • チャット状態(アクティブ/非アクティブ)

B. 新規チャット作成

  • 「新規チャット」ボタン
  • 1ペインでのチャット画面起動
  • フルページでのチャット体験

C. 既存チャット再開

  • 件名クリックでチャット画面表示
  • 過去の会話履歴継続
  • 1ペインでのWEB UI利用

UI/UX設計

1. ナビゲーションメニュー

<nav className="sidebar-nav">
  {/* 既存メニュー */}
  <NavItem icon="📊" label="ダッシュボード" to="/dashboard" />
  <NavItem icon="🎫" label="チケット" to="/tickets" />
  <NavItem icon="🏗️" label="プロジェクト管理" to="/projects" />
  <NavItem icon="📰" label="ニュース" to="/news" />
  <NavItem icon="📄" label="ドキュメント" to="/documents" />
  
  {/* 新規追加 */}
  <NavItem icon="🤖" label="AIチャット" to="/ai-chat" />
</nav>

2. AIチャット一覧ページ (/ai-chat)

<div className="ai-chat-list-page">
  {/* ヘッダー */}
  <div className="page-header">
    <h1 className="text-2xl font-bold">AIチャット</h1>
    <button className="btn btn-primary">
      ➕ 新規チャット
    </button>
  </div>
  
  {/* チャット一覧 */}
  <div className="chat-list">
    {chatHistory.map(chat => (
      <div key={chat.id} className="chat-item">
        <div className="chat-title">
          <a href={`/ai-chat/${chat.id}`}>{chat.title}</a>
        </div>
        <div className="chat-meta">
          <span className="last-updated">{chat.lastUpdated}</span>
          <span className="message-count">{chat.messageCount}</span>
        </div>
      </div>
    ))}
  </div>
</div>

3. 1ペインチャット画面 (/ai-chat/new, /ai-chat/:id)

<div className="ai-chat-fullpage">
  {/* チャットヘッダー */}
  <div className="chat-header">
    <button className="back-btn">← 一覧に戻る</button>
    <h2 className="chat-title">{chatTitle || "新規チャット"}</h2>
    <div className="chat-actions">
      <button className="save-btn">💾 保存</button>
      <button className="delete-btn">🗑️ 削除</button>
    </div>
  </div>
  
  {/* チャットエリア */}
  <div className="chat-messages-area">
    {messages.map(message => (
      <ChatMessage key={message.id} message={message} />
    ))}
  </div>
  
  {/* 入力エリア */}
  <div className="chat-input-area">
    <input 
      type="text" 
      placeholder="メッセージを入力..."
      className="chat-input"
    />
    <button className="send-btn">送信</button>
  </div>
</div>

データ管理仕様

1. チャット履歴保存

// チャットデータ構造
const chatSession = {
  id: 'chat_uuid_001',
  title: 'Redmine設定について',
  createdAt: '2025-06-04T18:30:00Z',
  lastUpdated: '2025-06-04T19:15:00Z',
  messageCount: 12,
  messages: [
    {
      id: 'msg_001',
      role: 'user',
      content: 'Redmineの設定方法を教えて',
      timestamp: '2025-06-04T18:30:00Z'
    },
    {
      id: 'msg_002', 
      role: 'assistant',
      content: 'Redmineの設定について説明します...',
      timestamp: '2025-06-04T18:30:15Z'
    }
  ],
  context: {
    url: 'https://task.call2arm.com/settings',
    pageContent: '...'
  }
};

2. 永続化ストレージ

  • ローカルストレージ: ブラウザでの一時保存
  • サーバーサイド: データベースでの永続保存
  • セッション管理: ユーザー別のチャット履歴

技術実装要件

1. ルーティング追加

// React Router設定
<Routes>
  {/* 既存ルート */}
  <Route path="/dashboard" element={<Dashboard />} />
  <Route path="/tickets" element={<Tickets />} />
  <Route path="/projects" element={<Projects />} />
  
  {/* 新規追加 */}
  <Route path="/ai-chat" element={<AIchatList />} />
  <Route path="/ai-chat/new" element={<AIchatFullpage />} />
  <Route path="/ai-chat/:chatId" element={<AIchatFullpage />} />
</Routes>

2. API エンドポイント

# チャット管理API
GET    /api/ai-chat/sessions          # チャット一覧取得
POST   /api/ai-chat/sessions          # 新規チャット作成
GET    /api/ai-chat/sessions/:id      # 特定チャット取得
PUT    /api/ai-chat/sessions/:id      # チャット更新
DELETE /api/ai-chat/sessions/:id      # チャット削除

# メッセージ管理API
POST   /api/ai-chat/sessions/:id/messages  # メッセージ送信
GET    /api/ai-chat/sessions/:id/messages  # メッセージ履歴取得

3. 状態管理

// Zustand/Redux での状態管理
const useAIChatStore = create((set, get) => ({
  chatSessions: [],
  currentChat: null,
  
  loadChatSessions: async () => {
    const sessions = await fetchChatSessions();
    set({ chatSessions: sessions });
  },
  
  createNewChat: async (initialMessage) => {
    const newChat = await createChatSession(initialMessage);
    set(state => ({
      chatSessions: [newChat, ...state.chatSessions],
      currentChat: newChat
    }));
  },
  
  loadChat: async (chatId) => {
    const chat = await fetchChatSession(chatId);
    set({ currentChat: chat });
  }
}));

期待効果

  • アクセス性向上: AIチャットへの直接アクセス
  • 履歴管理: 過去の相談内容の再参照
  • 作業継続性: 中断したチャットの再開
  • 統合体験: Redmine内でのシームレスなAI活用

関連チケット

  • #190: カレンダー機能削除(前提条件)
  • #191: Claude API接続問題(解決必要)
  • #192: AIアシスタント機能拡張(統合可能)

優先度

  • 緊急度: 中(UI改善・機能追加)
  • 重要度: 高(AI統合の中核機能)

Redmine Admin さんが4日前に更新

追加要望: AIチャット一覧の検索・絞り込み機能

1. 検索機能

全文検索バー追加:

<div className="ai-chat-search-section">
  <div className="search-bar">
    <input 
      type="text"
      placeholder="チャット内容を検索..."
      className="w-full px-4 py-2 border rounded-lg"
      onChange={handleSearchInput}
    />
    <button className="search-btn">🔍</button>
  </div>
</div>

検索対象:

  • ✅ チャットタイトル/件名
  • ✅ チャット内容(全メッセージ)
  • ✅ ユーザーメッセージ
  • ✅ AI回答内容
  • ✅ 関連プロジェクト情報

2. 絞り込み機能

フィルタリングオプション:

<div className="filter-section">
  {/* 表示範囲選択 */}
  <select className="scope-filter">
    <option value="all">全体</option>
    <option value="my">自分のチャット</option>
    <option value="project">プロジェクト別</option>
  </select>
  
  {/* 利用者別フィルタ */}
  <select className="user-filter">
    <option value="">すべてのユーザー</option>
    <option value="1">Redmine Admin</option>
    <option value="2">ユーザーB</option>
  </select>
  
  {/* プロジェクト別フィルタ */}
  <select className="project-filter">
    <option value="">すべてのプロジェクト</option>
    <option value="ai-chat-general">AIチャット一覧</option>
    <option value="1">Redmineの設定</option>
    <option value="2">サービス監視基盤構築</option>
    <option value="3">コーディング自動化基盤</option>
  </select>
</div>

3. プロジェクト分類システム

デフォルトプロジェクト:

  • プロジェクト未指定: 自動的に「AIチャット一覧」プロジェクトに格納
  • プロジェクト指定: 対応するRedmineプロジェクトと紐付け

データ構造:

const chatSession = {
  id: 'chat_uuid_001',
  title: 'nginx設定について相談',
  projectId: 'ai-chat-general', // デフォルト
  projectName: 'AIチャット一覧',
  userId: 1,
  userName: 'Redmine Admin',
  createdAt: '2025-06-04T18:30:00Z',
  messages: [...],
  tags: ['nginx', '設定', 'プロキシ'] // 検索用タグ
};

// プロジェクト指定の場合
const projectChat = {
  id: 'chat_uuid_002', 
  title: 'VPSセキュリティ強化方針',
  projectId: 5, // VPSセキュリティプロジェクト
  projectName: 'VPSセキュリティ',
  userId: 1,
  // ...
};

4. 検索・フィルタ画面設計

<div className="ai-chat-list-page">
  {/* 検索・フィルタセクション */}
  <div className="search-filter-section bg-gray-50 p-4 rounded-lg mb-6">
    {/* 検索バー */}
    <div className="search-bar mb-4">
      <input 
        type="text"
        placeholder="チャット内容、タイトル、プロジェクト名で検索..."
        className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500"
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
      />
    </div>
    
    {/* フィルタ群 */}
    <div className="filters grid grid-cols-1 md:grid-cols-3 gap-4">
      <div>
        <label className="block text-sm font-medium mb-2">表示範囲</label>
        <select 
          className="w-full border rounded-lg px-3 py-2"
          value={scopeFilter}
          onChange={(e) => setScopeFilter(e.target.value)}
        >
          <option value="all">全体</option>
          <option value="my">自分のチャット</option>
          <option value="shared">共有チャット</option>
        </select>
      </div>
      
      <div>
        <label className="block text-sm font-medium mb-2">利用者</label>
        <select 
          className="w-full border rounded-lg px-3 py-2"
          value={userFilter}
          onChange={(e) => setUserFilter(e.target.value)}
        >
          <option value="">すべてのユーザー</option>
          {users.map(user => (
            <option key={user.id} value={user.id}>{user.name}</option>
          ))}
        </select>
      </div>
      
      <div>
        <label className="block text-sm font-medium mb-2">プロジェクト</label>
        <select 
          className="w-full border rounded-lg px-3 py-2"
          value={projectFilter}
          onChange={(e) => setProjectFilter(e.target.value)}
        >
          <option value="">すべてのプロジェクト</option>
          <option value="ai-chat-general">AIチャット一覧</option>
          {projects.map(project => (
            <option key={project.id} value={project.id}>{project.name}</option>
          ))}
        </select>
      </div>
    </div>
    
    {/* アクティブフィルタ表示 */}
    <div className="active-filters mt-4">
      {activeFilters.map(filter => (
        <span key={filter.id} className="inline-flex items-center px-3 py-1 rounded-full text-xs bg-green-100 text-green-800 mr-2">
          {filter.label}
          <button onClick={() => removeFilter(filter.id)} className="ml-1 text-green-600">×</button>
        </span>
      ))}
    </div>
  </div>
  
  {/* 検索結果・チャット一覧 */}
  <div className="chat-list">
    <div className="results-header mb-4">
      <span className="text-sm text-gray-600">
        {filteredChats.length}件のチャットが見つかりました
      </span>
    </div>
    
    {filteredChats.map(chat => (
      <div key={chat.id} className="chat-item border rounded-lg p-4 mb-3 hover:bg-gray-50">
        <div className="flex justify-between items-start">
          <div className="flex-1">
            <h3 className="chat-title text-lg font-semibold">
              <a href={`/ai-chat/${chat.id}`} className="text-blue-600 hover:underline">
                {highlightSearchTerms(chat.title, searchQuery)}
              </a>
            </h3>
            <div className="chat-meta text-sm text-gray-600 mt-1">
              <span className="project-tag bg-blue-100 text-blue-800 px-2 py-1 rounded mr-2">
                📁 {chat.projectName}
              </span>
              <span className="user-info">👤 {chat.userName}</span>
              <span className="date-info ml-4">📅 {formatDate(chat.lastUpdated)}</span>
              <span className="message-count ml-4">💬 {chat.messageCount}</span>
            </div>
            {/* 検索結果のプレビュー */}
            {searchQuery && (
              <div className="search-preview mt-2 text-sm text-gray-700">
                {generateSearchPreview(chat, searchQuery)}
              </div>
            )}
          </div>
          <div className="chat-actions">
            <button className="text-gray-400 hover:text-gray-600"></button>
          </div>
        </div>
      </div>
    ))}
  </div>
</div>

5. API拡張仕様

# 検索・フィルタ対応API
GET /api/ai-chat/sessions?search={query}&user={userId}&project={projectId}&scope={scope}

# 例: nginx設定に関するチャットを検索
GET /api/ai-chat/sessions?search=nginx&project=1

# 例: 自分のチャットのみ表示
GET /api/ai-chat/sessions?scope=my&user=1

# 例: AIチャット一覧プロジェクトのチャット
GET /api/ai-chat/sessions?project=ai-chat-general

6. 検索アルゴリズム

検索対象の重み付け:

  • タイトル: 3.0
  • 最新メッセージ: 2.0
  • プロジェクト名: 1.5
  • チャット内容: 1.0
  • タグ: 2.5

検索機能:

  • 部分一致検索
  • AND/OR検索対応
  • タグベース検索
  • 日付範囲検索

7. 期待効果

  • 効率的なチャット発見: 過去の相談内容の素早い検索
  • プロジェクト管理: チャットのプロジェクト別整理
  • チーム協働: 他メンバーのチャット参照(権限に応じて)
  • 知識蓄積: AIとの対話履歴の有効活用

他の形式にエクスポート: Atom PDF