Skip to content

feat(ui): unified sidebar + project cache + homepage with Issues/Iterations#184

Open
eipasteur wants to merge 12 commits into
mainfrom
feature/project-homepage
Open

feat(ui): unified sidebar + project cache + homepage with Issues/Iterations#184
eipasteur wants to merge 12 commits into
mainfrom
feature/project-homepage

Conversation

@eipasteur
Copy link
Copy Markdown
Contributor

Summary

Refonte de l'expérience utilisateur autour des projets : sidebar globale avec status dots, cache stale-while-revalidate pour projects + sprints, et project homepage qui aligne Issues (via #171) et Iterations en colonnes parallèles.

Components

hooks/useProjectsCache.ts — stale-while-revalidate cache

  • React useSyncExternalStore pattern, projects cached 120s (TTL) with auto-refresh, sprints cached 60s per-project.
  • Avoids redundant /api/projects calls when the user navigates between sidebar entries; hot-tab returns to a stale view immediately while a background refresh validates.
  • Pure consumer-side logic — no backend changes.

Layout (AppShell + AppSidebar)

  • Global sidebar lists all projects with live status dots (green = active sprint, gray = idle). Clicking expands sprints under a project (collapsible, radix-ui).
  • AppShell consumes useProjectSprintsCache to pre-warm sprint panels and auto-opens the ActivityPanel when a project is selected.
  • Removed the dead-end Backlog button (replaced by Issues integration from Feature/GitHub issue integration #171).

pages/Project.tsx — project homepage

  • Two horizontally aligned cards: Issues (via IssueListPanel from Feature/GitHub issue integration #171) and Iterations.
  • Headers share h-7 height + matching space-y-2 so columns line up.
  • "Start" iteration button on the right of the Iterations title (consistent with sprint workflow).
  • Live updates via useProjectSprintsCache — sprint cards reflect agent state in real time without polling the network.

pages/Dashboard.tsx

IssueListPanel (Jan's #171)

  • Removed the internal mt-6 margin so the parent (Project page) controls spacing — keeps the layout decision where it belongs.

Stacked on

Includes #182 (esbuild fix). Merge #182 first; this branch will rebase cleanly.

Validation

  • ✅ 153 unit tests pass
  • ✅ TypeScript clean
  • npm run build clean
  • ✅ Cache hooks tested manually: stale-while-revalidate works, no double-fetch on tab refocus, cache invalidation on refresh() call
  • ✅ Sidebar collapsible verified on multiple projects
  • ✅ Issues/Iterations alignment verified pixel-perfect on Chrome + Firefox

Out of scope

eipasteur added 12 commits May 21, 2026 12:09
Replace sprint-centric sidebar with a global navigation layout that
shows all projects with their current agent status. The sidebar is now
always visible, providing quick access to projects, observability, and
backlog views.

- Add stale-while-revalidate cache for projects and sprint lists
- Show running/waiting agent indicators per project in sidebar
- Activity panel auto-opens only when inside a sprint
- Add Collapsible UI component (radix-ui)
- AppShell: ActivityPanel resolves projectId via useParams, no need to pass it down
- useProjectsCache: remove unused useState import
… Backlog link

- Dashboard now consumes the shared cache instead of fetching projects
  directly. This removes a duplicate request at startup (Dashboard +
  AppSidebar were both fetching) and lets create/delete actions invalidate
  a single source of truth.
- Remove the Backlog navigation entry from AppSidebar: there is no /backlog
  route in this codebase, the button was dead.
Refactor /project/:projectId to act as a true project landing page,
not a sprint-centric view. The page now:

- Reads project + sprints from useProjectsCache (shared with sidebar
  and dashboard) — single fetch path, no more duplicate requests when
  navigating between Dashboard and Project.
- Subscribes to sprint events via useSprintEvents on the latest sprint,
  so agent status changes appear in real time without manual refresh.
- Renders a Live badge while an agent is running or waiting on the
  latest sprint, plus per-sprint status icons in the iteration list.
- Splits sprints into 'Active' and 'Past' (collapsible) so the user
  always sees what is happening now first.
- Embeds Jan's IssueListPanel side-by-side with the iteration list, so
  GitHub issues drive sprint creation directly from this page.
…on button

- Wrap IssueListPanel under an 'Issues' uppercase header that mirrors
  the existing 'Iterations' header, so both columns line up at the
  same vertical position.
- Add a primary 'Start' button to the right of the Iterations title
  with a Dialog that asks for the iteration name and calls
  sprintsService.create. On success, refresh the cached sprint list.
- Drop the hard-coded mt-6 from IssueListPanel's outer Card; the
  parent layout now owns vertical spacing, so the component composes
  cleanly under any header.
…spacing

The two columns were drifting vertically because their headers had
different heights (the Issues h3 was its natural height, while the
Iterations row was a flex container sized by the Start button).

- Wrap both titles in 'flex items-center justify-between h-7' so they
  share the same fixed height as the Start button.
- Reduce the column space-y from 4 to 2 so the title sits closer to
  the panel underneath.
PR #176 migrated lambda/github from CommonJS (require('./shared/...')) to
ESM (import '../shared/response.js'), but the Terraform packaging copied
shared/ at the same level as index.js in the zip. At runtime,
'../shared/response.js' resolved to /var/shared/response.js (one level
above /var/task/), which does not exist, causing ERR_MODULE_NOT_FOUND
on every invocation.

Apply the same pattern as PR #180 (github-issues): build the lambda with
esbuild via the npm workspace and zip the .build output. esbuild inlines
the shared/ modules at build time, eliminating runtime path resolution.

- terraform/modules/api/lambda/main.tf:
  - Replace npm_requirements + prefix_in_zip='shared' with the build/:zip
    pattern, mirroring github_issues_lambda
  - Bump runtime to nodejs24.x for consistency with the build target
    (esbuild --target=node24, package.json already targets node24)

Tests (153 unit tests across 7 files) continue to pass since they import
'../index.js' which resolves to lambda/github/index.js, whose original
'../shared/response.js' import resolves correctly inside the source tree
at lambda/shared/response.js (the same path esbuild uses to bundle).

Closes the runtime regression introduced in #176.
Two layered runtime regressions blocked /api/github/* on staging:

1. PR #176 migrated lambda/github from CommonJS to ESM but the Terraform
   packaging used npm_requirements + prefix_in_zip='shared'. The ESM
   resolver dereferenced '../shared/response.js' to /var/shared/...
   (one level above /var/task/), which doesn't exist, throwing
   ERR_MODULE_NOT_FOUND on every cold start.

2. shared/git-token.js is CommonJS and does require('@aws-sdk/client-ssm')
   at the module top level. esbuild bundles a CJS shim around it, but
   the Node.js ESM runtime rejects the resulting dynamic require:
   'Dynamic require of @aws-sdk/client-ssm is not supported'.

Fix:
- terraform/modules/api/lambda/main.tf: switch github_lambda from
  npm_requirements to the build/:zip pattern (mirroring github_issues
  per PR #180), bumping runtime to nodejs24.x to match the esbuild
  --target=node24 the workspace already uses.
- lambda/github/index.js: inline resolveGitToken locally, mirroring the
  exact pattern adopted by lambda/github-issues. shared/response.js
  remains imported because esbuild can statically bundle it without
  hitting a dynamic require (it has no top-level require of an external
  package).

Validated end-to-end on staging:
- terraform apply succeeded (no drift)
- aws lambda invoke returns statusCode 200 with valid JSON body
  ({connected: false}) for /api/github/status
- 153 unit tests across the repo continue to pass

Closes the runtime regression introduced in #176.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant