Skip to main content
Glama

Context Engineering MCP Platform

dashboard.html23.1 kB
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ワークフロー管理ダッシュボード</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/d3@7"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Arial, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; color: #333; } .container { max-width: 1400px; margin: 0 auto; padding: 20px; } .header { text-align: center; color: white; margin-bottom: 30px; } .header h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; } .stat-card { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); text-align: center; transition: transform 0.3s ease; } .stat-card:hover { transform: translateY(-5px); } .stat-value { font-size: 2rem; font-weight: bold; margin-bottom: 5px; } .stat-label { color: #666; font-size: 0.9rem; } .main-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; } .chart-container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } .chart-title { font-size: 1.2rem; font-weight: bold; margin-bottom: 15px; color: #333; } .workflow-section { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); margin-bottom: 20px; } .workflow-header { display: flex; justify-content: between; align-items: center; margin-bottom: 15px; } .workflow-title { font-size: 1.3rem; font-weight: bold; } .workflow-status { padding: 5px 15px; border-radius: 20px; font-size: 0.8rem; font-weight: bold; text-transform: uppercase; } .status-executing { background: #e3f2fd; color: #1976d2; } .status-completed { background: #e8f5e8; color: #2e7d32; } .status-planning { background: #fff3e0; color: #f57c00; } .status-failed { background: #ffebee; color: #d32f2f; } .progress-bar { width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden; margin: 10px 0; } .progress-fill { height: 100%; background: linear-gradient(90deg, #4caf50, #8bc34a); transition: width 0.3s ease; } .task-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; margin-top: 15px; } .task-card { padding: 15px; border-radius: 8px; border-left: 4px solid; font-size: 0.9rem; } .task-pending { background: #f5f5f5; border-left-color: #9e9e9e; } .task-in_progress { background: #e3f2fd; border-left-color: #2196f3; } .task-completed { background: #e8f5e8; border-left-color: #4caf50; } .task-failed { background: #ffebee; border-left-color: #f44336; } .task-blocked { background: #fff3e0; border-left-color: #ff9800; } .task-title { font-weight: bold; margin-bottom: 5px; } .task-meta { color: #666; font-size: 0.8rem; } .agent-section { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; } .agent-card { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } .agent-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .agent-name { font-size: 1.1rem; font-weight: bold; } .agent-type { padding: 3px 8px; background: #f0f0f0; border-radius: 15px; font-size: 0.7rem; text-transform: uppercase; } .load-indicator { width: 100%; height: 6px; background: #e0e0e0; border-radius: 3px; overflow: hidden; margin: 10px 0; } .load-fill { height: 100%; transition: width 0.3s ease; } .load-low { background: #4caf50; } .load-medium { background: #ff9800; } .load-high { background: #f44336; } .control-panel { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); margin-bottom: 20px; } .input-group { margin-bottom: 15px; } .input-group label { display: block; margin-bottom: 5px; font-weight: bold; } .input-group input, .input-group textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 1rem; } .btn { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 24px; border-radius: 25px; cursor: pointer; font-size: 1rem; font-weight: bold; transition: transform 0.2s ease; } .btn:hover { transform: translateY(-2px); } .btn:active { transform: translateY(0); } #networkViz { width: 100%; height: 400px; border: 1px solid #ddd; border-radius: 8px; } .loading { display: inline-block; width: 20px; height: 20px; border: 3px solid #f3f3f3; border-top: 3px solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .hidden { display: none; } </style> </head> <body> <div class="container"> <div class="header"> <h1>🤖 ワークフロー管理ダッシュボード</h1> <p>AIによる自動ワークフロー生成・タスク分解・エージェントアサイン</p> </div> <!-- 統計情報 --> <div class="stats-grid"> <div class="stat-card"> <div class="stat-value" id="totalWorkflows">0</div> <div class="stat-label">アクティブワークフロー</div> </div> <div class="stat-card"> <div class="stat-value" id="totalTasks">0</div> <div class="stat-label">総タスク数</div> </div> <div class="stat-card"> <div class="stat-value" id="completedTasks">0</div> <div class="stat-label">完了タスク</div> </div> <div class="stat-card"> <div class="stat-value" id="activeAgents">0</div> <div class="stat-label">アクティブエージェント</div> </div> </div> <!-- 制御パネル --> <div class="control-panel"> <h3>新しいワークフロー作成</h3> <div class="input-group"> <label for="workflowInput">要求内容:</label> <textarea id="workflowInput" rows="3" placeholder="例: ECサイトの商品管理システムを開発してください"></textarea> </div> <button class="btn" onclick="generateWorkflow()"> <span id="generateBtn">ワークフロー生成</span> <span id="generateLoading" class="loading hidden"></span> </button> </div> <!-- メインチャート --> <div class="main-grid"> <div class="chart-container"> <div class="chart-title">タスク状態分布</div> <canvas id="taskStatusChart"></canvas> </div> <div class="chart-container"> <div class="chart-title">エージェント負荷</div> <canvas id="agentLoadChart"></canvas> </div> </div> <!-- ネットワーク可視化 --> <div class="chart-container"> <div class="chart-title">タスク依存関係ネットワーク</div> <div id="networkViz"></div> </div> <!-- ワークフロー一覧 --> <div id="workflowList"></div> <!-- エージェント一覧 --> <div class="workflow-section"> <h3>エージェント状態</h3> <div id="agentList" class="agent-section"></div> </div> </div> <script> // グローバル変数 let workflows = []; let agents = []; let taskStatusChart, agentLoadChart; // 初期化 document.addEventListener('DOMContentLoaded', function() { initCharts(); loadSampleData(); setInterval(updateDashboard, 5000); // 5秒ごとに更新 }); // チャート初期化 function initCharts() { // タスク状態チャート const taskCtx = document.getElementById('taskStatusChart').getContext('2d'); taskStatusChart = new Chart(taskCtx, { type: 'doughnut', data: { labels: ['待機中', '実行中', '完了', '失敗', 'ブロック'], datasets: [{ data: [0, 0, 0, 0, 0], backgroundColor: ['#9e9e9e', '#2196f3', '#4caf50', '#f44336', '#ff9800'] }] }, options: { responsive: true, plugins: { legend: { position: 'bottom' } } } }); // エージェント負荷チャート const agentCtx = document.getElementById('agentLoadChart').getContext('2d'); agentLoadChart = new Chart(agentCtx, { type: 'bar', data: { labels: [], datasets: [{ label: '負荷率 (%)', data: [], backgroundColor: 'rgba(54, 162, 235, 0.6)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 }] }, options: { responsive: true, scales: { y: { beginAtZero: true, max: 100 } } } }); } // サンプルデータ読み込み function loadSampleData() { // サンプルワークフロー workflows = [ { id: '1', title: 'ECサイト開発プロジェクト', status: 'executing', progress: 65, tasks: [ { id: '1', title: '要件定義', status: 'completed', agent: '分析エージェント' }, { id: '2', title: 'UI設計', status: 'in_progress', agent: '開発エージェント' }, { id: '3', title: 'バックエンド開発', status: 'pending', agent: null }, { id: '4', title: 'テスト', status: 'blocked', agent: null } ] } ]; // サンプルエージェント agents = [ { id: '1', name: '分析エージェント', type: 'analyst', load: 33, maxTasks: 3, currentTasks: 1 }, { id: '2', name: '開発エージェント', type: 'developer', load: 67, maxTasks: 3, currentTasks: 2 }, { id: '3', name: '研究エージェント', type: 'researcher', load: 0, maxTasks: 2, currentTasks: 0 } ]; updateDashboard(); } // ダッシュボード更新 function updateDashboard() { updateStatistics(); updateCharts(); updateWorkflowList(); updateAgentList(); updateNetworkVisualization(); } // 統計情報更新 function updateStatistics() { document.getElementById('totalWorkflows').textContent = workflows.length; const totalTasks = workflows.reduce((sum, wf) => sum + wf.tasks.length, 0); document.getElementById('totalTasks').textContent = totalTasks; const completedTasks = workflows.reduce((sum, wf) => sum + wf.tasks.filter(t => t.status === 'completed').length, 0); document.getElementById('completedTasks').textContent = completedTasks; const activeAgents = agents.filter(a => a.currentTasks > 0).length; document.getElementById('activeAgents').textContent = activeAgents; } // チャート更新 function updateCharts() { // タスク状態分布 const allTasks = workflows.flatMap(wf => wf.tasks); const statusCounts = { pending: allTasks.filter(t => t.status === 'pending').length, in_progress: allTasks.filter(t => t.status === 'in_progress').length, completed: allTasks.filter(t => t.status === 'completed').length, failed: allTasks.filter(t => t.status === 'failed').length, blocked: allTasks.filter(t => t.status === 'blocked').length }; taskStatusChart.data.datasets[0].data = [ statusCounts.pending, statusCounts.in_progress, statusCounts.completed, statusCounts.failed, statusCounts.blocked ]; taskStatusChart.update(); // エージェント負荷 agentLoadChart.data.labels = agents.map(a => a.name); agentLoadChart.data.datasets[0].data = agents.map(a => a.load); agentLoadChart.update(); } // ワークフロー一覧更新 function updateWorkflowList() { const container = document.getElementById('workflowList'); container.innerHTML = ''; workflows.forEach(workflow => { const workflowDiv = document.createElement('div'); workflowDiv.className = 'workflow-section'; workflowDiv.innerHTML = ` <div class="workflow-header"> <div class="workflow-title">${workflow.title}</div> <div class="workflow-status status-${workflow.status}">${workflow.status}</div> </div> <div class="progress-bar"> <div class="progress-fill" style="width: ${workflow.progress}%"></div> </div> <div class="task-grid"> ${workflow.tasks.map(task => ` <div class="task-card task-${task.status}"> <div class="task-title">${task.title}</div> <div class="task-meta"> 状態: ${task.status}<br> ${task.agent ? `担当: ${task.agent}` : '未アサイン'} </div> </div> `).join('')} </div> `; container.appendChild(workflowDiv); }); } // エージェント一覧更新 function updateAgentList() { const container = document.getElementById('agentList'); container.innerHTML = ''; agents.forEach(agent => { const agentDiv = document.createElement('div'); agentDiv.className = 'agent-card'; let loadClass = 'load-low'; if (agent.load > 70) loadClass = 'load-high'; else if (agent.load > 40) loadClass = 'load-medium'; agentDiv.innerHTML = ` <div class="agent-header"> <div class="agent-name">${agent.name}</div> <div class="agent-type">${agent.type}</div> </div> <div class="load-indicator"> <div class="load-fill ${loadClass}" style="width: ${agent.load}%"></div> </div> <div class="task-meta"> 負荷: ${agent.load}% (${agent.currentTasks}/${agent.maxTasks}) </div> `; container.appendChild(agentDiv); }); } // ネットワーク可視化更新 function updateNetworkVisualization() { const container = d3.select('#networkViz'); container.selectAll('*').remove(); if (workflows.length === 0) return; const workflow = workflows[0]; // 最初のワークフローを可視化 const tasks = workflow.tasks; const width = 800; const height = 400; const svg = container.append('svg') .attr('width', width) .attr('height', height); // ノードとリンクのデータ作成 const nodes = tasks.map(task => ({ id: task.id, title: task.title, status: task.status, x: Math.random() * width, y: Math.random() * height })); // シンプルな配置(実際の依存関係に基づく) const simulation = d3.forceSimulation(nodes) .force('charge', d3.forceManyBody().strength(-300)) .force('center', d3.forceCenter(width / 2, height / 2)) .force('collision', d3.forceCollide().radius(30)); // ノード描画 const node = svg.selectAll('.node') .data(nodes) .enter().append('g') .attr('class', 'node'); node.append('circle') .attr('r', 20) .attr('fill', d => { const colors = { pending: '#9e9e9e', in_progress: '#2196f3', completed: '#4caf50', failed: '#f44336', blocked: '#ff9800' }; return colors[d.status] || '#9e9e9e'; }); node.append('text') .attr('text-anchor', 'middle') .attr('dy', '.35em') .attr('font-size', '10px') .attr('fill', 'white') .text(d => d.id); // シミュレーション更新 simulation.on('tick', () => { node.attr('transform', d => `translate(${d.x},${d.y})`); }); } // ワークフロー生成 async function generateWorkflow() { const input = document.getElementById('workflowInput'); const btnText = document.getElementById('generateBtn'); const btnLoading = document.getElementById('generateLoading'); if (!input.value.trim()) { alert('要求内容を入力してください'); return; } // ローディング表示 btnText.classList.add('hidden'); btnLoading.classList.remove('hidden'); try { // サンプル応答(実際はAPIを呼び出し) await new Promise(resolve => setTimeout(resolve, 2000)); const newWorkflow = { id: Date.now().toString(), title: `生成されたワークフロー: ${input.value.substring(0, 20)}...`, status: 'planning', progress: 0, tasks: [ { id: `${Date.now()}-1`, title: '要件分析', status: 'pending', agent: null }, { id: `${Date.now()}-2`, title: '設計', status: 'pending', agent: null }, { id: `${Date.now()}-3`, title: '実装', status: 'pending', agent: null }, { id: `${Date.now()}-4`, title: 'テスト', status: 'pending', agent: null } ] }; workflows.push(newWorkflow); input.value = ''; updateDashboard(); alert('ワークフローが生成されました!'); } catch (error) { alert('ワークフロー生成に失敗しました: ' + error.message); } finally { // ローディング非表示 btnText.classList.remove('hidden'); btnLoading.classList.add('hidden'); } } </script> </body> </html>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ShunsukeHayashi/context_engineering_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server