diff --git a/runbot/controllers/frontend.py b/runbot/controllers/frontend.py index 181a2c184..a5d7c0dfd 100644 --- a/runbot/controllers/frontend.py +++ b/runbot/controllers/frontend.py @@ -908,6 +908,7 @@ def bundles_by_tag(self, bundle_tag_id=None, project=None, **kwargs): if not project and projects: project = projects[0] bundles_by_team = defaultdict(list) + open_bundles_by_team = defaultdict(int) nb_bundles = 0 nb_bundles_done = 0 for bundle in self.env['runbot.bundle'].search([('tag_ids', 'in', bundle_tag_id.id)]): @@ -916,10 +917,13 @@ def bundles_by_tag(self, bundle_tag_id=None, project=None, **kwargs): bundle_prs = bundle.branch_ids.filtered(lambda rec: rec.is_pr) if any(bundle_prs) and not any(bundle_prs.mapped('alive')): nb_bundles_done += 1 + else: + open_bundles_by_team[bundle.team_id.name or 'No Team Defined'] += 1 qctx = { 'tag': bundle_tag_id, 'bundles_by_team': bundles_by_team, + 'open_bundles_by_team': open_bundles_by_team, 'nb_bundles': nb_bundles, 'nb_bundles_done': nb_bundles_done, } diff --git a/runbot/static/src/css/runbot.css b/runbot/static/src/css/runbot.css index 39df923c8..8411a1cdf 100644 --- a/runbot/static/src/css/runbot.css +++ b/runbot/static/src/css/runbot.css @@ -471,3 +471,26 @@ code { .log-details td { padding-left: 20px; } + +/* + * Bundle by tag + */ +.o_team_bundle_grid { + display: grid; + /* menu | name (2/3 of free space) | tags | age | filler (1/3) */ + grid-template-columns: max-content 2fr max-content max-content 1fr; + column-gap: .5rem; + align-items: center; +} + +.o_team_bundle_grid > li { + display: contents; +} + +.o_team_bundle_grid > li > button { + grid-column: 1; /* each bundle starts a new grid row, even if the previous one has no age badge */ +} + +.o_hide_done .o_team_bundle_grid > li:has(a.bundle-done) { + display: none; +} diff --git a/runbot/static/src/js/runbot.js b/runbot/static/src/js/runbot.js index 76b2a129b..15513d3bf 100644 --- a/runbot/static/src/js/runbot.js +++ b/runbot/static/src/js/runbot.js @@ -54,3 +54,32 @@ document.addEventListener('DOMContentLoaded', function() { }); } }); + +// Expand/collapse all team sections at once. +document.addEventListener('DOMContentLoaded', function() { + const expandBtn = document.getElementById('toggleTeamsButton'); + if (expandBtn) { + const bundleGrid = document.querySelector('.o_team_bundle_grid'); + const container = bundleGrid.closest('.container-fluid'); + expandBtn.addEventListener('click', function () { + const details = container.querySelectorAll('details'); + const allOpen = Array.from(details).every(d => d.open); + details.forEach(d => d.open = !allOpen); + this.textContent = allOpen ? 'Expand all' : 'Collapse all'; + }); + } +}); + +// Show/hide done bundles, persisted across reloads. +document.addEventListener('DOMContentLoaded', function() { + const toggleHideDoneBtn = document.getElementById('toggleHideDoneButton'); + if (toggleHideDoneBtn) { + const bundleGrid = document.querySelector('.o_team_bundle_grid'); + const container = bundleGrid.closest('.container-fluid'); + toggleHideDoneBtn.addEventListener('click', function () { + const hidden = container.classList.toggle('o_hide_done'); + this.textContent = hidden ? 'Show done' : 'Hide done'; + localStorage.setItem('runbot_hide_done_bundles', hidden ? '1' : ''); + }); + } +}); \ No newline at end of file diff --git a/runbot/templates/bundles_by_tag.xml b/runbot/templates/bundles_by_tag.xml index e7a7834d9..40ca25550 100644 --- a/runbot/templates/bundles_by_tag.xml +++ b/runbot/templates/bundles_by_tag.xml @@ -9,20 +9,22 @@ - + - + - - - + + + + + - + @@ -33,11 +35,23 @@

+ +

-
-
    +
    + + + + + + + + All Done + + +