<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Agent Orchestrator — Admin</title>
<link rel="stylesheet" href="../../colors_and_type.css">
<style>
  * { box-sizing: border-box; }
  html, body { height: 100%; margin: 0; }
  body {
    font-family: var(--font-sans);
    background: var(--bg-app, #f8fafc);
    color: var(--fg1, #0f172a);
    font-size: 13px;
    -webkit-font-smoothing: antialiased;
  }
  @keyframes ao-spin { from { transform: rotate(0) } to { transform: rotate(360deg) } }
  @keyframes ao-pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.35 } }
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-thumb { background: #cbd5e1; border: 2px solid #f8fafc; border-radius: 10px; }
  ::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
</head>
<body>
<div id="root"></div>

<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>

<script type="text/babel" src="./Primitives.jsx"></script>
<script type="text/babel" src="./TopBar.jsx"></script>
<script type="text/babel" src="./TaskList.jsx"></script>
<script type="text/babel" src="./ChatPane.jsx"></script>
<script type="text/babel" src="./DetailPanel.jsx"></script>

<script type="text/babel">
const MIN = 60 * 1000;
const H = 60 * MIN;
const now = Date.now();

const SAMPLE_TASKS = [
  {
    id: 412, title: 'Xの朝のAIダイジェスト生成',
    body: '毎朝 7:00 JST にフォロー中のAI関連アカウントの過去24hをサマリし、DMで送信する。Twitter CLIを使用し、1スレッドにまとめること。',
    status: 'running', piece: 'x-ai-digest', worker: 'worker-03', attempts: 1,
    assignee: '@daichi', repo: 'gitea:daichi/agent-orchestrator', branch: 'task/412-morning-digest',
    createdAt: now - 2*H, updatedAt: now - 4*MIN,
    events: [
      { kind: 'info', label: '/brainstorm 完了', meta: '12個のアイデアを生成', time: '10:42' },
      { kind: 'info', label: '/plan 完了', meta: '12ステップ · 推定 4分', time: '10:43' },
      { kind: 'info', label: '/implement 実行中', meta: 'ステップ 8 / 12', time: '10:45' },
    ],
  },
  {
    id: 411, title: 'Brave Search の CAPTCHA 回避',
    body: 'noVNC経由でBraveに繰り返しCAPTCHAが発生。ユーザーの介入が必要。',
    status: 'waiting_human', piece: 'general', worker: 'worker-01', attempts: 2,
    assignee: '@daichi', repo: 'gitea:daichi/agent-orchestrator', branch: 'task/411-brave-captcha',
    createdAt: now - 3*H, updatedAt: now - 18*MIN,
    events: [
      { kind: 'info', label: '/brainstorm 完了', meta: '', time: '08:10' },
      { kind: 'error', label: 'ASK が発行されました', meta: 'CAPTCHAの解決を依頼', time: '08:22' },
    ],
  },
  {
    id: 410, title: 'GitHub Issue #284 の対応',
    body: 'scheduler.ts のタイムアウト処理リファクタ。worker-manager.test.ts を更新。',
    status: 'succeeded', piece: 'general', worker: 'worker-02', attempts: 1,
    assignee: '@daichi', repo: 'gitea:daichi/agent-orchestrator', branch: 'task/410-sched-timeout',
    createdAt: now - 8*H, updatedAt: now - 2*H,
    events: [
      { kind: 'info', label: '/plan 完了', meta: '7ステップ', time: '02:11' },
      { kind: 'ok', label: 'PR 作成', meta: '#284 テスト通過', time: '04:08' },
    ],
  },
  {
    id: 409, title: 'ブレスト: 社内AIエージェント活用事例',
    body: '営業部向け、週次の活用アイデアを10件ブレストし、優先順位をつけて提出。',
    status: 'queued', piece: 'brainstorming', worker: null, attempts: 0,
    assignee: '@tomoko', repo: 'gitea:corp/ops', branch: '—',
    createdAt: now - 30*MIN, updatedAt: now - 25*MIN,
    events: [],
  },
  {
    id: 408, title: '四半期データの集計とグラフ化',
    body: 'Q3のSNSエンゲージメントを集計し、CSVとPNGで出力。',
    status: 'waiting_subtasks', piece: 'data-process', worker: 'worker-05', attempts: 1,
    assignee: '@kenta', repo: 'gitea:corp/analytics', branch: 'task/408-q3-roundup',
    createdAt: now - 5*H, updatedAt: now - 45*MIN,
    events: [
      { kind: 'info', label: 'サブタスク3件を発行', meta: '#408-1, #408-2, #408-3', time: '09:30' },
    ],
  },
  {
    id: 407, title: '競合サービスのリサーチ',
    body: 'エージェント型ワーカー系SaaSを3社分析し、比較表を作成する。',
    status: 'failed', piece: 'research', worker: 'worker-04', attempts: 3,
    assignee: '@tomoko', repo: 'gitea:corp/research', branch: 'task/407-competitors',
    createdAt: now - 26*H, updatedAt: now - 10*H,
    events: [
      { kind: 'error', label: 'タイムアウト', meta: '3回連続で失敗', time: 'yesterday' },
    ],
  },
  {
    id: 406, title: 'ゲーム実況の告知ツイート生成',
    body: '今夜のストリーム用の告知ツイートを3案作成。ハッシュタグ付き。',
    status: 'retry', piece: 'game-tweet-generator', worker: 'worker-06', attempts: 2,
    assignee: '@daichi', repo: 'gitea:daichi/stream', branch: 'task/406-tweet-gen',
    createdAt: now - 40*MIN, updatedAt: now - 8*MIN,
    events: [
      { kind: 'error', label: 'レート制限', meta: '60秒後に再試行', time: '10:35' },
    ],
  },
  {
    id: 405, title: '経費申請書のOCRと仕分け',
    body: '添付PDFをOCRし、勘定科目ごとに仕分け。',
    status: 'cancelled', piece: 'office-process', worker: null, attempts: 1,
    assignee: '@kenta', repo: 'gitea:corp/ops', branch: '—',
    createdAt: now - 48*H, updatedAt: now - 20*H,
    events: [],
  },
];

const INITIAL_MESSAGES = {
  412: [
    { role: 'user', content: '毎朝7:00にAI関連のXアカウントの過去24hをまとめて、DMで送ってほしい。1スレッドで。', footer: '10:41 · @daichi' },
    { role: 'assistant', content: '了解。x-ai-digest ピースを使用します。対象アカウント、まとめる観点、文字数制限を確認させてください。' },
    { role: 'ask', content: '❓ 以下を確認させてください:\n\n1. 対象アカウントリストはこのリポジトリの accounts.txt で良いですか?\n2. 1ツイートあたりの上限文字数は280でOK?\n3. 日本語メインの要約で良いですか?' },
    { role: 'user', content: '1. OK\n2. OK\n3. 日本語で。でも原文が英語なら簡潔な英語の引用も残して。' },
    { role: 'progress', content: '/implement — ステップ 8 / 12 (Twitter CLIでタイムライン取得中)' },
  ],
  411: [
    { role: 'user', content: 'Brave Searchで検索結果が取れない。何度もCAPTCHAが出てるっぽい。' },
    { role: 'progress', content: 'noVNCセッションを開いて確認中...' },
    { role: 'ask', content: '❓ CAPTCHAの解決が必要です。noVNCで手動で解決していただけますか?\n\nsession: https://novnc.internal/412\n\n解決後 `/resume 411` と返信してください。' },
  ],
  410: [
    { role: 'user', content: 'Issue #284 の対応お願い。scheduler.tsのタイムアウト処理が不安定。' },
    { role: 'assistant', content: '了解。/brainstorm から始めます。' },
    { role: 'result', content: '✅ 完了しました。\n\n- PR: gitea:daichi/agent-orchestrator#291\n- 変更: scheduler.ts, worker-manager.test.ts, worker.test.ts\n- テスト: 42 passed\n\nレビューお願いします。' },
  ],
  409: [
    { role: 'user', content: '営業部向けに今週のエージェント活用ネタを10個ブレストしてほしい。' },
    { role: 'assistant', content: '了解。キューに入りました。ワーカーの空きが出次第処理します。' },
  ],
  408: [
    { role: 'user', content: 'Q3のSNSエンゲージメントまとめて、折れ線グラフと棒グラフのPNGで。' },
    { role: 'progress', content: 'サブタスク3件の完了を待っています (#408-1, #408-2, #408-3)' },
  ],
  407: [
    { role: 'user', content: '競合3社のリサーチと比較表を作成。' },
    { role: 'assistant', content: '3回試行しましたが、外部サイトの読み込みタイムアウトが続いています。' },
  ],
  406: [
    { role: 'user', content: '今夜のストリーム告知を3案、ハッシュタグ付きで。' },
    { role: 'progress', content: 'レート制限中 — 60秒後に再試行します' },
  ],
  405: [
    { role: 'user', content: '経費PDFのOCRと仕分け。' },
    { role: 'assistant', content: 'キャンセルされました。' },
  ],
};

function App() {
  const [tasks, setTasks] = React.useState(SAMPLE_TASKS);
  const [activeId, setActiveId] = React.useState(412);
  const [detailOpen, setDetailOpen] = React.useState(true);
  const [messages, setMessages] = React.useState(INITIAL_MESSAGES);
  const [filters, setFilters] = React.useState({ status: 'all', search: '', sort: 'updated' });
  const [page, setPage] = React.useState('tasks');

  const active = tasks.find(t => t.id === activeId) || tasks[0];

  const counts = {
    total: tasks.length,
    running: tasks.filter(t => t.status === 'running').length,
    waiting: tasks.filter(t => t.status === 'waiting_human' || t.status === 'waiting_subtasks').length,
    failed: tasks.filter(t => t.status === 'failed').length,
  };

  const onSend = (text) => {
    setMessages(m => ({
      ...m,
      [activeId]: [...(m[activeId] || []), { role: 'user', content: text, footer: 'たった今 · @daichi' }],
    }));
    // fake echo after small delay
    setTimeout(() => {
      setMessages(m => ({
        ...m,
        [activeId]: [...(m[activeId] || []), { role: 'progress', content: 'エージェントが応答を生成中...' }],
      }));
    }, 400);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
      <TopBar
        page={page} onNavigate={setPage}
        counts={counts}
        onOpenCreate={() => alert('新しい依頼 (モック)')}
        user={{ name: 'Daichi' }}
      />
      <div style={{
        flex: 1, minHeight: 0, display: 'grid',
        gridTemplateColumns: detailOpen ? '320px 1fr 380px' : '320px 1fr',
        background: '#f1f5f9', gap: 1,
      }}>
        <div style={{ background: '#fff', padding: 12, minWidth: 0, display: 'flex', flexDirection: 'column' }}>
          <TaskList
            tasks={tasks} activeId={activeId} onSelect={setActiveId}
            filters={filters} setFilters={setFilters}
          />
        </div>
        <ChatPane
          task={active}
          messages={messages[active.id] || []}
          onSend={onSend}
          onOpenDetail={() => setDetailOpen(v => !v)}
          detailOpen={detailOpen}
        />
        {detailOpen && <DetailPanel task={active} onClose={() => setDetailOpen(false)} />}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
