Skip to content

feat(projects): scope GET /dashboard to the caller's projects - #304

Draft
nourshoreibah wants to merge 1 commit into
mainfrom
feat/dashboard-scoped-to-members
Draft

feat(projects): scope GET /dashboard to the caller's projects#304
nourshoreibah wants to merge 1 commit into
mainfrom
feat/dashboard-scoped-to-members

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

ℹ️ Issue

Prerequisite for replacing the last of the frontend's hardcoded placeholder data. Split out per "if the API is missing, put up a separate PR to add it".

📝 Description

ProjectCard on /dashboard and /projects is rendered with budget_used={0} members={0} — hardcoded, for every project. The real numbers already exist in GET /projects/dashboard (spent, staff_count), but that route was gated if (!user.isAdmin) return 403, so a non-admin had no source for them at all.

This makes the route work for an admin and anyone who has projects:

  1. An admin still sees every project — unchanged payload.
  2. Anyone else gets the same payload scoped to the projects they are a member of. Every aggregate (totalSpent, totalProjects, topExpenseCategory, projects, per-project spend, per-project staff, expensesByMonth) is filtered by that id set.
  3. A caller with no memberships gets a zeroed payload rather than an error, short-circuiting before any aggregate query — where project_id in () is not valid SQL.

Duplicate membership rows are de-duped before they reach the IN list.

✔️ Verification

npx jest in apps/backend/lambdas/projects77 passed, 6 suites, against the local Postgres (branch_dev@localhost:5432/branch_db).

New coverage:

  • dashboard.unit.test.ts — non-admin gets a scoped dashboard; duplicate memberships don't widen the IN list; a member of nothing gets the empty payload after exactly one query.
  • projects.e2e.test.ts — replaces the old 403: non-admin is forbidden with the real scoped result against seed data (user 3 → project 2 only, spent: 4500, staff_count: 1), plus the no-memberships case.

🏕️ (Optional) Future Work / Notes

  • The follow-up frontend PR consumes spent / staff_count here to fill in ProjectCard. It is stacked on this branch.
  • Reviewers: the removed 403 was the only thing this route did with isAdmin, so the admin path is byte-for-byte the same set of queries as before.

🤖 Generated with Claude Code

The dashboard aggregates were admin-only, so a non-admin got a 403 and the
frontend had no source for a project's spend or staff count -- which is why
every ProjectCard on /dashboard and /projects renders budget_used={0}
members={0}.

An admin still sees every project. Everyone else now sees the same payload
restricted to the projects they are a member of, and a caller with no
memberships gets a zeroed payload rather than an error (`where in ()` is not
valid SQL, so that case short-circuits before any aggregate query runs).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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