diff --git a/frontend/src/views/ProjectView.vue b/frontend/src/views/ProjectView.vue index b98dd36..ae9bdaf 100644 --- a/frontend/src/views/ProjectView.vue +++ b/frontend/src/views/ProjectView.vue @@ -179,6 +179,25 @@ const milestoneGroups = computed((): MilestoneGroup[] => { return groups; }); +// The "No Milestone" group has no id, and collapsing it uses the same Set as +// the milestones: 0 stands for it, since no milestone id is ever 0. +const UNASSIGNED_KEY = 0; +function groupKey(group: MilestoneGroup): number { + return group.milestone?.id ?? UNASSIGNED_KEY; +} + +// A group's Done column starts COLLAPSED, to its header and count. Done work is +// the part of a board nobody is reading, and listed in full it pushed the open +// columns of every group below it off the screen (operator, 2026-09-15). Not +// persisted, matching the milestone collapse beside it: each load starts from +// the same defaults. +const expandedDone = ref>(new Set()); +function toggleDone(group: MilestoneGroup) { + const key = groupKey(group); + if (expandedDone.value.has(key)) expandedDone.value.delete(key); + else expandedDone.value.add(key); +} + function toggleMilestoneCollapse(id: number) { if (collapsedMilestones.value.has(id)) { collapsedMilestones.value.delete(id); @@ -386,6 +405,16 @@ async function loadTasks() { all.push(...next.notes); } tasks.value = all; + // The No Milestone group follows the milestone rule: once, on first load, + // it starts collapsed when everything in it is finished. + if (!autoCollapsedOnce.value.has(UNASSIGNED_KEY)) { + autoCollapsedOnce.value.add(UNASSIGNED_KEY); + const assigned = new Set(milestones.value.map((m) => m.id)); + const loose = all.filter((t) => !t.milestone_id || !assigned.has(t.milestone_id)); + if (loose.length && loose.every((t) => t.status === "done" || t.status === "cancelled")) { + collapsedMilestones.value.add(UNASSIGNED_KEY); + } + } } catch { // Say so. This used to swallow the error and leave an empty board, which is // indistinguishable from a project with no tasks — the same "hidden with no @@ -950,11 +979,12 @@ async function confirmDelete() {
- - + +
-
+
@@ -1094,12 +1124,19 @@ async function confirmDelete() {
-
+
-
+ + + +
brings with it. */ +.col-toggle { + width: 100%; + background: none; + border: none; + padding: 0; + font-family: inherit; + cursor: pointer; + text-align: left; +} +.col-toggle:focus-visible { outline: none; box-shadow: var(--fs-focus-ring); border-radius: var(--fs-radius-sm); } .col-count { background: var(--fs-surface-raised); border: 1px solid var(--fs-border-color);