1
0
Эх сурвалжийг харах

Present view: sprint switcher dropdown next to Close

Adds a <select> in the present-view header (visible to every
signed-in user, hidden when only one sprint exists) listing all
sprints, current selected. On change, the page navigates to the
chosen sprint's /sprints/{id}/present. SprintController::present()
fetches the full list ordered newest-start-first to match the home
listing; the change handler lives next to the existing
data-close-present block in sprint-planner.js, so strict CSP
remains intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 1 өдөр өмнө
parent
commit
1dba877efc

+ 14 - 0
public/assets/js/sprint-planner.js

@@ -2036,4 +2036,18 @@
             }, 100);
         });
     })();
+
+    // ---------------------------------------------------------------------
+    // Present view: sprint switcher dropdown
+    // ---------------------------------------------------------------------
+
+    (function initPresentSprintSwitcher() {
+        const sel = qs('[data-present-sprint-select]');
+        if (!sel) { return; }
+        sel.addEventListener('change', function () {
+            const id = parseInt(sel.value, 10);
+            if (!Number.isFinite(id) || id <= 0 || id === sprintId) { return; }
+            window.location.href = '/sprints/' + id + '/present';
+        });
+    })();
 })();

+ 12 - 3
src/Controllers/SprintController.php

@@ -212,11 +212,20 @@ final class SprintController
             return Response::text('Not Found', 404);
         }
 
+        // Sprint switcher: every sprint (current included), ordered newest start
+        // first to match the home listing.
+        $presentSprintChoices = [];
+        foreach ($this->sprints->allWithCounts() as $row) {
+            $s = $row['sprint'];
+            $presentSprintChoices[] = ['id' => $s->id, 'name' => $s->name];
+        }
+
         // Present view extends layout-bare.twig instead of the shared layout.twig.
         return Response::html($this->view->render('sprints/present', [
-            'title'       => $data['sprint']->name . ' — present',
-            'currentUser' => $actor,
-            'csrfToken'   => SessionGuard::csrfToken(),
+            'title'                => $data['sprint']->name . ' — present',
+            'currentUser'          => $actor,
+            'csrfToken'            => SessionGuard::csrfToken(),
+            'presentSprintChoices' => $presentSprintChoices,
         ] + $data));
     }
 

+ 9 - 0
views/sprints/present.twig

@@ -42,6 +42,15 @@
             <div data-status
                  class="text-xs border rounded px-2 py-0.5 opacity-0 transition-opacity duration-200 border-slate-200 bg-slate-50 text-slate-700 dark:bg-slate-800 dark:border-slate-700 dark:text-slate-300">
             </div>
+            {% if presentSprintChoices|default([])|length > 1 %}
+                <label class="sr-only" for="present-sprint-select">Sprint</label>
+                <select id="present-sprint-select" data-present-sprint-select
+                        class="rounded-md border border-slate-300 bg-white text-slate-700 px-2 py-1 text-sm hover:bg-slate-100 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-200 dark:hover:bg-slate-700">
+                    {% for choice in presentSprintChoices %}
+                        <option value="{{ choice.id }}"{% if choice.id == sprint.id %} selected{% endif %}>{{ choice.name }}</option>
+                    {% endfor %}
+                </select>
+            {% endif %}
             <a href="/sprints/{{ sprint.id }}" data-close-present
                class="inline-flex items-center gap-2 rounded-md border border-slate-300 bg-white text-slate-700 px-3 py-1 text-sm hover:bg-slate-100 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-200 dark:hover:bg-slate-700">
                 Close