reviewflow 3.24.2 → 3.26.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 (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/dashboard/index.html +248 -105
  3. package/dist/dashboard/modules/constants.d.ts +1 -0
  4. package/dist/dashboard/modules/constants.d.ts.map +1 -1
  5. package/dist/dashboard/modules/constants.js +1 -0
  6. package/dist/dashboard/modules/constants.js.map +1 -1
  7. package/dist/dashboard/modules/i18n.d.ts.map +1 -1
  8. package/dist/dashboard/modules/i18n.js +14 -4
  9. package/dist/dashboard/modules/i18n.js.map +1 -1
  10. package/dist/dashboard/modules/sectionVisibility.d.ts +17 -0
  11. package/dist/dashboard/modules/sectionVisibility.d.ts.map +1 -0
  12. package/dist/dashboard/modules/sectionVisibility.js +20 -0
  13. package/dist/dashboard/modules/sectionVisibility.js.map +1 -0
  14. package/dist/dashboard/styles.css +27 -0
  15. package/dist/frameworks/settings/runtimeSettings.d.ts +36 -11
  16. package/dist/frameworks/settings/runtimeSettings.d.ts.map +1 -1
  17. package/dist/frameworks/settings/runtimeSettings.js +98 -9
  18. package/dist/frameworks/settings/runtimeSettings.js.map +1 -1
  19. package/dist/main/server.d.ts.map +1 -1
  20. package/dist/main/server.js +4 -0
  21. package/dist/main/server.js.map +1 -1
  22. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.d.ts.map +1 -1
  23. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.js +7 -11
  24. package/dist/modules/cli-configuration/interface-adapters/controllers/http/settings.routes.js.map +1 -1
  25. package/dist/modules/review-execution/entities/reviewRequest/reviewRequestState.valueObject.js +1 -1
  26. package/dist/modules/review-execution/entities/reviewRequest/reviewRequestState.valueObject.js.map +1 -1
  27. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.d.ts.map +1 -1
  28. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.js +42 -0
  29. package/dist/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.js.map +1 -1
  30. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.d.ts +6 -0
  31. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.d.ts.map +1 -1
  32. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.js +7 -0
  33. package/dist/modules/tracking/usecases/tracking/transitionState.usecase.js.map +1 -1
  34. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.d.ts +19 -0
  35. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.d.ts.map +1 -0
  36. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.js +157 -0
  37. package/dist/tests/acceptance/182-mark-pending-fix-as-merged.acceptance.test.js.map +1 -0
  38. package/dist/tests/units/dashboard/dashboardLayout.test.d.ts +13 -0
  39. package/dist/tests/units/dashboard/dashboardLayout.test.d.ts.map +1 -0
  40. package/dist/tests/units/dashboard/dashboardLayout.test.js +247 -0
  41. package/dist/tests/units/dashboard/dashboardLayout.test.js.map +1 -0
  42. package/dist/tests/units/dashboard/modules/sectionVisibility.test.d.ts +2 -0
  43. package/dist/tests/units/dashboard/modules/sectionVisibility.test.d.ts.map +1 -0
  44. package/dist/tests/units/dashboard/modules/sectionVisibility.test.js +58 -0
  45. package/dist/tests/units/dashboard/modules/sectionVisibility.test.js.map +1 -0
  46. package/dist/tests/units/entities/reviewRequestState.valueObject.test.js +5 -0
  47. package/dist/tests/units/entities/reviewRequestState.valueObject.test.js.map +1 -1
  48. package/dist/tests/units/frameworks/settings/runtimeSettings.test.js +123 -16
  49. package/dist/tests/units/frameworks/settings/runtimeSettings.test.js.map +1 -1
  50. package/dist/tests/units/interface-adapters/controllers/http/settings.routes.test.js +1 -1
  51. package/dist/tests/units/interface-adapters/controllers/http/settings.routes.test.js.map +1 -1
  52. package/dist/tests/units/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.test.js +37 -0
  53. package/dist/tests/units/modules/tracking/interface-adapters/controllers/http/mrTracking.routes.test.js.map +1 -1
  54. package/dist/tests/units/usecases/tracking/transitionState.usecase.test.js +36 -0
  55. package/dist/tests/units/usecases/tracking/transitionState.usecase.test.js.map +1 -1
  56. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ 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.26.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.25.0...reviewflow-v3.26.0) (2026-05-27)
9
+
10
+
11
+ ### Added
12
+
13
+ * **dashboard:** make now lane collapsible ([#225](https://github.com/DGouron/review-flow/issues/225)) ([7c895ba](https://github.com/DGouron/review-flow/commit/7c895bacb79637b0116198a2fdc74d6cf75f9fe5))
14
+
15
+ ## [3.25.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.24.2...reviewflow-v3.25.0) (2026-05-27)
16
+
17
+
18
+ ### Added
19
+
20
+ * **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))
21
+ * **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))
22
+
23
+
24
+ ### Fixed
25
+
26
+ * **settings:** persist runtime settings across restarts ([#221](https://github.com/DGouron/review-flow/issues/221)) ([f8a71b8](https://github.com/DGouron/review-flow/commit/f8a71b8dbcf3ae16557b219131cdc8f08a4e9d65))
27
+
8
28
  ## [3.24.2](https://github.com/DGouron/review-flow/compare/reviewflow-v3.24.1...reviewflow-v3.24.2) (2026-05-27)
9
29
 
10
30
 
@@ -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 -->
@@ -357,12 +372,13 @@
357
372
  } from './modules/animations.js';
358
373
  import { escapeHtml, markdownToHtml, sanitizeHttpUrl } from './modules/html.js';
359
374
  import { getAgentIcon, icon, refreshIcons } from './modules/icons.js';
360
- import { MAX_RECONNECT_ATTEMPTS, RECONNECT_DELAY, STORAGE_KEY_CURRENT, STORAGE_KEY_FOCUS_STRIP_MODE, QUALITY_TARGET_SCORE } from './modules/constants.js';
375
+ import { MAX_RECONNECT_ATTEMPTS, RECONNECT_DELAY, STORAGE_KEY_CURRENT, STORAGE_KEY_FOCUS_STRIP_MODE, STORAGE_KEY_NOW_LANE_COLLAPSED, QUALITY_TARGET_SCORE } from './modules/constants.js';
361
376
  import { buildTabBarModel, renderTabBarHtml, readActiveTab, writeActiveTab } from './modules/tabBar.js';
362
377
  import { buildManagePanelModel, renderManagePanelHtml, buildOptimisticAddedRow, validateLocalPathInput } from './modules/managePanel.js';
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,8 +437,9 @@
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]));
442
+ let nowLaneCollapsed = false;
427
443
  const quietRefreshSections = [
428
444
  'active-reviews-section',
429
445
  'active-followups-section',
@@ -492,6 +508,30 @@
492
508
  refreshIcons();
493
509
  }
494
510
 
511
+ function applyNowLaneCollapsed() {
512
+ const lane = document.getElementById('queue-lane-now');
513
+ if (!lane) return;
514
+ const body = document.getElementById('queue-lane-now-body');
515
+ const header = lane.querySelector('.queue-lane-header');
516
+ const toggle = lane.querySelector('.queue-lane-toggle');
517
+ if (body) body.classList.toggle('hidden', nowLaneCollapsed);
518
+ if (toggle) toggle.classList.toggle('collapsed', nowLaneCollapsed);
519
+ if (header) header.setAttribute('aria-expanded', nowLaneCollapsed ? 'false' : 'true');
520
+ }
521
+
522
+ function loadNowLaneCollapsed() {
523
+ const value = localStorage.getItem(STORAGE_KEY_NOW_LANE_COLLAPSED);
524
+ nowLaneCollapsed = value === 'collapsed';
525
+ applyNowLaneCollapsed();
526
+ }
527
+
528
+ function toggleNowLane() {
529
+ nowLaneCollapsed = !nowLaneCollapsed;
530
+ localStorage.setItem(STORAGE_KEY_NOW_LANE_COLLAPSED, nowLaneCollapsed ? 'collapsed' : 'expanded');
531
+ applyNowLaneCollapsed();
532
+ refreshIcons();
533
+ }
534
+
495
535
  function setLoadingFlag(source, isLoading) {
496
536
  if (!(source in loadingState)) return;
497
537
  const nextValue = isLoading
@@ -853,12 +893,12 @@
853
893
  const activeReviewsEl = document.getElementById('active-reviews');
854
894
  const activeReviewsCount = document.getElementById('active-reviews-count');
855
895
 
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>`;
896
+ if (shouldHideActiveReviewsSection({ activeReviews: currentData.activeReviews })) {
897
+ activeReviewsSection.classList.add('hidden');
898
+ activeReviewsEl.innerHTML = '';
860
899
  activeReviewsCount.classList.add('hidden');
861
900
  } else {
901
+ activeReviewsSection.classList.remove('hidden');
862
902
  activeReviewsEl.innerHTML = reviews.map(r => renderReview(r, true)).join('');
863
903
  activeReviewsCount.textContent = reviews.length;
864
904
  activeReviewsCount.classList.remove('hidden');
@@ -1319,20 +1359,6 @@
1319
1359
  updateLogs();
1320
1360
  }
1321
1361
 
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
1362
  function toggleTeamSection() {
1337
1363
  teamCollapsed = !teamCollapsed;
1338
1364
  const content = document.getElementById('team-tab-content');
@@ -1533,6 +1559,37 @@
1533
1559
  document.body.style.overflow = '';
1534
1560
  }
1535
1561
 
1562
+ function openEconomicsSheet() {
1563
+ document.getElementById('economics-sheet-overlay').classList.add('open');
1564
+ document.getElementById('economics-sheet').classList.add('open');
1565
+ document.body.style.overflow = 'hidden';
1566
+ refreshTokenUsageTile();
1567
+ refreshBudgetTile();
1568
+ initBudgetSlider();
1569
+ refreshIcons();
1570
+ }
1571
+
1572
+ function closeEconomicsSheet() {
1573
+ document.getElementById('economics-sheet-overlay').classList.remove('open');
1574
+ document.getElementById('economics-sheet').classList.remove('open');
1575
+ document.body.style.overflow = '';
1576
+ }
1577
+
1578
+ function openStatsSheet() {
1579
+ if (!currentProjectPath) return;
1580
+ document.getElementById('stats-sheet-overlay').classList.add('open');
1581
+ document.getElementById('stats-sheet').classList.add('open');
1582
+ document.body.style.overflow = 'hidden';
1583
+ fetchProjectStats();
1584
+ refreshIcons();
1585
+ }
1586
+
1587
+ function closeStatsSheet() {
1588
+ document.getElementById('stats-sheet-overlay').classList.remove('open');
1589
+ document.getElementById('stats-sheet').classList.remove('open');
1590
+ document.body.style.overflow = '';
1591
+ }
1592
+
1536
1593
  function exportInsightsPdf() {
1537
1594
  if (!currentInsightsData || currentInsightsData.isEmpty) return;
1538
1595
 
@@ -1620,13 +1677,16 @@
1620
1677
  const autoFollowupChecked = mr.autoFollowup !== false ? 'checked' : '';
1621
1678
  const showFollowupActions = type === 'pending-fix' ||
1622
1679
  (type === 'pending-approval' && mr.totalWarnings > 0);
1680
+ const markMergedBtn = type === 'pending-fix'
1681
+ ? `<button class="btn-action" onclick="showMarkMergedModal('${encodedMrId}', ${mr.mrNumber})"><i data-lucide="git-merge"></i> ${t('button.markAsMerged')}</button>`
1682
+ : '';
1623
1683
 
1624
1684
  const actions = showFollowupActions
1625
1685
  ? `<label class="auto-followup-toggle" onclick="event.stopPropagation()" title="${t('button.autoFollowup')}">
1626
1686
  <input type="checkbox" ${autoFollowupChecked} onchange="toggleAutoFollowup('${encodedMrId}', this.checked)">
1627
1687
  ${t('button.autoFollowup')}
1628
1688
  </label>
1629
- <button class="btn-action" onclick="triggerFollowup('${encodedMrId}')"><i data-lucide="refresh-cw"></i> ${t('button.followup')}</button>${openBtn}`
1689
+ <button class="btn-action" onclick="triggerFollowup('${encodedMrId}')"><i data-lucide="refresh-cw"></i> ${t('button.followup')}</button>${markMergedBtn}${openBtn}`
1630
1690
  : openBtn;
1631
1691
 
1632
1692
  const statsBadges = [];
@@ -1806,14 +1866,18 @@
1806
1866
  )
1807
1867
  : `<div class="empty-state">${t('queueLane.emptyReadyToApprove')}</div>`;
1808
1868
 
1869
+ const nowLaneCollapsedClass = nowLaneCollapsed ? 'collapsed' : '';
1809
1870
  return `
1810
1871
  <div class="queue-lanes-grid">
1811
- <div class="queue-lane">
1812
- <div class="queue-lane-header">
1872
+ <div class="queue-lane" id="queue-lane-now">
1873
+ <div class="queue-lane-header clickable" onclick="toggleNowLane()" role="button" tabindex="0" aria-expanded="${nowLaneCollapsed ? 'false' : 'true'}" aria-controls="queue-lane-now-body" onkeydown="activateOnKeydown(event)">
1813
1874
  <span class="queue-lane-title">${t('queueLane.now')}</span>
1814
- <span class="queue-lane-count">${queueLanesModel.nowLaneCount}</span>
1875
+ <span class="queue-lane-header-right">
1876
+ <span class="queue-lane-count">${queueLanesModel.nowLaneCount}</span>
1877
+ <span class="queue-lane-toggle ${nowLaneCollapsedClass}"><i data-lucide="chevron-down"></i></span>
1878
+ </span>
1815
1879
  </div>
1816
- <div class="queue-lane-body">${nowLaneContent}</div>
1880
+ <div class="queue-lane-body ${nowLaneCollapsed ? 'hidden' : ''}" id="queue-lane-now-body">${nowLaneContent}</div>
1817
1881
  </div>
1818
1882
  <div class="queue-lane">
1819
1883
  <div class="queue-lane-header">
@@ -1908,6 +1972,15 @@
1908
1972
  const countBadge = document.getElementById('pending-reviews-count');
1909
1973
  if (!container || !countBadge || !section) return;
1910
1974
 
1975
+ if (shouldHidePendingReviewsSection({ pendingReviews: currentData.pendingReviews })) {
1976
+ section.classList.add('hidden');
1977
+ container.innerHTML = '';
1978
+ countBadge.classList.add('hidden');
1979
+ countBadge.textContent = '0';
1980
+ return;
1981
+ }
1982
+ section.classList.remove('hidden');
1983
+
1911
1984
  const model = buildPendingReviewsModel(currentData.pendingReviews || []);
1912
1985
  container.innerHTML = renderPendingReviewsHtml(model);
1913
1986
 
@@ -2384,8 +2457,12 @@
2384
2457
  fetchTeamInsights();
2385
2458
  refreshTokenUsageTile();
2386
2459
 
2387
- document.getElementById('stats-section').classList.remove('hidden');
2388
2460
  document.getElementById('team-section').classList.remove('hidden');
2461
+ const statsSheetButton = document.getElementById('open-stats-sheet-btn');
2462
+ if (statsSheetButton) {
2463
+ statsSheetButton.removeAttribute('disabled');
2464
+ statsSheetButton.setAttribute('aria-disabled', 'false');
2465
+ }
2389
2466
 
2390
2467
  const platformIcon = activePlatform === 'gitlab' ? '<i data-lucide="gitlab"></i> GitLab' : '<i data-lucide="github"></i> GitHub';
2391
2468
  info.innerHTML = `
@@ -2500,6 +2577,11 @@
2500
2577
  renderDashboardTabs();
2501
2578
  renderCardCounters();
2502
2579
  syncSettingsButtonVisibility();
2580
+ const statsSheetButton = document.getElementById('open-stats-sheet-btn');
2581
+ if (statsSheetButton) {
2582
+ statsSheetButton.setAttribute('disabled', '');
2583
+ statsSheetButton.setAttribute('aria-disabled', 'true');
2584
+ }
2503
2585
  refreshOverviewSection();
2504
2586
  }
2505
2587
 
@@ -2932,6 +3014,56 @@
2932
3014
  }
2933
3015
  }
2934
3016
 
3017
+ let markMergedModalMrId = null;
3018
+
3019
+ function showMarkMergedModal(encodedMrId, mrNumber) {
3020
+ markMergedModalMrId = safeDecodeURIComponent(encodedMrId);
3021
+ document.getElementById('mark-merged-modal-title').textContent = t('modal.markMerged.title', { label: getMrLabel(), number: mrNumber });
3022
+ const modal = document.getElementById('mark-merged-modal');
3023
+ modal.classList.remove('hidden');
3024
+ requestAnimationFrame(() => modal.classList.add('visible'));
3025
+ }
3026
+
3027
+ function closeMarkMergedModal(event) {
3028
+ if (event && event.target !== event.currentTarget) return;
3029
+ const modal = document.getElementById('mark-merged-modal');
3030
+ modal.classList.remove('visible');
3031
+ setTimeout(() => modal.classList.add('hidden'), 200);
3032
+ markMergedModalMrId = null;
3033
+ }
3034
+
3035
+ async function confirmMarkAsMerged() {
3036
+ if (!markMergedModalMrId) return;
3037
+ trackUsefulAction('markAsMerged');
3038
+
3039
+ const mrId = markMergedModalMrId;
3040
+ closeMarkMergedModal();
3041
+
3042
+ try {
3043
+ const response = await fetch(`${API_URL}/api/mr-tracking/mark-as-merged`, {
3044
+ method: 'POST',
3045
+ headers: { 'Content-Type': 'application/json' },
3046
+ body: JSON.stringify({
3047
+ mrId,
3048
+ projectPath: currentProjectPath || undefined,
3049
+ }),
3050
+ });
3051
+ const data = await response.json();
3052
+
3053
+ if (data.success) {
3054
+ const card = document.querySelector(`.mr-item-accordion[data-mr-id="${mrId}"]`);
3055
+ if (card) card.remove();
3056
+ showToast(t('success.markedAsMerged'), 'success');
3057
+ fetchStatus();
3058
+ } else {
3059
+ showToast(data.error || t('error.markAsMerged'), 'error');
3060
+ }
3061
+ } catch (error) {
3062
+ console.error('Error marking as merged:', error);
3063
+ showToast(t('error.markAsMerged'), 'error');
3064
+ }
3065
+ }
3066
+
2935
3067
  function showToast(message, type) {
2936
3068
  const container = document.getElementById('toast-container');
2937
3069
  const iconName = type === 'success' ? 'check-circle' : type === 'info' ? 'info' : 'alert-circle';
@@ -3073,12 +3205,6 @@
3073
3205
  const emptyStats = document.getElementById('i18n-empty-stats');
3074
3206
  if (emptyStats) emptyStats.textContent = t('empty.stats');
3075
3207
 
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
3208
  const emptyActiveFollowups = document.getElementById('i18n-empty-active-followups');
3083
3209
  if (emptyActiveFollowups) emptyActiveFollowups.textContent = t('empty.activeFollowups');
3084
3210
 
@@ -3111,6 +3237,15 @@
3111
3237
 
3112
3238
  const modalConfirm = document.getElementById('cancel-modal-confirm');
3113
3239
  if (modalConfirm) modalConfirm.textContent = t('modal.confirm');
3240
+
3241
+ const markMergedModalMessage = document.getElementById('i18n-mark-merged-modal-message');
3242
+ if (markMergedModalMessage) markMergedModalMessage.textContent = t('modal.markMerged.message');
3243
+
3244
+ const markMergedModalBack = document.getElementById('i18n-mark-merged-modal-back');
3245
+ if (markMergedModalBack) markMergedModalBack.textContent = t('modal.markMerged.back');
3246
+
3247
+ const markMergedModalConfirm = document.getElementById('mark-merged-modal-confirm');
3248
+ if (markMergedModalConfirm) markMergedModalConfirm.textContent = t('modal.markMerged.confirm');
3114
3249
  }
3115
3250
 
3116
3251
  async function checkForUpdates() {
@@ -3265,7 +3400,6 @@
3265
3400
  window.toggleCollapsibleList = toggleCollapsibleList;
3266
3401
  window.checkClaudeStatus = checkClaudeStatus;
3267
3402
  window.toggleLogs = toggleLogs;
3268
- window.toggleStats = toggleStats;
3269
3403
  window.changeModel = changeModel;
3270
3404
  window.changeLanguage = changeLanguage;
3271
3405
  window.toggleReviewAccordion = toggleReviewAccordion;
@@ -3279,8 +3413,12 @@
3279
3413
  window.showCancelModal = showCancelModal;
3280
3414
  window.closeCancelModal = closeCancelModal;
3281
3415
  window.confirmCancelReview = confirmCancelReview;
3416
+ window.showMarkMergedModal = showMarkMergedModal;
3417
+ window.closeMarkMergedModal = closeMarkMergedModal;
3418
+ window.confirmMarkAsMerged = confirmMarkAsMerged;
3282
3419
  window.toggleFocusStripMode = toggleFocusStripMode;
3283
3420
  window.toggleSection = toggleSection;
3421
+ window.toggleNowLane = toggleNowLane;
3284
3422
  window.onUsefulLinkAction = onUsefulLinkAction;
3285
3423
  window.activateOnKeydown = activateOnKeydown;
3286
3424
  window.handleCleanupClick = handleCleanupClick;
@@ -3288,6 +3426,10 @@
3288
3426
  window.closeMrSheet = closeMrSheet;
3289
3427
  window.openDevSheet = openDevSheet;
3290
3428
  window.closeDevSheet = closeDevSheet;
3429
+ window.openEconomicsSheet = openEconomicsSheet;
3430
+ window.closeEconomicsSheet = closeEconomicsSheet;
3431
+ window.openStatsSheet = openStatsSheet;
3432
+ window.closeStatsSheet = closeStatsSheet;
3291
3433
  window.generateAiInsights = generateAiInsights;
3292
3434
  window.exportInsightsPdf = exportInsightsPdf;
3293
3435
  window.toggleTeamAnalysis = toggleTeamAnalysis;
@@ -3305,6 +3447,7 @@
3305
3447
  Notification.requestPermission().catch(() => {});
3306
3448
  }
3307
3449
  loadFocusStripMode();
3450
+ loadNowLaneCollapsed();
3308
3451
  renderStaticLabels();
3309
3452
  updateSessionMetricsUI();
3310
3453
  connectWebSocket();
@@ -3,6 +3,7 @@ export const RECONNECT_DELAY: 3000;
3
3
  export const STORAGE_KEY_PROJECTS: "review-flow-projects";
4
4
  export const STORAGE_KEY_CURRENT: "review-flow-current-project";
5
5
  export const STORAGE_KEY_FOCUS_STRIP_MODE: "review-flow-focus-strip-mode";
6
+ export const STORAGE_KEY_NOW_LANE_COLLAPSED: "review-flow-now-lane-collapsed";
6
7
  export const STORAGE_KEY_ACTIVE_TAB: "review-flow-active-tab";
7
8
  export const QUALITY_TARGET_SCORE: 8;
8
9
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/constants.js"],"names":[],"mappings":"AAAA,qCAAsC,EAAE,CAAC;AACzC,8BAA+B,IAAI,CAAC;AACpC,mCAAoC,sBAAsB,CAAC;AAC3D,kCAAmC,6BAA6B,CAAC;AACjE,2CAA4C,8BAA8B,CAAC;AAC3E,qCAAsC,wBAAwB,CAAC;AAC/D,mCAAoC,CAAC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/constants.js"],"names":[],"mappings":"AAAA,qCAAsC,EAAE,CAAC;AACzC,8BAA+B,IAAI,CAAC;AACpC,mCAAoC,sBAAsB,CAAC;AAC3D,kCAAmC,6BAA6B,CAAC;AACjE,2CAA4C,8BAA8B,CAAC;AAC3E,6CAA8C,gCAAgC,CAAC;AAC/E,qCAAsC,wBAAwB,CAAC;AAC/D,mCAAoC,CAAC,CAAC"}
@@ -3,5 +3,6 @@ export const RECONNECT_DELAY = 3000;
3
3
  export const STORAGE_KEY_PROJECTS = 'review-flow-projects';
4
4
  export const STORAGE_KEY_CURRENT = 'review-flow-current-project';
5
5
  export const STORAGE_KEY_FOCUS_STRIP_MODE = 'review-flow-focus-strip-mode';
6
+ export const STORAGE_KEY_NOW_LANE_COLLAPSED = 'review-flow-now-lane-collapsed';
6
7
  export const STORAGE_KEY_ACTIVE_TAB = 'review-flow-active-tab';
7
8
  export const QUALITY_TARGET_SCORE = 8;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/dashboard/modules/constants.js"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AACjE,MAAM,CAAC,MAAM,4BAA4B,GAAG,8BAA8B,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/dashboard/modules/constants.js"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AACjE,MAAM,CAAC,MAAM,4BAA4B,GAAG,8BAA8B,CAAC;AAC3E,MAAM,CAAC,MAAM,8BAA8B,GAAG,gCAAgC,CAAC;AAC/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC"}
@@ -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"}