reviewflow 3.24.2 → 3.25.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 (51) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/dashboard/index.html +212 -100
  3. package/dist/dashboard/modules/i18n.d.ts.map +1 -1
  4. package/dist/dashboard/modules/i18n.js +14 -4
  5. package/dist/dashboard/modules/i18n.js.map +1 -1
  6. package/dist/dashboard/modules/sectionVisibility.d.ts +17 -0
  7. package/dist/dashboard/modules/sectionVisibility.d.ts.map +1 -0
  8. package/dist/dashboard/modules/sectionVisibility.js +20 -0
  9. package/dist/dashboard/modules/sectionVisibility.js.map +1 -0
  10. package/dist/frameworks/settings/runtimeSettings.d.ts +36 -11
  11. package/dist/frameworks/settings/runtimeSettings.d.ts.map +1 -1
  12. package/dist/frameworks/settings/runtimeSettings.js +98 -9
  13. package/dist/frameworks/settings/runtimeSettings.js.map +1 -1
  14. package/dist/main/server.d.ts.map +1 -1
  15. package/dist/main/server.js +4 -0
  16. package/dist/main/server.js.map +1 -1
  17. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.d.ts.map +1 -1
  18. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.js +7 -11
  19. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.js.map +1 -1
  20. package/dist/modules/review-execution/entities/reviewRequest/reviewRequestState.valueObject.js +1 -1
  21. package/dist/modules/review-execution/entities/reviewRequest/reviewRequestState.valueObject.js.map +1 -1
  22. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.d.ts.map +1 -1
  23. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.js +42 -0
  24. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.js.map +1 -1
  25. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.d.ts +6 -0
  26. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.d.ts.map +1 -1
  27. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.js +7 -0
  28. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.js.map +1 -1
  29. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.d.ts +19 -0
  30. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.d.ts.map +1 -0
  31. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.js +157 -0
  32. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.js.map +1 -0
  33. package/dist/tests/units/dashboard/dashboardLayout.test.d.ts +13 -0
  34. package/dist/tests/units/dashboard/dashboardLayout.test.d.ts.map +1 -0
  35. package/dist/tests/units/dashboard/dashboardLayout.test.js +247 -0
  36. package/dist/tests/units/dashboard/dashboardLayout.test.js.map +1 -0
  37. package/dist/tests/units/dashboard/modules/sectionVisibility.test.d.ts +2 -0
  38. package/dist/tests/units/dashboard/modules/sectionVisibility.test.d.ts.map +1 -0
  39. package/dist/tests/units/dashboard/modules/sectionVisibility.test.js +58 -0
  40. package/dist/tests/units/dashboard/modules/sectionVisibility.test.js.map +1 -0
  41. package/dist/tests/units/entities/reviewRequestState.valueObject.test.js +5 -0
  42. package/dist/tests/units/entities/reviewRequestState.valueObject.test.js.map +1 -1
  43. package/dist/tests/units/frameworks/settings/runtimeSettings.test.js +123 -16
  44. package/dist/tests/units/frameworks/settings/runtimeSettings.test.js.map +1 -1
  45. package/dist/tests/units/interface-adapters/controllers/http/settings.routes.test.js +1 -1
  46. package/dist/tests/units/interface-adapters/controllers/http/settings.routes.test.js.map +1 -1
  47. package/dist/tests/units/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.test.js +37 -0
  48. package/dist/tests/units/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.test.js.map +1 -1
  49. package/dist/tests/units/usecases/tracking/transitionState.usecase.test.js +36 -0
  50. package/dist/tests/units/usecases/tracking/transitionState.usecase.test.js.map +1 -1
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.25.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.24.2...reviewflow-v3.25.0) (2026-05-27)
9
+
10
+
11
+ ### Added
12
+
13
+ * **dashboard:** restructure empty states and promote team section ([#222](https://github.com/DGouron/review-flow/issues/222)) ([493cd8a](https://github.com/DGouron/review-flow/commit/493cd8a30f0b25fbc7f2b1ae0c6e42393713fd79))
14
+ * **tracking:** allow manual mark-as-merged from pending-fix ([#224](https://github.com/DGouron/review-flow/issues/224)) ([f4cf64d](https://github.com/DGouron/review-flow/commit/f4cf64d03c9d121d9462462ca9f32090d1e915f6))
15
+
16
+
17
+ ### Fixed
18
+
19
+ * **settings:** persist runtime settings across restarts ([#221](https://github.com/DGouron/review-flow/issues/221)) ([f8a71b8](https://github.com/DGouron/review-flow/commit/f8a71b8dbcf3ae16557b219131cdc8f08a4e9d65))
20
+
8
21
  ## [3.24.2](https://github.com/DGouron/review-flow/compare/reviewflow-v3.24.1...reviewflow-v3.24.2) (2026-05-27)
9
22
 
10
23
 
@@ -99,6 +99,12 @@
99
99
  <button type="button" id="open-settings-modal-btn" class="sidebar-settings-button" hidden>
100
100
  <span class="sidebar-settings-button__prefix">// SETTINGS</span>
101
101
  </button>
102
+ <button type="button" id="open-economics-sheet-btn" class="sidebar-settings-button" onclick="openEconomicsSheet()">
103
+ <span class="sidebar-settings-button__prefix">// CLAUDE ECONOMICS</span>
104
+ </button>
105
+ <button type="button" id="open-stats-sheet-btn" class="sidebar-settings-button" onclick="openStatsSheet()" disabled aria-disabled="true">
106
+ <span class="sidebar-settings-button__prefix">// PROJECT STATS</span>
107
+ </button>
102
108
 
103
109
  <span id="config-status" class="config-status hidden"></span>
104
110
 
@@ -106,6 +112,16 @@
106
112
  </aside>
107
113
 
108
114
  <main class="dashboard-main">
115
+ <div id="team-section" class="section hidden">
116
+ <div class="section-header clickable" onclick="toggleTeamSection()" role="button" tabindex="0" onkeydown="activateOnKeydown(event)">
117
+ <i data-lucide="users"></i> <span id="i18n-section-team"></span>
118
+ <span id="team-toggle" class="toggle-icon collapsed"><i data-lucide="chevron-down"></i></span>
119
+ </div>
120
+ <div id="team-tab-content" class="section-content hidden">
121
+ <div class="empty-state" id="i18n-empty-team"></div>
122
+ </div>
123
+ </div>
124
+
109
125
  <div class="focus-strip attention-strip" aria-label="Attention indicators">
110
126
  <div class="focus-chip focus-now">
111
127
  <div class="focus-copy">
@@ -152,18 +168,12 @@
152
168
  <div style="margin-top: 0.75rem;" id="git-login-instructions"></div>
153
169
  </div>
154
170
 
155
- <div class="section" id="pending-reviews-section">
171
+ <div class="section hidden" id="pending-reviews-section">
156
172
  <div class="section-header">
157
173
  <i data-lucide="hourglass"></i> <span id="i18n-section-pending-reviews"></span>
158
174
  <span id="pending-reviews-count" class="badge-count hidden">0</span>
159
175
  </div>
160
- <div id="pending-reviews" class="section-content">
161
- <div class="heartbeat-empty-state" id="pending-reviews-empty-state">
162
- <span class="heartbeat-label">// STANDBY</span>
163
- <span class="heartbeat-message" id="i18n-empty-pending-reviews">No reviews waiting for confirmation</span>
164
- <div class="heartbeat-line-container" id="heartbeat-line" aria-hidden="true"></div>
165
- </div>
166
- </div>
176
+ <div id="pending-reviews" class="section-content"></div>
167
177
  </div>
168
178
 
169
179
  <div id="logs-section" class="section hidden">
@@ -176,73 +186,12 @@
176
186
  </div>
177
187
  </div>
178
188
 
179
- <div id="stats-section" class="section hidden">
180
- <div class="section-header clickable" onclick="toggleStats()" role="button" tabindex="0" onkeydown="activateOnKeydown(event)">
181
- <i data-lucide="bar-chart-3"></i> <span id="i18n-section-stats"></span>
182
- <span id="stats-toggle" class="toggle-icon collapsed"><i data-lucide="chevron-down"></i></span>
183
- <button id="recalculate-btn" class="btn btn-sm btn-secondary" onclick="event.stopPropagation(); recalculateStats()" style="margin-left: auto; font-size: 0.75rem; padding: 2px 8px;">
184
- <i data-lucide="refresh-cw" style="width: 12px; height: 12px;"></i>
185
- <span id="recalculate-label"></span>
186
- </button>
187
- <span id="backfill-progress" class="badge-count hidden" style="margin-left: 4px;"></span>
188
- </div>
189
- <div id="project-stats" class="section-content stats-grid hidden">
190
- <div class="empty-state" id="i18n-empty-stats"></div>
191
- </div>
192
- </div>
193
-
194
- <div id="team-section" class="section hidden">
195
- <div class="section-header clickable" onclick="toggleTeamSection()" role="button" tabindex="0" onkeydown="activateOnKeydown(event)">
196
- <i data-lucide="users"></i> <span id="i18n-section-team"></span>
197
- <span id="team-toggle" class="toggle-icon collapsed"><i data-lucide="chevron-down"></i></span>
198
- </div>
199
- <div id="team-tab-content" class="section-content hidden">
200
- <div class="empty-state" id="i18n-empty-team"></div>
201
- </div>
202
- </div>
203
-
204
- <div id="claude-economics-section" class="section">
205
- <div class="section-header clickable" onclick="toggleSection('claude-economics-section')" role="button" tabindex="0" onkeydown="activateOnKeydown(event)">
206
- <i data-lucide="circuit-board"></i> <span id="i18n-section-claude-economics"></span>
207
- <span class="section-toggle collapsed"><i data-lucide="chevron-down"></i></span>
208
- </div>
209
- <div class="section-content economics-content hidden">
210
- <div class="economics-panel">
211
- <div class="economics-panel-header">
212
- <span class="economics-panel-title" id="i18n-economics-token-usage"></span>
213
- </div>
214
- <div class="economics-panel-body" id="token-usage-content">
215
- <div class="empty-state">Loading…</div>
216
- </div>
217
- </div>
218
-
219
- <div class="economics-panel">
220
- <div class="economics-panel-header">
221
- <span class="economics-panel-title" id="i18n-economics-monthly-budget"></span>
222
- </div>
223
- <div class="economics-panel-body">
224
- <div id="budget-tile">
225
- <div class="empty-state">Loading…</div>
226
- </div>
227
- <div class="budget-slider-row">
228
- <label for="budget-slider" id="budget-slider-label">Cap: <span id="budget-slider-value">$200</span></label>
229
- <input type="range" id="budget-slider" min="0" max="600" step="10" value="200" />
230
- <button id="budget-slider-submit" type="button">Apply</button>
231
- <span id="budget-slider-status" class="budget-slider-status"></span>
232
- </div>
233
- </div>
234
- </div>
235
- </div>
236
- </div>
237
-
238
- <div class="section" id="active-reviews-section">
189
+ <div class="section hidden" id="active-reviews-section">
239
190
  <div class="section-header">
240
191
  <i data-lucide="file-search"></i> <span id="i18n-section-active-reviews"></span>
241
192
  <span id="active-reviews-count" class="badge-count hidden">0</span>
242
193
  </div>
243
- <div id="active-reviews" class="section-content">
244
- <div class="empty-state" id="i18n-empty-active-reviews"></div>
245
- </div>
194
+ <div id="active-reviews" class="section-content"></div>
246
195
  </div>
247
196
 
248
197
  <div class="section hidden" id="active-followups-section">
@@ -317,6 +266,17 @@
317
266
  </div>
318
267
  </div>
319
268
 
269
+ <div id="mark-merged-modal" class="modal-overlay hidden" onclick="closeMarkMergedModal(event)">
270
+ <div class="modal-content" onclick="event.stopPropagation()">
271
+ <div class="modal-title" id="mark-merged-modal-title"></div>
272
+ <div class="modal-message" id="i18n-mark-merged-modal-message"></div>
273
+ <div class="modal-actions">
274
+ <button class="btn-modal-back" onclick="closeMarkMergedModal()" id="i18n-mark-merged-modal-back"></button>
275
+ <button class="btn-modal-confirm" id="mark-merged-modal-confirm" onclick="confirmMarkAsMerged()"></button>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
320
280
  <div id="toast-container" class="toast-container"></div>
321
281
 
322
282
  <div id="mr-sheet-overlay" class="sheet-overlay" onclick="closeMrSheet()"></div>
@@ -329,6 +289,61 @@
329
289
  <div id="dev-sheet-content" class="sheet-content"></div>
330
290
  </div>
331
291
 
292
+ <div id="economics-sheet-overlay" class="sheet-overlay" onclick="closeEconomicsSheet()"></div>
293
+ <div id="economics-sheet" class="sheet-panel">
294
+ <div id="economics-sheet-content" class="sheet-content">
295
+ <button class="sheet-close" onclick="closeEconomicsSheet()"><i data-lucide="x"></i></button>
296
+ <div class="sheet-mr-header">
297
+ <div class="sheet-mr-title"><i data-lucide="circuit-board"></i> <span id="i18n-section-claude-economics"></span></div>
298
+ </div>
299
+ <div class="economics-panel">
300
+ <div class="economics-panel-header">
301
+ <span class="economics-panel-title" id="i18n-economics-token-usage"></span>
302
+ </div>
303
+ <div class="economics-panel-body" id="token-usage-content">
304
+ <div class="empty-state">Loading…</div>
305
+ </div>
306
+ </div>
307
+
308
+ <div class="economics-panel">
309
+ <div class="economics-panel-header">
310
+ <span class="economics-panel-title" id="i18n-economics-monthly-budget"></span>
311
+ </div>
312
+ <div class="economics-panel-body">
313
+ <div id="budget-tile">
314
+ <div class="empty-state">Loading…</div>
315
+ </div>
316
+ <div class="budget-slider-row">
317
+ <label for="budget-slider" id="budget-slider-label">Cap: <span id="budget-slider-value">$200</span></label>
318
+ <input type="range" id="budget-slider" min="0" max="600" step="10" value="200" />
319
+ <button id="budget-slider-submit" type="button">Apply</button>
320
+ <span id="budget-slider-status" class="budget-slider-status"></span>
321
+ </div>
322
+ </div>
323
+ </div>
324
+ </div>
325
+ </div>
326
+
327
+ <div id="stats-sheet-overlay" class="sheet-overlay" onclick="closeStatsSheet()"></div>
328
+ <div id="stats-sheet" class="sheet-panel">
329
+ <div id="stats-sheet-content" class="sheet-content">
330
+ <button class="sheet-close" onclick="closeStatsSheet()"><i data-lucide="x"></i></button>
331
+ <div class="sheet-mr-header">
332
+ <div class="sheet-mr-title">
333
+ <i data-lucide="bar-chart-3"></i> <span id="i18n-section-stats"></span>
334
+ <button id="recalculate-btn" class="btn btn-sm btn-secondary" onclick="recalculateStats()" style="margin-left: auto; font-size: 0.75rem; padding: 2px 8px;">
335
+ <i data-lucide="refresh-cw" style="width: 12px; height: 12px;"></i>
336
+ <span id="recalculate-label"></span>
337
+ </button>
338
+ <span id="backfill-progress" class="badge-count hidden" style="margin-left: 4px;"></span>
339
+ </div>
340
+ </div>
341
+ <div id="project-stats" class="stats-grid">
342
+ <div class="empty-state" id="i18n-empty-stats"></div>
343
+ </div>
344
+ </div>
345
+ </div>
346
+
332
347
  <dialog id="settings-modal" class="settings-modal" aria-labelledby="settings-modal-title"></dialog>
333
348
 
334
349
  <!-- UI language select — kept in DOM for JS wiring; visual control lives in settings modal -->
@@ -363,6 +378,7 @@
363
378
  import { renderOverviewHtml } from './modules/overview.js';
364
379
  import { getDesktopNotificationPayload, shouldNotifyDesktop } from './modules/desktopNotifications.js';
365
380
  import { getLoadingPresentation, getQuietRefreshSectionIdentifiers } from './modules/loading.js';
381
+ import { shouldHideActiveReviewsSection, shouldHidePendingReviewsSection } from './modules/sectionVisibility.js';
366
382
  import { computeCardCounters, extractGithubSlug } from './modules/cardCounters.js';
367
383
  import { collectReviewNotifications, createReviewNotificationState } from './modules/notifications.js';
368
384
  import { resolveReviewAssigneeDisplay } from './modules/assignee.js';
@@ -414,7 +430,6 @@
414
430
  let currentData = { activeReviews: [], recentReviews: [], logs: [], reviewFiles: [], pendingFix: [], pendingApproval: [], pendingReviews: [] };
415
431
  const mrDataStore = new Map();
416
432
  let loadedReviews = {};
417
- let statsCollapsed = true;
418
433
  let currentStatsReviews = [];
419
434
  let currentDevFilter = 'all';
420
435
  let focusStripCompact = false;
@@ -422,7 +437,7 @@
422
437
  let currentInsightsData = null;
423
438
  const loadingState = { status: 0, reviewFiles: 0, stats: 0, mrTracking: 0 };
424
439
  let hasLoadedStatusOnce = false;
425
- const secondarySections = ['active-followups-section', 'pending-approval-section', 'completed-reviews-section', 'claude-economics-section'];
440
+ const secondarySections = ['active-followups-section', 'pending-approval-section', 'completed-reviews-section'];
426
441
  const sectionExpandedState = Object.fromEntries(secondarySections.map((sectionIdentifier) => [sectionIdentifier, false]));
427
442
  const quietRefreshSections = [
428
443
  'active-reviews-section',
@@ -853,12 +868,12 @@
853
868
  const activeReviewsEl = document.getElementById('active-reviews');
854
869
  const activeReviewsCount = document.getElementById('active-reviews-count');
855
870
 
856
- activeReviewsSection.classList.remove('hidden');
857
- if (reviews.length === 0) {
858
- const showInitialLoadingState = !hasLoadedStatusOnce && loadingState.status > 0;
859
- activeReviewsEl.innerHTML = `<div class="empty-state">${showInitialLoadingState ? t('loading.section') : t('empty.activeReviews')}</div>`;
871
+ if (shouldHideActiveReviewsSection({ activeReviews: currentData.activeReviews })) {
872
+ activeReviewsSection.classList.add('hidden');
873
+ activeReviewsEl.innerHTML = '';
860
874
  activeReviewsCount.classList.add('hidden');
861
875
  } else {
876
+ activeReviewsSection.classList.remove('hidden');
862
877
  activeReviewsEl.innerHTML = reviews.map(r => renderReview(r, true)).join('');
863
878
  activeReviewsCount.textContent = reviews.length;
864
879
  activeReviewsCount.classList.remove('hidden');
@@ -1319,20 +1334,6 @@
1319
1334
  updateLogs();
1320
1335
  }
1321
1336
 
1322
- function toggleStats() {
1323
- statsCollapsed = !statsCollapsed;
1324
- const content = document.getElementById('project-stats');
1325
- const toggle = document.getElementById('stats-toggle');
1326
-
1327
- if (statsCollapsed) {
1328
- content.classList.add('hidden');
1329
- toggle.classList.add('collapsed');
1330
- } else {
1331
- content.classList.remove('hidden');
1332
- toggle.classList.remove('collapsed');
1333
- }
1334
- }
1335
-
1336
1337
  function toggleTeamSection() {
1337
1338
  teamCollapsed = !teamCollapsed;
1338
1339
  const content = document.getElementById('team-tab-content');
@@ -1533,6 +1534,37 @@
1533
1534
  document.body.style.overflow = '';
1534
1535
  }
1535
1536
 
1537
+ function openEconomicsSheet() {
1538
+ document.getElementById('economics-sheet-overlay').classList.add('open');
1539
+ document.getElementById('economics-sheet').classList.add('open');
1540
+ document.body.style.overflow = 'hidden';
1541
+ refreshTokenUsageTile();
1542
+ refreshBudgetTile();
1543
+ initBudgetSlider();
1544
+ refreshIcons();
1545
+ }
1546
+
1547
+ function closeEconomicsSheet() {
1548
+ document.getElementById('economics-sheet-overlay').classList.remove('open');
1549
+ document.getElementById('economics-sheet').classList.remove('open');
1550
+ document.body.style.overflow = '';
1551
+ }
1552
+
1553
+ function openStatsSheet() {
1554
+ if (!currentProjectPath) return;
1555
+ document.getElementById('stats-sheet-overlay').classList.add('open');
1556
+ document.getElementById('stats-sheet').classList.add('open');
1557
+ document.body.style.overflow = 'hidden';
1558
+ fetchProjectStats();
1559
+ refreshIcons();
1560
+ }
1561
+
1562
+ function closeStatsSheet() {
1563
+ document.getElementById('stats-sheet-overlay').classList.remove('open');
1564
+ document.getElementById('stats-sheet').classList.remove('open');
1565
+ document.body.style.overflow = '';
1566
+ }
1567
+
1536
1568
  function exportInsightsPdf() {
1537
1569
  if (!currentInsightsData || currentInsightsData.isEmpty) return;
1538
1570
 
@@ -1620,13 +1652,16 @@
1620
1652
  const autoFollowupChecked = mr.autoFollowup !== false ? 'checked' : '';
1621
1653
  const showFollowupActions = type === 'pending-fix' ||
1622
1654
  (type === 'pending-approval' && mr.totalWarnings > 0);
1655
+ const markMergedBtn = type === 'pending-fix'
1656
+ ? `<button class="btn-action" onclick="showMarkMergedModal('${encodedMrId}', ${mr.mrNumber})"><i data-lucide="git-merge"></i> ${t('button.markAsMerged')}</button>`
1657
+ : '';
1623
1658
 
1624
1659
  const actions = showFollowupActions
1625
1660
  ? `<label class="auto-followup-toggle" onclick="event.stopPropagation()" title="${t('button.autoFollowup')}">
1626
1661
  <input type="checkbox" ${autoFollowupChecked} onchange="toggleAutoFollowup('${encodedMrId}', this.checked)">
1627
1662
  ${t('button.autoFollowup')}
1628
1663
  </label>
1629
- <button class="btn-action" onclick="triggerFollowup('${encodedMrId}')"><i data-lucide="refresh-cw"></i> ${t('button.followup')}</button>${openBtn}`
1664
+ <button class="btn-action" onclick="triggerFollowup('${encodedMrId}')"><i data-lucide="refresh-cw"></i> ${t('button.followup')}</button>${markMergedBtn}${openBtn}`
1630
1665
  : openBtn;
1631
1666
 
1632
1667
  const statsBadges = [];
@@ -1908,6 +1943,15 @@
1908
1943
  const countBadge = document.getElementById('pending-reviews-count');
1909
1944
  if (!container || !countBadge || !section) return;
1910
1945
 
1946
+ if (shouldHidePendingReviewsSection({ pendingReviews: currentData.pendingReviews })) {
1947
+ section.classList.add('hidden');
1948
+ container.innerHTML = '';
1949
+ countBadge.classList.add('hidden');
1950
+ countBadge.textContent = '0';
1951
+ return;
1952
+ }
1953
+ section.classList.remove('hidden');
1954
+
1911
1955
  const model = buildPendingReviewsModel(currentData.pendingReviews || []);
1912
1956
  container.innerHTML = renderPendingReviewsHtml(model);
1913
1957
 
@@ -2384,8 +2428,12 @@
2384
2428
  fetchTeamInsights();
2385
2429
  refreshTokenUsageTile();
2386
2430
 
2387
- document.getElementById('stats-section').classList.remove('hidden');
2388
2431
  document.getElementById('team-section').classList.remove('hidden');
2432
+ const statsSheetButton = document.getElementById('open-stats-sheet-btn');
2433
+ if (statsSheetButton) {
2434
+ statsSheetButton.removeAttribute('disabled');
2435
+ statsSheetButton.setAttribute('aria-disabled', 'false');
2436
+ }
2389
2437
 
2390
2438
  const platformIcon = activePlatform === 'gitlab' ? '<i data-lucide="gitlab"></i> GitLab' : '<i data-lucide="github"></i> GitHub';
2391
2439
  info.innerHTML = `
@@ -2500,6 +2548,11 @@
2500
2548
  renderDashboardTabs();
2501
2549
  renderCardCounters();
2502
2550
  syncSettingsButtonVisibility();
2551
+ const statsSheetButton = document.getElementById('open-stats-sheet-btn');
2552
+ if (statsSheetButton) {
2553
+ statsSheetButton.setAttribute('disabled', '');
2554
+ statsSheetButton.setAttribute('aria-disabled', 'true');
2555
+ }
2503
2556
  refreshOverviewSection();
2504
2557
  }
2505
2558
 
@@ -2932,6 +2985,56 @@
2932
2985
  }
2933
2986
  }
2934
2987
 
2988
+ let markMergedModalMrId = null;
2989
+
2990
+ function showMarkMergedModal(encodedMrId, mrNumber) {
2991
+ markMergedModalMrId = safeDecodeURIComponent(encodedMrId);
2992
+ document.getElementById('mark-merged-modal-title').textContent = t('modal.markMerged.title', { label: getMrLabel(), number: mrNumber });
2993
+ const modal = document.getElementById('mark-merged-modal');
2994
+ modal.classList.remove('hidden');
2995
+ requestAnimationFrame(() => modal.classList.add('visible'));
2996
+ }
2997
+
2998
+ function closeMarkMergedModal(event) {
2999
+ if (event && event.target !== event.currentTarget) return;
3000
+ const modal = document.getElementById('mark-merged-modal');
3001
+ modal.classList.remove('visible');
3002
+ setTimeout(() => modal.classList.add('hidden'), 200);
3003
+ markMergedModalMrId = null;
3004
+ }
3005
+
3006
+ async function confirmMarkAsMerged() {
3007
+ if (!markMergedModalMrId) return;
3008
+ trackUsefulAction('markAsMerged');
3009
+
3010
+ const mrId = markMergedModalMrId;
3011
+ closeMarkMergedModal();
3012
+
3013
+ try {
3014
+ const response = await fetch(`${API_URL}/api/mr-tracking/mark-as-merged`, {
3015
+ method: 'POST',
3016
+ headers: { 'Content-Type': 'application/json' },
3017
+ body: JSON.stringify({
3018
+ mrId,
3019
+ projectPath: currentProjectPath || undefined,
3020
+ }),
3021
+ });
3022
+ const data = await response.json();
3023
+
3024
+ if (data.success) {
3025
+ const card = document.querySelector(`.mr-item-accordion[data-mr-id="${mrId}"]`);
3026
+ if (card) card.remove();
3027
+ showToast(t('success.markedAsMerged'), 'success');
3028
+ fetchStatus();
3029
+ } else {
3030
+ showToast(data.error || t('error.markAsMerged'), 'error');
3031
+ }
3032
+ } catch (error) {
3033
+ console.error('Error marking as merged:', error);
3034
+ showToast(t('error.markAsMerged'), 'error');
3035
+ }
3036
+ }
3037
+
2935
3038
  function showToast(message, type) {
2936
3039
  const container = document.getElementById('toast-container');
2937
3040
  const iconName = type === 'success' ? 'check-circle' : type === 'info' ? 'info' : 'alert-circle';
@@ -3073,12 +3176,6 @@
3073
3176
  const emptyStats = document.getElementById('i18n-empty-stats');
3074
3177
  if (emptyStats) emptyStats.textContent = t('empty.stats');
3075
3178
 
3076
- const emptyPendingReviews = document.getElementById('i18n-empty-pending-reviews');
3077
- if (emptyPendingReviews) emptyPendingReviews.textContent = t('empty.pendingReviews');
3078
-
3079
- const emptyActiveReviews = document.getElementById('i18n-empty-active-reviews');
3080
- if (emptyActiveReviews) emptyActiveReviews.textContent = t('empty.activeReviews');
3081
-
3082
3179
  const emptyActiveFollowups = document.getElementById('i18n-empty-active-followups');
3083
3180
  if (emptyActiveFollowups) emptyActiveFollowups.textContent = t('empty.activeFollowups');
3084
3181
 
@@ -3111,6 +3208,15 @@
3111
3208
 
3112
3209
  const modalConfirm = document.getElementById('cancel-modal-confirm');
3113
3210
  if (modalConfirm) modalConfirm.textContent = t('modal.confirm');
3211
+
3212
+ const markMergedModalMessage = document.getElementById('i18n-mark-merged-modal-message');
3213
+ if (markMergedModalMessage) markMergedModalMessage.textContent = t('modal.markMerged.message');
3214
+
3215
+ const markMergedModalBack = document.getElementById('i18n-mark-merged-modal-back');
3216
+ if (markMergedModalBack) markMergedModalBack.textContent = t('modal.markMerged.back');
3217
+
3218
+ const markMergedModalConfirm = document.getElementById('mark-merged-modal-confirm');
3219
+ if (markMergedModalConfirm) markMergedModalConfirm.textContent = t('modal.markMerged.confirm');
3114
3220
  }
3115
3221
 
3116
3222
  async function checkForUpdates() {
@@ -3265,7 +3371,6 @@
3265
3371
  window.toggleCollapsibleList = toggleCollapsibleList;
3266
3372
  window.checkClaudeStatus = checkClaudeStatus;
3267
3373
  window.toggleLogs = toggleLogs;
3268
- window.toggleStats = toggleStats;
3269
3374
  window.changeModel = changeModel;
3270
3375
  window.changeLanguage = changeLanguage;
3271
3376
  window.toggleReviewAccordion = toggleReviewAccordion;
@@ -3279,6 +3384,9 @@
3279
3384
  window.showCancelModal = showCancelModal;
3280
3385
  window.closeCancelModal = closeCancelModal;
3281
3386
  window.confirmCancelReview = confirmCancelReview;
3387
+ window.showMarkMergedModal = showMarkMergedModal;
3388
+ window.closeMarkMergedModal = closeMarkMergedModal;
3389
+ window.confirmMarkAsMerged = confirmMarkAsMerged;
3282
3390
  window.toggleFocusStripMode = toggleFocusStripMode;
3283
3391
  window.toggleSection = toggleSection;
3284
3392
  window.onUsefulLinkAction = onUsefulLinkAction;
@@ -3288,6 +3396,10 @@
3288
3396
  window.closeMrSheet = closeMrSheet;
3289
3397
  window.openDevSheet = openDevSheet;
3290
3398
  window.closeDevSheet = closeDevSheet;
3399
+ window.openEconomicsSheet = openEconomicsSheet;
3400
+ window.closeEconomicsSheet = closeEconomicsSheet;
3401
+ window.openStatsSheet = openStatsSheet;
3402
+ window.closeStatsSheet = closeStatsSheet;
3291
3403
  window.generateAiInsights = generateAiInsights;
3292
3404
  window.exportInsightsPdf = exportInsightsPdf;
3293
3405
  window.toggleTeamAnalysis = toggleTeamAnalysis;
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/i18n.js"],"names":[],"mappings":"AAw1BA,6BAA6B;AAC7B,+BADc,IAAI,GAAG,IAAI,CAGxB;AAED,oCAAoC;AACpC,sCADY,IAAI,GAAG,IAAI,QAGtB;AAED;;;;GAIG;AACH,uBAJW,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC7B,MAAM,CAUlB"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/i18n.js"],"names":[],"mappings":"AAk2BA,6BAA6B;AAC7B,+BADc,IAAI,GAAG,IAAI,CAGxB;AAED,oCAAoC;AACpC,sCADY,IAAI,GAAG,IAAI,QAGtB;AAED;;;;GAIG;AACH,uBAJW,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC7B,MAAM,CAUlB"}
@@ -170,8 +170,6 @@ const translations = {
170
170
  'empty.logs': 'No logs',
171
171
  'empty.stats': 'Load a project to see stats',
172
172
  'empty.statsNoData': 'No statistics available',
173
- 'empty.pendingReviews': 'No reviews waiting for confirmation',
174
- 'empty.activeReviews': 'No active reviews',
175
173
  'empty.activeFollowups': 'No follow-up in progress',
176
174
  'empty.pendingFix': 'No MR pending fix',
177
175
  'empty.pendingApproval': 'No MR pending approval',
@@ -225,6 +223,7 @@ const translations = {
225
223
  'button.autoFollowup': 'Auto follow-up',
226
224
  'button.delete': 'Delete',
227
225
  'button.syncThreads': 'Sync GitLab threads',
226
+ 'button.markAsMerged': 'Mark as merged',
228
227
 
229
228
  // MR details
230
229
  'mr.threads.open': '{{count}} open',
@@ -248,6 +247,10 @@ const translations = {
248
247
  'modal.cancel.message': 'This action is irreversible. The review will be stopped immediately.',
249
248
  'modal.back': 'Go back',
250
249
  'modal.confirm': 'Confirm',
250
+ 'modal.markMerged.title': 'Mark {{label}} !{{number}} as merged?',
251
+ 'modal.markMerged.message': 'This manually marks the MR as merged. The card will leave "Corrections requises".',
252
+ 'modal.markMerged.back': 'Go back',
253
+ 'modal.markMerged.confirm': 'Mark as merged',
251
254
 
252
255
  // Confirm dialogs
253
256
  'confirm.deleteReview': 'Delete {{filename}}?',
@@ -257,6 +260,7 @@ const translations = {
257
260
  // Success
258
261
  'success.reviewCancelled': 'Review cancelled',
259
262
  'success.reviewAlreadyCompleted': 'This review is already completed',
263
+ 'success.markedAsMerged': 'MR marked as merged',
260
264
 
261
265
  // Errors
262
266
  'error.loading': 'Loading error',
@@ -269,6 +273,7 @@ const translations = {
269
273
  'error.approveMr': 'Error approving',
270
274
  'error.syncThreads': 'Error syncing threads',
271
275
  'error.cancelReview': 'Error cancelling review',
276
+ 'error.markAsMerged': 'Error marking as merged',
272
277
  'error.selectOrEnterPath': 'Select or enter a path',
273
278
  'error.projectNotLoaded': 'Load a project first',
274
279
 
@@ -595,8 +600,6 @@ const translations = {
595
600
  'empty.logs': 'Aucun log',
596
601
  'empty.stats': 'Charger un projet pour voir les stats',
597
602
  'empty.statsNoData': 'Aucune statistique disponible',
598
- 'empty.pendingReviews': 'Aucune review en attente de confirmation',
599
- 'empty.activeReviews': 'Aucune review en cours',
600
603
  'empty.activeFollowups': 'Aucun follow-up en cours',
601
604
  'empty.pendingFix': 'Aucune MR en attente de correctif',
602
605
  'empty.pendingApproval': "Aucune MR en attente d'approbation",
@@ -650,6 +653,7 @@ const translations = {
650
653
  'button.autoFollowup': 'Auto follow-up',
651
654
  'button.delete': 'Supprimer',
652
655
  'button.syncThreads': 'Synchroniser les threads GitLab',
656
+ 'button.markAsMerged': 'Marquer comme mergée',
653
657
 
654
658
  // MR details
655
659
  'mr.threads.open': '{{count}} ouvert(s)',
@@ -673,6 +677,10 @@ const translations = {
673
677
  'modal.cancel.message': 'Cette action est irréversible. La review sera arrêtée immédiatement.',
674
678
  'modal.back': 'Revenir',
675
679
  'modal.confirm': 'Confirmer',
680
+ 'modal.markMerged.title': 'Marquer la {{label}} !{{number}} comme mergée ?',
681
+ 'modal.markMerged.message': 'Marque manuellement la MR comme mergée. La carte quittera "Corrections requises".',
682
+ 'modal.markMerged.back': 'Revenir',
683
+ 'modal.markMerged.confirm': 'Marquer comme mergée',
676
684
 
677
685
  // Confirm dialogs
678
686
  'confirm.deleteReview': 'Supprimer {{filename}} ?',
@@ -682,6 +690,7 @@ const translations = {
682
690
  // Success
683
691
  'success.reviewCancelled': 'Review annulée',
684
692
  'success.reviewAlreadyCompleted': 'Cette review est déjà terminée',
693
+ 'success.markedAsMerged': 'MR marquée comme mergée',
685
694
 
686
695
  // Errors
687
696
  'error.loading': 'Erreur de chargement',
@@ -694,6 +703,7 @@ const translations = {
694
703
  'error.approveMr': "Erreur lors de l'approbation",
695
704
  'error.syncThreads': 'Erreur lors de la synchronisation des threads',
696
705
  'error.cancelReview': "Erreur lors de l'annulation",
706
+ 'error.markAsMerged': 'Erreur lors du marquage',
697
707
  'error.selectOrEnterPath': 'Sélectionnez ou entrez un chemin',
698
708
  'error.projectNotLoaded': "Charger un projet d'abord",
699
709