reviewflow 3.19.2 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/config/projectConfig.d.ts +7 -0
  3. package/dist/config/projectConfig.d.ts.map +1 -1
  4. package/dist/config/projectConfig.js +18 -0
  5. package/dist/config/projectConfig.js.map +1 -1
  6. package/dist/dashboard/index.html +452 -142
  7. package/dist/dashboard/modules/cardCounters.d.ts +32 -0
  8. package/dist/dashboard/modules/cardCounters.d.ts.map +1 -0
  9. package/dist/dashboard/modules/cardCounters.js +40 -0
  10. package/dist/dashboard/modules/cardCounters.js.map +1 -0
  11. package/dist/dashboard/modules/constants.d.ts +1 -0
  12. package/dist/dashboard/modules/constants.d.ts.map +1 -1
  13. package/dist/dashboard/modules/constants.js +1 -0
  14. package/dist/dashboard/modules/constants.js.map +1 -1
  15. package/dist/dashboard/modules/managePanel.d.ts +49 -0
  16. package/dist/dashboard/modules/managePanel.d.ts.map +1 -0
  17. package/dist/dashboard/modules/managePanel.js +123 -0
  18. package/dist/dashboard/modules/managePanel.js.map +1 -0
  19. package/dist/dashboard/modules/overview.d.ts +65 -0
  20. package/dist/dashboard/modules/overview.d.ts.map +1 -0
  21. package/dist/dashboard/modules/overview.js +260 -0
  22. package/dist/dashboard/modules/overview.js.map +1 -0
  23. package/dist/dashboard/modules/settingsModal.d.ts +77 -0
  24. package/dist/dashboard/modules/settingsModal.d.ts.map +1 -0
  25. package/dist/dashboard/modules/settingsModal.js +182 -0
  26. package/dist/dashboard/modules/settingsModal.js.map +1 -0
  27. package/dist/dashboard/modules/tabBar.d.ts +60 -0
  28. package/dist/dashboard/modules/tabBar.d.ts.map +1 -0
  29. package/dist/dashboard/modules/tabBar.js +103 -0
  30. package/dist/dashboard/modules/tabBar.js.map +1 -0
  31. package/dist/dashboard/styles.css +936 -0
  32. package/dist/frameworks/config/configLoader.d.ts +8 -0
  33. package/dist/frameworks/config/configLoader.d.ts.map +1 -1
  34. package/dist/frameworks/config/configLoader.js +18 -0
  35. package/dist/frameworks/config/configLoader.js.map +1 -1
  36. package/dist/main/routes.d.ts.map +1 -1
  37. package/dist/main/routes.js +67 -11
  38. package/dist/main/routes.js.map +1 -1
  39. package/dist/modules/cli-configuration/entities/projectConfig/projectConfig.gateway.d.ts +20 -0
  40. package/dist/modules/cli-configuration/entities/projectConfig/projectConfig.gateway.d.ts.map +1 -0
  41. package/dist/modules/cli-configuration/entities/projectConfig/projectConfig.gateway.js +2 -0
  42. package/dist/modules/cli-configuration/entities/projectConfig/projectConfig.gateway.js.map +1 -0
  43. package/dist/modules/cli-configuration/entities/repositoryEntry/repositoryEntry.d.ts +13 -0
  44. package/dist/modules/cli-configuration/entities/repositoryEntry/repositoryEntry.d.ts.map +1 -0
  45. package/dist/modules/cli-configuration/entities/repositoryEntry/repositoryEntry.js +2 -0
  46. package/dist/modules/cli-configuration/entities/repositoryEntry/repositoryEntry.js.map +1 -0
  47. package/dist/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.d.ts +6 -1
  48. package/dist/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.d.ts.map +1 -1
  49. package/dist/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.js +116 -13
  50. package/dist/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.js.map +1 -1
  51. package/dist/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.d.ts +43 -0
  52. package/dist/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.d.ts.map +1 -0
  53. package/dist/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.js +82 -0
  54. package/dist/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.js.map +1 -0
  55. package/dist/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.d.ts +7 -0
  56. package/dist/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.d.ts.map +1 -0
  57. package/dist/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.js +48 -0
  58. package/dist/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.js.map +1 -0
  59. package/dist/modules/cli-configuration/usecases/cli/addRepositoriesToConfig.usecase.d.ts +1 -6
  60. package/dist/modules/cli-configuration/usecases/cli/addRepositoriesToConfig.usecase.d.ts.map +1 -1
  61. package/dist/modules/cli-configuration/usecases/cli/addRepositoriesToConfig.usecase.js.map +1 -1
  62. package/dist/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.d.ts +21 -0
  63. package/dist/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.d.ts.map +1 -0
  64. package/dist/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.js +27 -0
  65. package/dist/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.js.map +1 -0
  66. package/dist/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.d.ts +22 -0
  67. package/dist/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.d.ts.map +1 -0
  68. package/dist/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.js +27 -0
  69. package/dist/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.js.map +1 -0
  70. package/dist/modules/cli-configuration/usecases/cli/writeInitConfig.usecase.d.ts +1 -6
  71. package/dist/modules/cli-configuration/usecases/cli/writeInitConfig.usecase.d.ts.map +1 -1
  72. package/dist/modules/cli-configuration/usecases/cli/writeInitConfig.usecase.js.map +1 -1
  73. package/dist/modules/cli-configuration/usecases/dashboardRepositories/addRepositoryFromDashboard.usecase.d.ts +19 -0
  74. package/dist/modules/cli-configuration/usecases/dashboardRepositories/addRepositoryFromDashboard.usecase.d.ts.map +1 -0
  75. package/dist/modules/cli-configuration/usecases/dashboardRepositories/addRepositoryFromDashboard.usecase.js +30 -0
  76. package/dist/modules/cli-configuration/usecases/dashboardRepositories/addRepositoryFromDashboard.usecase.js.map +1 -0
  77. package/dist/modules/cli-configuration/usecases/dashboardRepositories/removeRepositoryFromDashboard.usecase.d.ts +16 -0
  78. package/dist/modules/cli-configuration/usecases/dashboardRepositories/removeRepositoryFromDashboard.usecase.d.ts.map +1 -0
  79. package/dist/modules/cli-configuration/usecases/dashboardRepositories/removeRepositoryFromDashboard.usecase.js +27 -0
  80. package/dist/modules/cli-configuration/usecases/dashboardRepositories/removeRepositoryFromDashboard.usecase.js.map +1 -0
  81. package/dist/modules/cli-configuration/usecases/dashboardRepositories/updateRepositoryEnabledFromDashboard.usecase.d.ts +17 -0
  82. package/dist/modules/cli-configuration/usecases/dashboardRepositories/updateRepositoryEnabledFromDashboard.usecase.d.ts.map +1 -0
  83. package/dist/modules/cli-configuration/usecases/dashboardRepositories/updateRepositoryEnabledFromDashboard.usecase.js +28 -0
  84. package/dist/modules/cli-configuration/usecases/dashboardRepositories/updateRepositoryEnabledFromDashboard.usecase.js.map +1 -0
  85. package/dist/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.d.ts +31 -0
  86. package/dist/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.d.ts.map +1 -0
  87. package/dist/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.js +102 -0
  88. package/dist/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.js.map +1 -0
  89. package/dist/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.d.ts +16 -0
  90. package/dist/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.d.ts.map +1 -0
  91. package/dist/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.js +53 -0
  92. package/dist/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.js.map +1 -0
  93. package/dist/modules/statistics-insights/interface-adapters/presenters/overview.presenter.d.ts +93 -0
  94. package/dist/modules/statistics-insights/interface-adapters/presenters/overview.presenter.d.ts.map +1 -0
  95. package/dist/modules/statistics-insights/interface-adapters/presenters/overview.presenter.js +145 -0
  96. package/dist/modules/statistics-insights/interface-adapters/presenters/overview.presenter.js.map +1 -0
  97. package/dist/tests/acceptance/177-dashboard-add-project-ui.acceptance.test.d.ts +12 -0
  98. package/dist/tests/acceptance/177-dashboard-add-project-ui.acceptance.test.d.ts.map +1 -0
  99. package/dist/tests/acceptance/177-dashboard-add-project-ui.acceptance.test.js +304 -0
  100. package/dist/tests/acceptance/177-dashboard-add-project-ui.acceptance.test.js.map +1 -0
  101. package/dist/tests/acceptance/178-dashboard-tabs-reposition.acceptance.test.d.ts +12 -0
  102. package/dist/tests/acceptance/178-dashboard-tabs-reposition.acceptance.test.d.ts.map +1 -0
  103. package/dist/tests/acceptance/178-dashboard-tabs-reposition.acceptance.test.js +131 -0
  104. package/dist/tests/acceptance/178-dashboard-tabs-reposition.acceptance.test.js.map +1 -0
  105. package/dist/tests/acceptance/179-dashboard-project-settings-modal.acceptance.test.d.ts +12 -0
  106. package/dist/tests/acceptance/179-dashboard-project-settings-modal.acceptance.test.d.ts.map +1 -0
  107. package/dist/tests/acceptance/179-dashboard-project-settings-modal.acceptance.test.js +312 -0
  108. package/dist/tests/acceptance/179-dashboard-project-settings-modal.acceptance.test.js.map +1 -0
  109. package/dist/tests/acceptance/91-dashboard-multi-project-overview.acceptance.test.d.ts +10 -0
  110. package/dist/tests/acceptance/91-dashboard-multi-project-overview.acceptance.test.d.ts.map +1 -0
  111. package/dist/tests/acceptance/91-dashboard-multi-project-overview.acceptance.test.js +275 -0
  112. package/dist/tests/acceptance/91-dashboard-multi-project-overview.acceptance.test.js.map +1 -0
  113. package/dist/tests/factories/projectStatsApiResponse.factory.d.ts +16 -0
  114. package/dist/tests/factories/projectStatsApiResponse.factory.d.ts.map +1 -0
  115. package/dist/tests/factories/projectStatsApiResponse.factory.js +39 -0
  116. package/dist/tests/factories/projectStatsApiResponse.factory.js.map +1 -0
  117. package/dist/tests/factories/recentReviewFile.factory.d.ts +5 -0
  118. package/dist/tests/factories/recentReviewFile.factory.d.ts.map +1 -0
  119. package/dist/tests/factories/recentReviewFile.factory.js +16 -0
  120. package/dist/tests/factories/recentReviewFile.factory.js.map +1 -0
  121. package/dist/tests/factories/repositoryConfig.factory.d.ts +5 -0
  122. package/dist/tests/factories/repositoryConfig.factory.d.ts.map +1 -0
  123. package/dist/tests/factories/repositoryConfig.factory.js +14 -0
  124. package/dist/tests/factories/repositoryConfig.factory.js.map +1 -0
  125. package/dist/tests/stubs/projectConfigGateway.stub.d.ts +15 -0
  126. package/dist/tests/stubs/projectConfigGateway.stub.d.ts.map +1 -0
  127. package/dist/tests/stubs/projectConfigGateway.stub.js +40 -0
  128. package/dist/tests/stubs/projectConfigGateway.stub.js.map +1 -0
  129. package/dist/tests/units/config/projectConfig.test.js +43 -0
  130. package/dist/tests/units/config/projectConfig.test.js.map +1 -1
  131. package/dist/tests/units/dashboard/modules/cardCounters.test.d.ts +2 -0
  132. package/dist/tests/units/dashboard/modules/cardCounters.test.d.ts.map +1 -0
  133. package/dist/tests/units/dashboard/modules/cardCounters.test.js +106 -0
  134. package/dist/tests/units/dashboard/modules/cardCounters.test.js.map +1 -0
  135. package/dist/tests/units/dashboard/modules/constants.test.js +2 -1
  136. package/dist/tests/units/dashboard/modules/constants.test.js.map +1 -1
  137. package/dist/tests/units/dashboard/modules/managePanel.test.d.ts +2 -0
  138. package/dist/tests/units/dashboard/modules/managePanel.test.d.ts.map +1 -0
  139. package/dist/tests/units/dashboard/modules/managePanel.test.js +112 -0
  140. package/dist/tests/units/dashboard/modules/managePanel.test.js.map +1 -0
  141. package/dist/tests/units/dashboard/modules/overview.test.d.ts +2 -0
  142. package/dist/tests/units/dashboard/modules/overview.test.d.ts.map +1 -0
  143. package/dist/tests/units/dashboard/modules/overview.test.js +268 -0
  144. package/dist/tests/units/dashboard/modules/overview.test.js.map +1 -0
  145. package/dist/tests/units/dashboard/modules/settingsModal.test.d.ts +2 -0
  146. package/dist/tests/units/dashboard/modules/settingsModal.test.d.ts.map +1 -0
  147. package/dist/tests/units/dashboard/modules/settingsModal.test.js +166 -0
  148. package/dist/tests/units/dashboard/modules/settingsModal.test.js.map +1 -0
  149. package/dist/tests/units/dashboard/modules/tabBar.test.d.ts +2 -0
  150. package/dist/tests/units/dashboard/modules/tabBar.test.d.ts.map +1 -0
  151. package/dist/tests/units/dashboard/modules/tabBar.test.js +128 -0
  152. package/dist/tests/units/dashboard/modules/tabBar.test.js.map +1 -0
  153. package/dist/tests/units/frameworks/config/configLoader.test.js +35 -1
  154. package/dist/tests/units/frameworks/config/configLoader.test.js.map +1 -1
  155. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.test.js +111 -0
  156. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/projectConfig.routes.test.js.map +1 -1
  157. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.test.d.ts +2 -0
  158. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.test.d.ts.map +1 -0
  159. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.test.js +298 -0
  160. package/dist/tests/units/modules/cli-configuration/interface-adapters/controllers/http/repositories.routes.test.js.map +1 -0
  161. package/dist/tests/units/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.test.d.ts +2 -0
  162. package/dist/tests/units/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.test.d.ts.map +1 -0
  163. package/dist/tests/units/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.test.js +72 -0
  164. package/dist/tests/units/modules/cli-configuration/interface-adapters/gateways/projectConfig.fileSystem.gateway.test.js.map +1 -0
  165. package/dist/tests/units/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.test.d.ts +2 -0
  166. package/dist/tests/units/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.test.d.ts.map +1 -0
  167. package/dist/tests/units/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.test.js +76 -0
  168. package/dist/tests/units/modules/cli-configuration/usecases/cli/removeRepositoryFromConfig.usecase.test.js.map +1 -0
  169. package/dist/tests/units/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.test.d.ts +2 -0
  170. package/dist/tests/units/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.test.d.ts.map +1 -0
  171. package/dist/tests/units/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.test.js +84 -0
  172. package/dist/tests/units/modules/cli-configuration/usecases/cli/toggleRepositoryEnabled.usecase.test.js.map +1 -0
  173. package/dist/tests/units/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.test.d.ts +2 -0
  174. package/dist/tests/units/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.test.d.ts.map +1 -0
  175. package/dist/tests/units/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.test.js +141 -0
  176. package/dist/tests/units/modules/cli-configuration/usecases/projectConfig/updateProjectConfig.usecase.test.js.map +1 -0
  177. package/dist/tests/units/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.test.d.ts +2 -0
  178. package/dist/tests/units/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.test.d.ts.map +1 -0
  179. package/dist/tests/units/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.test.js +200 -0
  180. package/dist/tests/units/modules/statistics-insights/interface-adapters/controllers/http/overview.routes.test.js.map +1 -0
  181. package/dist/tests/units/modules/statistics-insights/interface-adapters/presenters/overview.presenter.test.d.ts +2 -0
  182. package/dist/tests/units/modules/statistics-insights/interface-adapters/presenters/overview.presenter.test.d.ts.map +1 -0
  183. package/dist/tests/units/modules/statistics-insights/interface-adapters/presenters/overview.presenter.test.js +331 -0
  184. package/dist/tests/units/modules/statistics-insights/interface-adapters/presenters/overview.presenter.test.js.map +1 -0
  185. package/package.json +1 -1
@@ -30,6 +30,20 @@
30
30
  </div>
31
31
  </header>
32
32
 
33
+ <div class="project-bar" role="region" aria-label="Project navigation">
34
+ <button type="button" id="manage-projects-toggle" class="manage-projects-toggle" aria-expanded="false" aria-controls="manage-panel">
35
+ <span class="manage-projects-toggle-label">// MANAGE PROJECTS</span>
36
+ <i data-lucide="chevron-down" class="manage-projects-toggle-icon"></i>
37
+ </button>
38
+ <section id="manage-panel" class="manage-panel" aria-label="Manage projects" data-open="false"></section>
39
+ <nav id="dashboard-tabs" class="dashboard-tab-bar-wrapper" aria-label="Project tabs"></nav>
40
+ </div>
41
+
42
+ <div id="cards-scope-marker" class="cards-scope-marker" data-scope-kind="overview">
43
+ <span class="cards-scope-prefix">// SCOPE</span>
44
+ <span class="cards-scope-label">TOUS LES PROJETS</span>
45
+ </div>
46
+
33
47
  <div class="cards">
34
48
  <div class="card card-priority">
35
49
  <div class="card-label" id="i18n-card-running"></div>
@@ -78,19 +92,7 @@
78
92
  </select>
79
93
  </div>
80
94
 
81
- <div class="project-loader">
82
- <select id="project-select" class="project-input" onchange="onProjectSelect(this.value)">
83
- <option value="" id="i18n-project-placeholder"></option>
84
- </select>
85
- <input type="text" id="project-path-input" class="project-input" value="">
86
- <button class="btn btn-primary" onclick="loadProjectConfig()">
87
- <i data-lucide="folder-open"></i> <span id="i18n-project-load"></span>
88
- </button>
89
- <button id="remove-project-btn" class="btn btn-secondary" onclick="removeCurrentProject()">
90
- <i data-lucide="trash-2"></i>
91
- </button>
92
- <span id="config-status" class="config-status hidden"></span>
93
- </div>
95
+ <span id="config-status" class="config-status hidden"></span>
94
96
 
95
97
  <div class="focus-strip">
96
98
  <div class="focus-chip focus-now">
@@ -118,9 +120,15 @@
118
120
  </div>
119
121
 
120
122
  <section id="worktree-section" aria-label="Worktree pool"></section>
123
+
124
+ <button type="button" id="open-settings-modal-btn" class="sidebar-settings-button" hidden>
125
+ <span class="sidebar-settings-button__prefix">// SETTINGS</span>
126
+ </button>
121
127
  </aside>
122
128
 
123
129
  <main class="dashboard-main">
130
+ <section id="overview-section" class="overview-section" aria-label="Multi-project overview"></section>
131
+
124
132
  <div id="data-loading-state" class="data-loading hidden" role="status" aria-live="polite">
125
133
  <i data-lucide="loader-circle"></i>
126
134
  <span id="i18n-loading-data"></span>
@@ -313,14 +321,20 @@
313
321
  <div id="dev-sheet-content" class="sheet-content"></div>
314
322
  </div>
315
323
 
324
+ <dialog id="settings-modal" class="settings-modal" aria-labelledby="settings-modal-title"></dialog>
325
+
316
326
  <script type="module">
317
327
  import { t, setLanguage, getLanguage } from './modules/i18n.js';
318
328
  import { formatTime, formatDuration, formatPhase, formatLogTime } from './modules/formatting.js';
319
329
  import { escapeHtml, markdownToHtml, sanitizeHttpUrl } from './modules/html.js';
320
330
  import { getAgentIcon, icon, refreshIcons } from './modules/icons.js';
321
- import { MAX_RECONNECT_ATTEMPTS, RECONNECT_DELAY, STORAGE_KEY_PROJECTS, STORAGE_KEY_CURRENT, STORAGE_KEY_FOCUS_STRIP_MODE, QUALITY_TARGET_SCORE } from './modules/constants.js';
331
+ import { MAX_RECONNECT_ATTEMPTS, RECONNECT_DELAY, STORAGE_KEY_CURRENT, STORAGE_KEY_FOCUS_STRIP_MODE, QUALITY_TARGET_SCORE } from './modules/constants.js';
332
+ import { buildTabBarModel, renderTabBarHtml, readActiveTab, writeActiveTab } from './modules/tabBar.js';
333
+ import { buildManagePanelModel, renderManagePanelHtml, buildOptimisticAddedRow, validateLocalPathInput } from './modules/managePanel.js';
334
+ import { renderOverviewHtml } from './modules/overview.js';
322
335
  import { getDesktopNotificationPayload, shouldNotifyDesktop } from './modules/desktopNotifications.js';
323
336
  import { getLoadingPresentation, getQuietRefreshSectionIdentifiers } from './modules/loading.js';
337
+ import { computeCardCounters } from './modules/cardCounters.js';
324
338
  import { collectReviewNotifications, createReviewNotificationState } from './modules/notifications.js';
325
339
  import { resolveReviewAssigneeDisplay } from './modules/assignee.js';
326
340
  import { buildQueueLanesModel } from './modules/queueLanes.js';
@@ -352,6 +366,12 @@
352
366
  fetchBudgetStatus,
353
367
  submitBudget,
354
368
  } from './modules/budgetSettings.js';
369
+ import {
370
+ buildSettingsViewModel,
371
+ renderSettingsModalHtml,
372
+ validateExternalLink,
373
+ extractFormPayload,
374
+ } from './modules/settingsModal.js';
355
375
 
356
376
  const API_URL = window.location.origin;
357
377
  const WS_URL = `ws://${window.location.host}/ws`;
@@ -774,14 +794,12 @@
774
794
  const reviews = currentData.activeReviews.filter(r => r.jobType !== 'followup');
775
795
  const followups = currentData.activeReviews.filter(r => r.jobType === 'followup');
776
796
 
797
+ renderCardCounters();
798
+ const blocked = currentData.pendingFix.length;
777
799
  const running = currentData.activeReviews.filter(r => r.status === 'running').length;
778
800
  const queued = currentData.activeReviews.filter(r => r.status === 'queued').length;
779
- const blocked = currentData.pendingFix.length;
780
801
  const nowCount = running + blocked;
781
802
  const nextCount = queued + currentData.pendingApproval.length;
782
- document.getElementById('running-count').textContent = running;
783
- document.getElementById('queued-count').textContent = queued;
784
- document.getElementById('completed-count').textContent = currentData.reviewFiles.length;
785
803
  document.getElementById('focus-now-count').textContent = String(nowCount);
786
804
  document.getElementById('focus-next-count').textContent = String(nextCount);
787
805
  document.getElementById('focus-blocked-count').textContent = String(blocked);
@@ -2088,6 +2106,9 @@
2088
2106
  if (message.type === 'state') {
2089
2107
  fetchMrTracking();
2090
2108
  }
2109
+ if (activeTabId === 'overview') {
2110
+ refreshOverviewSection();
2111
+ }
2091
2112
  break;
2092
2113
  case 'progress':
2093
2114
  handleProgressUpdate(message);
@@ -2277,69 +2298,6 @@
2277
2298
  let currentProjectConfig = null;
2278
2299
  let currentProjectPath = null;
2279
2300
 
2280
- function getStoredProjects() {
2281
- try {
2282
- return JSON.parse(localStorage.getItem(STORAGE_KEY_PROJECTS) || '[]');
2283
- } catch {
2284
- return [];
2285
- }
2286
- }
2287
-
2288
- function saveProjects(projects) {
2289
- localStorage.setItem(STORAGE_KEY_PROJECTS, JSON.stringify(projects));
2290
- }
2291
-
2292
- function addProjectToHistory(path) {
2293
- const projects = getStoredProjects();
2294
- const filtered = projects.filter(p => p !== path);
2295
- filtered.unshift(path);
2296
- saveProjects(filtered.slice(0, 10));
2297
- updateProjectSelect();
2298
- }
2299
-
2300
- function removeProjectFromHistory(path) {
2301
- const projects = getStoredProjects().filter(p => p !== path);
2302
- saveProjects(projects);
2303
- updateProjectSelect();
2304
- }
2305
-
2306
- function updateProjectSelect() {
2307
- const select = document.getElementById('project-select');
2308
- const projects = getStoredProjects();
2309
- const current = localStorage.getItem(STORAGE_KEY_CURRENT) || '';
2310
-
2311
- select.innerHTML = `<option value="">${t('project.selectPlaceholder')}</option>`;
2312
- for (const path of projects) {
2313
- const shortName = path.split('/').slice(-2).join('/');
2314
- const option = document.createElement('option');
2315
- option.value = path;
2316
- option.textContent = shortName;
2317
- option.title = path;
2318
- if (path === current) option.selected = true;
2319
- select.appendChild(option);
2320
- }
2321
- }
2322
-
2323
- function onProjectSelect(path) {
2324
- if (path) {
2325
- document.getElementById('project-path-input').value = '';
2326
- loadProjectConfigFromPath(path);
2327
- }
2328
- }
2329
-
2330
- async function loadProjectConfig() {
2331
- const input = document.getElementById('project-path-input');
2332
- const select = document.getElementById('project-select');
2333
- const projectPath = input.value.trim() || select.value;
2334
-
2335
- if (!projectPath) {
2336
- showConfigStatus(t('error.selectOrEnterPath'), 'error');
2337
- return;
2338
- }
2339
-
2340
- await loadProjectConfigFromPath(projectPath);
2341
- }
2342
-
2343
2301
  async function loadProjectConfigFromPath(projectPath) {
2344
2302
  const status = document.getElementById('config-status');
2345
2303
  const info = document.getElementById('config-info');
@@ -2355,12 +2313,8 @@
2355
2313
  currentProjectConfig = data.config;
2356
2314
  currentProjectPath = projectPath;
2357
2315
 
2358
- addProjectToHistory(projectPath);
2359
2316
  localStorage.setItem(STORAGE_KEY_CURRENT, projectPath);
2360
2317
 
2361
- document.getElementById('project-select').value = projectPath;
2362
- document.getElementById('project-path-input').value = '';
2363
-
2364
2318
  const shortName = projectPath.split('/').slice(-2).join('/');
2365
2319
  showConfigStatus(`<i data-lucide="check-circle"></i> ${escapeHtml(shortName)}`, 'success');
2366
2320
  refreshIcons();
@@ -2413,59 +2367,434 @@
2413
2367
  refreshIcons();
2414
2368
  }
2415
2369
 
2416
- function removeCurrentProject() {
2417
- const select = document.getElementById('project-select');
2418
- const path = select.value;
2419
- if (!path) {
2420
- showConfigStatus(t('project.noProjectSelected'), 'error');
2370
+ // SPEC-91 — Dashboard Multi-Project Overview
2371
+ let availableRepositories = [];
2372
+ let activeTabId = 'overview';
2373
+
2374
+ async function fetchAvailableRepositories() {
2375
+ try {
2376
+ const response = await fetch(`${API_URL}/api/repositories`);
2377
+ const data = await response.json();
2378
+ availableRepositories = Array.isArray(data.repositories) ? data.repositories : [];
2379
+ } catch {
2380
+ availableRepositories = [];
2381
+ }
2382
+ }
2383
+
2384
+ function syncAvailableRepositoriesFromResponse(payload) {
2385
+ availableRepositories = Array.isArray(payload?.repositories) ? payload.repositories : [];
2386
+ }
2387
+
2388
+ function renderCardCounters() {
2389
+ let scope;
2390
+ if (activeTabId === 'overview') {
2391
+ scope = { kind: 'overview' };
2392
+ } else {
2393
+ const repository = availableRepositories.find((r) => r.localPath === activeTabId);
2394
+ const projectName = repository?.name ?? activeTabId.split('/').filter(Boolean).pop() ?? activeTabId;
2395
+ scope = { kind: 'project', localPath: activeTabId, projectName };
2396
+ }
2397
+ const counters = computeCardCounters({
2398
+ activeReviews: currentData.activeReviews,
2399
+ reviewFiles: currentData.reviewFiles,
2400
+ scope,
2401
+ });
2402
+ const runningEl = document.getElementById('running-count');
2403
+ const queuedEl = document.getElementById('queued-count');
2404
+ const completedEl = document.getElementById('completed-count');
2405
+ if (runningEl) runningEl.textContent = counters.running;
2406
+ if (queuedEl) queuedEl.textContent = counters.queued;
2407
+ if (completedEl) completedEl.textContent = counters.completed;
2408
+ const markerEl = document.getElementById('cards-scope-marker');
2409
+ if (markerEl) {
2410
+ markerEl.dataset.scopeKind = counters.markerKind;
2411
+ const labelEl = markerEl.querySelector('.cards-scope-label');
2412
+ if (labelEl) labelEl.textContent = counters.markerLabel;
2413
+ }
2414
+ }
2415
+
2416
+ function renderDashboardTabs() {
2417
+ const container = document.getElementById('dashboard-tabs');
2418
+ if (!container) return;
2419
+ const model = buildTabBarModel({
2420
+ repositories: availableRepositories,
2421
+ activeTabId: activeTabId === 'overview' ? null : activeTabId,
2422
+ });
2423
+ container.innerHTML = renderTabBarHtml(model);
2424
+ container.querySelectorAll('.dashboard-tab').forEach((button) => {
2425
+ button.addEventListener('click', () => {
2426
+ const tabId = button.dataset.tabId;
2427
+ if (!tabId) return;
2428
+ handleTabClick(tabId);
2429
+ });
2430
+ });
2431
+ }
2432
+
2433
+ function handleTabClick(tabId) {
2434
+ if (tabId === 'overview') {
2435
+ activateOverviewTab();
2421
2436
  return;
2422
2437
  }
2423
- const shortName = path.split('/').slice(-2).join('/');
2424
- if (confirm(t('confirm.removeProject', { name: shortName }))) {
2425
- removeProjectFromHistory(path);
2426
- localStorage.removeItem(STORAGE_KEY_CURRENT);
2427
- currentProjectPath = null;
2428
- currentProjectConfig = null;
2429
- document.getElementById('config-info').classList.add('hidden');
2430
- showConfigStatus(t('project.removed'), 'success');
2438
+ activateProjectTab(tabId);
2439
+ }
2440
+
2441
+ function activateOverviewTab() {
2442
+ activeTabId = 'overview';
2443
+ writeActiveTab('overview');
2444
+ document.body.classList.add('overview-tab-active');
2445
+ const overviewSection = document.getElementById('overview-section');
2446
+ if (overviewSection) overviewSection.classList.remove('hidden');
2447
+ renderDashboardTabs();
2448
+ renderCardCounters();
2449
+ syncSettingsButtonVisibility();
2450
+ refreshOverviewSection();
2451
+ }
2452
+
2453
+ function activateProjectTab(projectPath) {
2454
+ activeTabId = projectPath;
2455
+ writeActiveTab(projectPath);
2456
+ document.body.classList.remove('overview-tab-active');
2457
+ const overviewSection = document.getElementById('overview-section');
2458
+ if (overviewSection) overviewSection.classList.add('hidden');
2459
+ renderDashboardTabs();
2460
+ renderCardCounters();
2461
+ syncSettingsButtonVisibility();
2462
+ loadProjectConfigFromPath(projectPath);
2463
+ }
2464
+
2465
+ function syncSettingsButtonVisibility() {
2466
+ const button = document.getElementById('open-settings-modal-btn');
2467
+ if (!button) return;
2468
+ if (activeTabId === 'overview') {
2469
+ button.hidden = true;
2470
+ } else {
2471
+ button.hidden = false;
2431
2472
  }
2432
2473
  }
2433
2474
 
2434
- async function syncServerRepositories() {
2475
+ function resolveActiveProjectName() {
2476
+ if (activeTabId === 'overview') return '—';
2477
+ const repository = availableRepositories.find((r) => r.localPath === activeTabId);
2478
+ if (repository?.name) return repository.name;
2479
+ return activeTabId.split('/').filter(Boolean).pop() ?? activeTabId;
2480
+ }
2481
+
2482
+ async function openSettingsModal() {
2483
+ if (activeTabId === 'overview') return;
2484
+ const dialog = document.getElementById('settings-modal');
2485
+ if (!dialog || typeof dialog.showModal !== 'function') return;
2435
2486
  try {
2436
- const response = await fetch(`${API_URL}/api/repositories`);
2437
- const data = await response.json();
2438
- if (!data.repositories) return;
2487
+ const response = await fetch(
2488
+ `${API_URL}/api/project-config?path=${encodeURIComponent(activeTabId)}`,
2489
+ );
2490
+ const payload = await response.json();
2491
+ if (!payload?.success) {
2492
+ dialog.innerHTML = `<form method="dialog" class="settings-modal__form"><p class="settings-modal__error">${escapeHtml(payload?.error || 'Configuration projet illisible')}</p><div class="settings-modal__actions"><button type="submit" class="settings-modal__cancel">Fermer</button></div></form>`;
2493
+ dialog.showModal();
2494
+ return;
2495
+ }
2496
+ const viewModel = buildSettingsViewModel({
2497
+ config: payload.config,
2498
+ projectName: resolveActiveProjectName(),
2499
+ });
2500
+ dialog.innerHTML = renderSettingsModalHtml(viewModel);
2501
+ bindSettingsModalForm(dialog);
2502
+ dialog.showModal();
2503
+ } catch (error) {
2504
+ showToast(t('toast.error') || 'Erreur', 'error');
2505
+ }
2506
+ }
2439
2507
 
2440
- const stored = getStoredProjects();
2441
- for (const repository of data.repositories) {
2442
- if (repository.enabled && !stored.includes(repository.localPath)) {
2443
- stored.push(repository.localPath);
2508
+ function closeSettingsModal() {
2509
+ const dialog = document.getElementById('settings-modal');
2510
+ if (dialog && dialog.open) dialog.close();
2511
+ }
2512
+
2513
+ function bindSettingsModalForm(dialog) {
2514
+ const form = dialog.querySelector('form.settings-modal__form');
2515
+ const cancelBtn = dialog.querySelector('.settings-modal__cancel');
2516
+ const errorEl = dialog.querySelector('.settings-modal__error');
2517
+ if (cancelBtn) {
2518
+ cancelBtn.addEventListener('click', (event) => {
2519
+ event.preventDefault();
2520
+ closeSettingsModal();
2521
+ });
2522
+ }
2523
+ if (!form) return;
2524
+ form.addEventListener('submit', async (event) => {
2525
+ event.preventDefault();
2526
+ if (errorEl) errorEl.textContent = '';
2527
+ const formData = new FormData(form);
2528
+ const payload = extractFormPayload(formData);
2529
+ const linkValidation = validateExternalLink(payload.externalLink ?? '');
2530
+ if (!linkValidation.ok) {
2531
+ if (errorEl) errorEl.textContent = linkValidation.message;
2532
+ return;
2533
+ }
2534
+ try {
2535
+ const response = await fetch(
2536
+ `${API_URL}/api/project-config?path=${encodeURIComponent(activeTabId)}`,
2537
+ {
2538
+ method: 'PATCH',
2539
+ headers: { 'Content-Type': 'application/json' },
2540
+ body: JSON.stringify(payload),
2541
+ },
2542
+ );
2543
+ if (!response.ok) {
2544
+ const errorPayload = await response.json().catch(() => ({ error: 'Échec de la sauvegarde' }));
2545
+ if (errorEl) errorEl.textContent = errorPayload?.error || 'Échec de la sauvegarde';
2546
+ return;
2444
2547
  }
2548
+ closeSettingsModal();
2549
+ refreshOverviewSection();
2550
+ } catch {
2551
+ if (errorEl) errorEl.textContent = 'Échec de la sauvegarde';
2445
2552
  }
2446
- saveProjects(stored);
2447
- updateProjectSelect();
2553
+ });
2554
+ }
2555
+
2556
+ function bindSettingsModalDismissals() {
2557
+ const dialog = document.getElementById('settings-modal');
2558
+ if (!dialog) return;
2559
+ dialog.addEventListener('click', (event) => {
2560
+ if (event.target === dialog) {
2561
+ closeSettingsModal();
2562
+ }
2563
+ });
2564
+ }
2565
+
2566
+ function bindSettingsModalTrigger() {
2567
+ const button = document.getElementById('open-settings-modal-btn');
2568
+ if (!button) return;
2569
+ button.addEventListener('click', () => {
2570
+ openSettingsModal();
2571
+ });
2572
+ }
2573
+
2574
+ async function refreshOverviewSection() {
2575
+ const container = document.getElementById('overview-section');
2576
+ if (!container) return;
2577
+ if (activeTabId !== 'overview') return;
2578
+ try {
2579
+ const response = await fetch(`${API_URL}/api/overview`);
2580
+ const viewModel = await response.json();
2581
+ container.innerHTML = renderOverviewHtml(viewModel);
2582
+ container.querySelectorAll('.overview-project-card').forEach((card) => {
2583
+ card.addEventListener('click', () => {
2584
+ const projectPath = card.dataset.projectPath;
2585
+ if (projectPath) activateProjectTab(projectPath);
2586
+ });
2587
+ });
2588
+ container.querySelectorAll('.project-card__external').forEach((anchor) => {
2589
+ anchor.addEventListener('click', (event) => event.stopPropagation());
2590
+ });
2448
2591
  } catch {
2449
- // Server unreachable use localStorage only
2592
+ // Silent: WS reconnection or next refresh will retry
2450
2593
  }
2451
2594
  }
2452
2595
 
2453
- async function initProjectLoader() {
2454
- await syncServerRepositories();
2455
- updateProjectSelect();
2456
- const lastProject = localStorage.getItem(STORAGE_KEY_CURRENT);
2457
- if (lastProject) {
2458
- loadProjectConfigFromPath(lastProject);
2596
+ async function initOverviewAndTabs() {
2597
+ await fetchAvailableRepositories();
2598
+ renderManagePanel();
2599
+ bindManagePanelToggle();
2600
+ bindSettingsModalTrigger();
2601
+ bindSettingsModalDismissals();
2602
+ const persistedTab = readActiveTab();
2603
+ const repositoryPaths = availableRepositories.map((repository) => repository.localPath);
2604
+ if (persistedTab && persistedTab !== 'overview' && repositoryPaths.includes(persistedTab)) {
2605
+ activateProjectTab(persistedTab);
2459
2606
  } else {
2460
- const storedProjects = getStoredProjects();
2461
- if (storedProjects.length > 0) {
2462
- loadProjectConfigFromPath(storedProjects[0]);
2607
+ activateOverviewTab();
2608
+ }
2609
+ }
2610
+
2611
+ let isManagePanelOpen = false;
2612
+
2613
+ function renderManagePanel() {
2614
+ const panel = document.getElementById('manage-panel');
2615
+ if (!panel) return;
2616
+ const model = buildManagePanelModel({ repositories: availableRepositories, isOpen: isManagePanelOpen });
2617
+ panel.innerHTML = renderManagePanelHtml(model);
2618
+ panel.dataset.open = isManagePanelOpen ? 'true' : 'false';
2619
+ bindManagePanelHandlers();
2620
+ }
2621
+
2622
+ function bindManagePanelToggle() {
2623
+ const toggle = document.getElementById('manage-projects-toggle');
2624
+ if (!toggle || toggle.dataset.bound === 'true') return;
2625
+ toggle.dataset.bound = 'true';
2626
+ toggle.addEventListener('click', () => {
2627
+ isManagePanelOpen = !isManagePanelOpen;
2628
+ toggle.setAttribute('aria-expanded', isManagePanelOpen ? 'true' : 'false');
2629
+ renderManagePanel();
2630
+ });
2631
+ }
2632
+
2633
+ function bindManagePanelHandlers() {
2634
+ const panel = document.getElementById('manage-panel');
2635
+ if (!panel) return;
2636
+ const form = panel.querySelector('form.add-form');
2637
+ if (form) {
2638
+ form.addEventListener('submit', handleAddProjectSubmit);
2639
+ const input = form.querySelector('input.add-form-input');
2640
+ if (input) {
2641
+ input.addEventListener('keydown', (event) => {
2642
+ if (event.key === 'Escape') {
2643
+ input.value = '';
2644
+ clearManagePanelError();
2645
+ }
2646
+ });
2647
+ }
2648
+ }
2649
+ panel.querySelectorAll('.manage-row-delete').forEach((button) => {
2650
+ button.addEventListener('click', () => {
2651
+ const row = button.closest('.manage-row');
2652
+ const localPath = row?.dataset.localPath;
2653
+ if (localPath) handleDeleteProject(localPath, row);
2654
+ });
2655
+ });
2656
+ panel.querySelectorAll('.manage-row-toggle').forEach((button) => {
2657
+ button.addEventListener('click', () => {
2658
+ const row = button.closest('.manage-row');
2659
+ const localPath = row?.dataset.localPath;
2660
+ const currentlyEnabled = row?.dataset.enabled === 'true';
2661
+ if (localPath) handleToggleProject(localPath, !currentlyEnabled);
2662
+ });
2663
+ });
2664
+ }
2665
+
2666
+ function setManagePanelError(message) {
2667
+ const panel = document.getElementById('manage-panel');
2668
+ const target = panel?.querySelector('[data-role="error-message"]');
2669
+ if (!target) return;
2670
+ target.textContent = message;
2671
+ target.hidden = false;
2672
+ }
2673
+
2674
+ function clearManagePanelError() {
2675
+ const panel = document.getElementById('manage-panel');
2676
+ const target = panel?.querySelector('[data-role="error-message"]');
2677
+ if (!target) return;
2678
+ target.textContent = '';
2679
+ target.hidden = true;
2680
+ }
2681
+
2682
+ function flashAddFormError() {
2683
+ const form = document.querySelector('#manage-panel form.add-form');
2684
+ if (!form) return;
2685
+ form.classList.remove('is-error');
2686
+ requestAnimationFrame(() => form.classList.add('is-error'));
2687
+ setTimeout(() => form.classList.remove('is-error'), 320);
2688
+ }
2689
+
2690
+ function flashAddFormSuccess() {
2691
+ const input = document.querySelector('#manage-panel input.add-form-input');
2692
+ if (!input) return;
2693
+ input.classList.remove('is-success');
2694
+ requestAnimationFrame(() => input.classList.add('is-success'));
2695
+ setTimeout(() => input.classList.remove('is-success'), 1500);
2696
+ }
2697
+
2698
+ function flashTabEntering(localPath) {
2699
+ const tab = document.querySelector(`.dashboard-tab[data-tab-id="${CSS.escape(localPath)}"]`);
2700
+ if (!tab) return;
2701
+ tab.classList.add('is-entering');
2702
+ setTimeout(() => tab.classList.remove('is-entering'), 1500);
2703
+ }
2704
+
2705
+ async function handleAddProjectSubmit(event) {
2706
+ event.preventDefault();
2707
+ const form = event.currentTarget;
2708
+ const input = form.querySelector('input.add-form-input');
2709
+ const submit = form.querySelector('button.add-form-submit');
2710
+ const rawValue = input ? input.value : '';
2711
+ const validation = validateLocalPathInput(rawValue);
2712
+ clearManagePanelError();
2713
+ if (!validation.ok) {
2714
+ const message = validation.reason === 'empty' ? 'Chemin du projet requis' : 'Le chemin doit être absolu';
2715
+ setManagePanelError(message);
2716
+ flashAddFormError();
2717
+ return;
2718
+ }
2719
+ const trimmed = rawValue.trim();
2720
+ if (submit) submit.classList.add('is-busy');
2721
+ try {
2722
+ const response = await fetch(`${API_URL}/api/repositories`, {
2723
+ method: 'POST',
2724
+ headers: { 'Content-Type': 'application/json' },
2725
+ body: JSON.stringify({ localPath: trimmed }),
2726
+ });
2727
+ const body = await response.json().catch(() => ({}));
2728
+ if (!response.ok) {
2729
+ setManagePanelError(typeof body?.error === 'string' ? body.error : 'Erreur inconnue');
2730
+ flashAddFormError();
2731
+ return;
2732
+ }
2733
+ syncAvailableRepositoriesFromResponse(body);
2734
+ if (input) input.value = '';
2735
+ flashAddFormSuccess();
2736
+ renderManagePanel();
2737
+ renderDashboardTabs();
2738
+ flashTabEntering(trimmed);
2739
+ } catch {
2740
+ setManagePanelError('Erreur réseau');
2741
+ flashAddFormError();
2742
+ } finally {
2743
+ if (submit) submit.classList.remove('is-busy');
2744
+ }
2745
+ }
2746
+
2747
+ async function handleDeleteProject(localPath, rowElement) {
2748
+ try {
2749
+ const response = await fetch(`${API_URL}/api/repositories?localPath=${encodeURIComponent(localPath)}`, {
2750
+ method: 'DELETE',
2751
+ });
2752
+ const body = await response.json().catch(() => ({}));
2753
+ if (!response.ok) {
2754
+ setManagePanelError(typeof body?.error === 'string' ? body.error : 'Erreur inconnue');
2755
+ return;
2756
+ }
2757
+ syncAvailableRepositoriesFromResponse(body);
2758
+ if (rowElement) {
2759
+ rowElement.classList.add('is-leaving');
2760
+ setTimeout(() => {
2761
+ renderManagePanel();
2762
+ }, 250);
2763
+ } else {
2764
+ renderManagePanel();
2765
+ }
2766
+ const tab = document.querySelector(`.dashboard-tab[data-tab-id="${CSS.escape(localPath)}"]`);
2767
+ if (tab) {
2768
+ tab.classList.add('is-leaving');
2769
+ setTimeout(() => renderDashboardTabs(), 250);
2463
2770
  } else {
2464
- document.getElementById('recent-reviews').innerHTML =
2465
- `<div class="empty-state">${t('empty.reviewsNoProject')}</div>`;
2466
- document.getElementById('project-stats').innerHTML =
2467
- `<div class="empty-state">${t('empty.statsNoProject')}</div>`;
2771
+ renderDashboardTabs();
2772
+ }
2773
+ if (activeTabId === localPath) {
2774
+ activateOverviewTab();
2468
2775
  }
2776
+ } catch {
2777
+ setManagePanelError('Erreur réseau');
2778
+ }
2779
+ }
2780
+
2781
+ async function handleToggleProject(localPath, enabled) {
2782
+ try {
2783
+ const response = await fetch(`${API_URL}/api/repositories?localPath=${encodeURIComponent(localPath)}`, {
2784
+ method: 'PATCH',
2785
+ headers: { 'Content-Type': 'application/json' },
2786
+ body: JSON.stringify({ enabled }),
2787
+ });
2788
+ const body = await response.json().catch(() => ({}));
2789
+ if (!response.ok) {
2790
+ setManagePanelError(typeof body?.error === 'string' ? body.error : 'Erreur inconnue');
2791
+ return;
2792
+ }
2793
+ syncAvailableRepositoriesFromResponse(body);
2794
+ renderManagePanel();
2795
+ renderDashboardTabs();
2796
+ } catch {
2797
+ setManagePanelError('Erreur réseau');
2469
2798
  }
2470
2799
  }
2471
2800
 
@@ -2608,19 +2937,6 @@
2608
2937
  const modelSonnet = document.getElementById('i18n-model-sonnet');
2609
2938
  if (modelSonnet) modelSonnet.textContent = t('model.sonnet');
2610
2939
 
2611
- // Project loader
2612
- const projectPlaceholder = document.getElementById('i18n-project-placeholder');
2613
- if (projectPlaceholder) projectPlaceholder.textContent = t('project.selectPlaceholder');
2614
-
2615
- const projectPathInput = document.getElementById('project-path-input');
2616
- if (projectPathInput) projectPathInput.placeholder = t('project.inputPlaceholder');
2617
-
2618
- const projectLoad = document.getElementById('i18n-project-load');
2619
- if (projectLoad) projectLoad.textContent = t('project.load');
2620
-
2621
- const removeProjectBtn = document.getElementById('remove-project-btn');
2622
- if (removeProjectBtn) removeProjectBtn.title = t('project.removeTooltip');
2623
-
2624
2940
  // Login sections
2625
2941
  const claudeLoginTitle = document.getElementById('i18n-claude-login-title');
2626
2942
  if (claudeLoginTitle) claudeLoginTitle.textContent = t('login.claude.title');
@@ -2719,9 +3035,6 @@
2719
3035
 
2720
3036
  const modalConfirm = document.getElementById('cancel-modal-confirm');
2721
3037
  if (modalConfirm) modalConfirm.textContent = t('modal.confirm');
2722
-
2723
- // Update project select placeholder (re-render the select)
2724
- updateProjectSelect();
2725
3038
  }
2726
3039
 
2727
3040
  async function checkForUpdates() {
@@ -2879,9 +3192,6 @@
2879
3192
  window.toggleStats = toggleStats;
2880
3193
  window.changeModel = changeModel;
2881
3194
  window.changeLanguage = changeLanguage;
2882
- window.onProjectSelect = onProjectSelect;
2883
- window.loadProjectConfig = loadProjectConfig;
2884
- window.removeCurrentProject = removeCurrentProject;
2885
3195
  window.toggleReviewAccordion = toggleReviewAccordion;
2886
3196
  window.toggleReviewDescription = toggleReviewDescription;
2887
3197
  window.deleteReviewFile = deleteReviewFile;
@@ -2926,7 +3236,7 @@
2926
3236
  checkClaudeStatus();
2927
3237
  loadModelSetting();
2928
3238
  loadLanguageSetting();
2929
- initProjectLoader();
3239
+ initOverviewAndTabs();
2930
3240
  refreshBudgetTile();
2931
3241
  initBudgetSlider();
2932
3242