index.html 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>News MCP Dashboard</title>
  7. <link rel="stylesheet" href="/dashboard/style.css">
  8. <script src="https://cdn.jsdelivr.net/npm/htmx.org@1.9.12"></script>
  9. <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
  10. </head>
  11. <body>
  12. <!-- TOP NAV -->
  13. <nav class="topnav">
  14. <div class="nav-brand">📡 news-mcp dashboard</div>
  15. <div class="nav-links">
  16. <a href="#" onclick="switchView('health'); return false;" class="active" data-view="health">Health</a>
  17. <a href="#" onclick="switchView('feeds'); return false;" data-view="feeds">Feeds</a>
  18. <a href="#" onclick="switchView('clusters'); return false;" data-view="clusters">Clusters</a>
  19. <a href="#" onclick="switchView('sentiment'); return false;" data-view="sentiment">Sentiment</a>
  20. <a href="#" onclick="switchView('entities'); return false;" data-view="entities">Entities</a>
  21. <a href="#" onclick="switchView('keywords'); return false;" data-view="keywords">Keywords</a>
  22. <a href="#" onclick="switchView('detail'); return false;" data-view="detail">Detail</a>
  23. </div>
  24. <div class="nav-meta" id="nav-meta"></div>
  25. </nav>
  26. <!-- HEALTH VIEW -->
  27. <div id="view-health" class="view active">
  28. <div class="card">
  29. <h3>📊 System Status</h3>
  30. <div id="health-stats" class="stat-grid">
  31. <div class="stat-box"><div class="label">Total Clusters</div><div class="value blue" id="stat-clusters">—</div></div>
  32. <div class="stat-box"><div class="label">Total Entities</div><div class="value blue" id="stat-entities">—</div></div>
  33. <div class="stat-box"><div class="label">Extracted Entities</div><div class="value blue" id="stat-cluster-entities">—</div></div>
  34. <div class="stat-box"><div class="label">Data Fresh</div><div class="value" id="stat-fresh">—</div></div>
  35. <div class="stat-box"><div class="label">Last Refresh</div><div class="value" style="font-size:1rem" id="stat-refresh">—</div></div>
  36. </div>
  37. </div>
  38. <div class="grid grid-3" style="margin-top:1rem">
  39. <div class="card">
  40. <h3>📋 Topics Distribution</h3>
  41. <div class="chart-wrap"><canvas id="chart-topic-dist"></canvas></div>
  42. </div>
  43. <div class="card">
  44. <h3>📈 Sentiment Over Time (4h buckets)</h3>
  45. <div class="chart-wrap"><canvas id="chart-sentiment-overview"></canvas></div>
  46. </div>
  47. <div class="card">
  48. <h3>🔗 Feed Activity</h3>
  49. <div id="feed-status"><div class="loading">Loading…</div></div>
  50. </div>
  51. </div>
  52. </div>
  53. <!-- FEEDS VIEW -->
  54. <div id="view-feeds" class="view">
  55. <div class="card">
  56. <h3>📡 RSS Feed Management</h3>
  57. <p class="muted" style="margin-bottom:.75rem">Toggle feeds on/off. Changes take effect on the next refresh cycle.</p>
  58. <div id="feeds-list"><div class="loading">Loading…</div></div>
  59. </div>
  60. </div>
  61. <!-- CLUSTERS VIEW -->
  62. <div id="view-clusters" class="view">
  63. <div class="card">
  64. <div class="toolbar">
  65. <div class="filters">
  66. <select id="cluster-topic" onchange="reloadClusters()">
  67. <option value="all">All Topics</option>
  68. <option value="crypto">Crypto</option>
  69. <option value="macro">Macro</option>
  70. <option value="regulation">Regulation</option>
  71. <option value="ai">AI</option>
  72. <option value="other">Other</option>
  73. </select>
  74. <select id="cluster-hours" onchange="reloadClusters()">
  75. <option value="1">Last 1h</option>
  76. <option value="6">Last 6h</option>
  77. <option value="24">Last 24h</option>
  78. <option value="72">Last 3 days</option>
  79. <option value="168" selected>Last week</option>
  80. </select>
  81. <input type="text" id="cluster-search" placeholder="Search headlines…" onkeyup="filterClusters()" />
  82. <span class="badge" id="cluster-total">—</span>
  83. </div>
  84. </div>
  85. <div id="cluster-table"></div>
  86. </div>
  87. </div>
  88. <!-- SENTIMENT VIEW -->
  89. <div id="view-sentiment" class="view">
  90. <div class="card">
  91. <div class="toolbar">
  92. <div class="filters">
  93. <select id="sentiment-topic" onchange="reloadSentiment()">
  94. <option value="all">All Topics</option>
  95. <option value="crypto">Crypto</option>
  96. <option value="macro">Macro</option>
  97. <option value="regulation">Regulation</option>
  98. <option value="ai">AI</option>
  99. </select>
  100. <select id="sentiment-hours" onchange="reloadSentiment()">
  101. <option value="6">Last 6h</option>
  102. <option value="24">Last 24h</option>
  103. <option value="72">Last 3 days</option>
  104. <option value="168" selected>Last week</option>
  105. </select>
  106. <select id="sentiment-bucket" onchange="reloadSentiment()">
  107. <option value="1">1h buckets</option>
  108. <option value="4" selected>4h buckets</option>
  109. <option value="12">12h buckets</option>
  110. </select>
  111. </div>
  112. </div>
  113. <div class="chart-wrap"><canvas id="chart-sentiment"></canvas></div>
  114. <div id="sentiment-stats" class="sentiment-stats"></div>
  115. </div>
  116. </div>
  117. <!-- ENTITIES VIEW -->
  118. <div id="view-entities" class="view">
  119. <div class="grid grid-3">
  120. <div class="card">
  121. <h3>🔗 Top Entities <small class="muted">(24h mentions)</small></h3>
  122. <div id="entity-list"><div class="loading">Loading…</div></div>
  123. </div>
  124. <div class="card">
  125. <h3>📊 Entity Frequency</h3>
  126. <div class="chart-wrap chart-wrap-tall"><canvas id="chart-entities"></canvas></div>
  127. </div>
  128. <div class="card">
  129. <h3>ℹ️ Entity Detail</h3>
  130. <div id="entity-detail"><p class="muted">Click an entity in the list to see details.</p></div>
  131. </div>
  132. </div>
  133. </div>
  134. <!-- KEYWORDS VIEW -->
  135. <div id="view-keywords" class="view">
  136. <div class="grid grid-3">
  137. <div class="card">
  138. <h3>🏷️ Top Keywords <small class="muted">(24h occurrences, excluding entities)</small></h3>
  139. <div id="keyword-list"><div class="loading">Loading…</div></div>
  140. </div>
  141. <div class="card">
  142. <h3>📊 Keyword Frequency</h3>
  143. <div class="chart-wrap chart-wrap-tall"><canvas id="chart-keywords"></canvas></div>
  144. </div>
  145. <div class="card">
  146. <h3>ℹ️ Keyword Detail</h3>
  147. <div id="keyword-detail"><p class="muted">Click a keyword in the list to see matching clusters.</p></div>
  148. </div>
  149. </div>
  150. </div>
  151. <!-- DETAIL VIEW -->
  152. <div id="view-detail" class="view">
  153. <div class="card">
  154. <div class="toolbar">
  155. <input type="text" id="detail-search" placeholder="Paste cluster_id or search…" />
  156. <button onclick="searchDetail()">🔍 Search</button>
  157. </div>
  158. <div id="detail-content"><p class="muted">Select a cluster from <a href="#" onclick="switchView('clusters'); return false;">Clusters</a> or search by cluster ID.</p></div>
  159. </div>
  160. </div>
  161. <!-- DETAIL MODAL (cluster drill-down) -->
  162. <div id="cluster-modal" class="modal-overlay" onclick="closeModal()">
  163. <div class="modal" onclick="event.stopPropagation()">
  164. <button class="modal-close" onclick="closeModal()">✕</button>
  165. <div id="modal-content"><div class="loading">Loading…</div></div>
  166. </div>
  167. </div>
  168. <div id="toast" class="toast"></div>
  169. <script src="/dashboard/dashboard.js"></script>
  170. </body>
  171. </html>