Forráskód Böngészése

HANDOFF.md: add Phase 14 plan (hamburger menu)

Plan-only commit. The Phase 14 entry in §9 Upcoming describes moving
the admin utility links (Workers, Users, Audit log) plus Sign out from
the site header into a hamburger-icon dropdown, while keeping Sprints,
New sprint, and the user badge visible as primary actions. Vanilla-JS
toggle in app.js, CSP-clean inline SVG icon, Sign-out stays a real
form POST so the CSRF guard is preserved. Non-admin users get a menu
containing just Sign out. §12 resume prompt updated so a fresh session
sees Phase 14 as scheduled.

No code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
achiappa 2 hete
szülő
commit
15695ab934
1 módosított fájl, 137 hozzáadás és 10 törlés
  1. 137 10
      HANDOFF.md

+ 137 - 10
HANDOFF.md

@@ -290,9 +290,136 @@ with a `BOOTSTRAP_ADMIN` audit row.
 
 ### Upcoming
 
-Nothing scheduled. Residual follow-ups are in §10; the main outstanding
-item is a human-run walkthrough of [ACCEPTANCE.md](ACCEPTANCE.md)
-(now covering Phases 1–13) in a running container.
+- [ ] **Phase 14 — Hamburger menu groups admin utilities + Sign out**
+
+      **Problem.** The site header currently renders every nav link as a
+      top-level anchor: Sprints, Workers, Users, New sprint, Audit log,
+      display-name, and a Sign-out `<button>` in a `<form>`. On a
+      reasonably wide screen this is already six hit-targets; at laptop
+      widths the row wraps or compresses the display name. More
+      importantly, "Workers / Users / Audit log" are *administrative*
+      concerns — they're not the primary workflow (which is opening a
+      sprint) — but they share visual weight with the real actions.
+      Sign out is housekeeping and deserves even less real estate.
+
+      **Goal.** Tuck **Workers, Users, Audit log, Sign out** into a
+      hamburger-icon dropdown on the right of the header. Keep
+      **Sprints** and **New sprint** visible as primary actions. The
+      user's display name + admin badge stay visible too.
+
+      **Scope (plan).**
+
+      1. **View — `views/layout.php`.**
+         - Replace the flat `<nav>` content with two segments:
+           (a) primary links (Sprints, New sprint) and the user
+           badge, rendered as today;
+           (b) a `<button data-menu-trigger aria-expanded="false"
+           aria-haspopup="true" aria-controls="app-menu">` carrying
+           an inline SVG hamburger icon (three stacked `<rect>`s or
+           `<line>`s, width/height 20, stroke-current — no external
+           image, no font icons, CSP-clean).
+         - Next to the button, a `<div id="app-menu" data-menu
+           role="menu" hidden class="...">` absolutely positioned
+           dropdown that contains the four items in this order:
+             * `<a href="/workers" role="menuitem">Workers</a>` — admin only
+             * `<a href="/users"   role="menuitem">Users</a>`   — admin only
+             * `<a href="/audit"   role="menuitem">Audit log</a>` — admin only
+             * a visual divider (`<hr>`)
+             * the existing Sign-out `<form method="post" action="/auth/logout">`
+               with its CSRF hidden input; the submit button gets
+               `role="menuitem"` and matches the other items' styling.
+         - Non-admin users see a menu containing just the Sign-out
+           form, preceded by no divider. The hamburger is always
+           rendered when a user is signed in (simpler than
+           conditionally hiding it for non-admins — the one item
+           still reads fine inside the dropdown).
+         - Signed-out state is unchanged — still the inline "Sign
+           in" link.
+
+      2. **JS — `public/assets/js/app.js`.**
+         - Add a small controller (≤ 25 lines). `[data-menu-trigger]`
+           toggles `[hidden]` on `[data-menu]` and flips
+           `aria-expanded`. Close on:
+             * outside-click (pattern copied from
+               `sprint-planner.js`'s owner-filter dropdown);
+             * `Escape` keypress (focus returns to the trigger);
+             * clicking any `role="menuitem"` inside (so following a
+               link feels snappy and Sign-out's form submit isn't
+               visually delayed).
+         - No dependency on jQuery — `app.js` today is vanilla JS
+           (see the `data-href` handler); keep it that way.
+
+      3. **Styling.**
+         - Menu panel: rounded border, white bg, `shadow-lg`, min-w
+           ~12rem, 1-px slate border. Items: `px-3 py-2 text-sm
+           text-slate-700 hover:bg-slate-50` matching the settings
+           page's row styling. The Sign-out button inherits
+           `font-[inherit] text-left w-full` so it reads as a menu
+           row, not a form button.
+         - Hamburger button: `p-2 rounded-md hover:bg-slate-100`,
+           24×24 icon. `aria-label="Open menu"` on the button since
+           the icon is decorative.
+         - Focus-ring: `focus:outline-none focus:ring-2
+           focus:ring-slate-400` on both the trigger and each item.
+
+      4. **A11y.**
+         - `role="menu"` on the panel, `role="menuitem"` on each
+           item, `aria-expanded` on the trigger, `aria-controls`
+           pointing at the panel id.
+         - Keyboard: Enter/Space on trigger opens the menu and
+           focuses the first item. Arrow-down/up cycles items
+           (out of scope for a first cut — note as a follow-up if
+           screen-reader users complain; the menu is short enough
+           that tab-cycling is usable).
+         - The Sign-out button stays a real `<button type="submit">`
+           inside a real `<form method="post">` — no JS-driven POST,
+           preserves the `_csrf` guard.
+
+      5. **Edge cases.**
+         - **Hydration timing.** `app.js` is loaded with `defer`, so
+           the menu starts closed by virtue of `hidden` in the HTML.
+           No flash.
+         - **Very narrow screens.** The primary links (Sprints / New
+           sprint) already wrap in the current layout on mobile;
+           this phase does not make them responsive — deferred to
+           §10 if a real mobile need surfaces.
+         - **Sign-out posts through fine.** The enclosing form
+           submit is a native action; outside-click logic ignores
+           clicks on the submit button (handled by the
+           role="menuitem" close-on-click rule firing *after* the
+           form submission is already in-flight — native `<form>`
+           submit doesn't need the menu to stay open).
+
+      6. **Tests.** Zero PHPUnit additions — view-only change, same
+         pattern as Phase 10 and 13. Manual acceptance in
+         [ACCEPTANCE.md](ACCEPTANCE.md), add a new "Phase 14 —
+         Hamburger menu" section with:
+           (a) signed-in admin: trigger opens dropdown with Workers
+               / Users / Audit log / Sign out;
+           (b) signed-in non-admin: dropdown contains only Sign out;
+           (c) outside-click and Escape close the menu;
+           (d) Sign out from the menu still logs out with a valid
+               CSRF token (one full loop).
+
+      **Out of scope.**
+      - A full responsive mobile navigation (collapse of primary
+        links, off-canvas drawer, etc.). Deferred; this phase only
+        moves the admin / utility items into a dropdown.
+      - Theming or a dark-mode toggle inside the menu. Separate
+        concern, no request yet.
+      - Reordering `Workers / Users / Audit log / Sign out` — kept
+        in the order the user listed.
+      - Making the menu sticky on scroll. Header is already
+        non-sticky.
+
+      **Spec alignment.**
+      - §3 layout: no new files or directories (views/layout.php
+        and public/assets/js/app.js both already exist).
+      - §6 routes: unchanged — the menu links to existing pages.
+      - §7 audit: unchanged — Sign-out still emits its LOGOUT row.
+      - §11: phpunit count stays 88.
+      - CSP (Phase 11) stays strict — no inline handlers, no new
+        script host.
 
 ## 10. Residual known gaps / deferred items
 
@@ -350,13 +477,13 @@ Tell Claude:
 
 > Working on `/Users/achiappa/Development/claude_code_private/sprint_planer_web`.
 > Read `HANDOFF.md`, the git log, and `ACCEPTANCE.md`. Phases 1–13 are
-> shipped (see §9) and §9 Upcoming is empty — no scheduled work.
-> Outstanding items are in §10 (mostly a human-run acceptance
-> walkthrough in the running container). If you plan to take on a new
-> phase, append its plan to §9 Upcoming first; when it ships, follow
-> the maintenance rule in §14 — commit code, then commit a HANDOFF.md
-> update separately that moves the entry from Upcoming → Shipped with
-> its SHA.
+> shipped (see §9); Phase 14 (hamburger menu grouping Workers / Users /
+> Audit log / Sign out) is planned but not implemented — the scope is
+> in §9 under Upcoming. Other outstanding items are in §10 (mostly a
+> human-run acceptance walkthrough in the running container). If I ask
+> you to work Phase 14, follow the maintenance rule in §14 — commit
+> code, then commit a HANDOFF.md update separately that moves the
+> entry from Upcoming → Shipped with its SHA.
 
 Claude should verify what's described here against actual repo state
 before acting — nothing here is load-bearing once it grows stale.