Skip to content

feat: granular project access for org members#3066

Merged
HarshMN2345 merged 18 commits into
mainfrom
feat-ser542-granular-project-access
May 29, 2026
Merged

feat: granular project access for org members#3066
HarshMN2345 merged 18 commits into
mainfrom
feat-ser542-granular-project-access

Conversation

@HarshMN2345

@HarshMN2345 HarshMN2345 commented May 28, 2026

Copy link
Copy Markdown
Member

Add support for assigning org members to specific projects with per-project roles (owner, developer, editor, analyst) rather than granting access to all projects in the organization.

  • Add project-role helpers to billing store: isProjectSpecificRole, parseProjectRole, buildProjectRole, getRoleLabel, projectRoles
  • Pass projectId to getScopes in project layout so project-specific members receive correct scopes inside their assigned projects
  • Add owner-only guard on org settings page
  • Load orgProjects alongside members in the members page load
  • New projectAccessSelector component for inline project+role rows with duplicate-project prevention
  • Invite and edit modals use radio toggle (All / Specific projects) with inline selector — gated to plans with supportsOrganizationRoles
  • Members table shows per-project role badges for project-specific members
  • roles.svelte accepts isProjectSpecific prop for contextual role descriptions

What does this PR do?

image image image image

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Add support for assigning org members to specific projects with
per-project roles (owner, developer, editor, analyst) rather than
granting access to all projects in the organization.

- Add project-role helpers to billing store: isProjectSpecificRole,
  parseProjectRole, buildProjectRole, getRoleLabel, projectRoles
- Pass projectId to getScopes in project layout so project-specific
  members receive correct scopes inside their assigned projects
- Add owner-only guard on org settings page
- Load orgProjects alongside members in the members page load
- New projectAccessSelector component for inline project+role rows
  with duplicate-project prevention
- Invite and edit modals use radio toggle (All / Specific projects)
  with inline selector — gated to plans with supportsOrganizationRoles
- Members table shows per-project role badges for project-specific members
- roles.svelte accepts isProjectSpecific prop for contextual role descriptions
@greptile-apps

greptile-apps Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds granular project access for org members, allowing admins to assign members to specific projects with per-project roles (owner/developer/editor/analyst) rather than granting access to all projects.

  • Billing store helpers (isProjectSpecificRole, parseProjectRole, buildProjectRole, getRoleLabel) parse and build the project-{id}-{role} role format using lastIndexOf for deterministic parsing.
  • Invite & edit modals gain a radio toggle (All / Specific projects) backed by the new ProjectAccessSelector component with duplicate-project prevention; both modals are migrated to Svelte 5 runes.
  • Org home page adds a membership query to filter the project list for project-specific members; the project layout passes projectId to getScopes so those members receive correct scopes inside their assigned projects.

Confidence Score: 4/5

Safe to merge with awareness of two non-crash edge cases in project filtering and list truncation.

The core access-control and scoping logic is correct for all UI-driven paths. A member with both a general role and project-specific roles reachable via direct API would have their org home filtered to only the project-specific subset. The hard 100-project cap silently truncates larger organizations in both the table badges and the project selector.

src/routes/(console)/organization-[organization]/+page.ts and members/+page.ts

Important Files Changed

Filename Overview
src/lib/stores/billing.ts Adds project-role helpers; lastDash boundary check correctly rejects empty/missing projectId segments.
src/routes/(console)/organization-[organization]/+page.ts Adds project-scoped filtering; omits the guard for members with mixed general + project-specific roles.
src/routes/(console)/organization-[organization]/projectAccessSelector.svelte New component for per-project role rows with duplicate-project prevention via allSelected derived.
src/routes/(console)/organization-[organization]/createMember.svelte Migrated to Svelte 5 runes; Query.limit(100) caps visible projects silently.
src/routes/(console)/organization-[organization]/members/+page.ts Loads orgProjects alongside members; Query.limit(100) caps project list silently.
src/routes/(console)/organization-[organization]/members/edit.svelte Migrated to Svelte 5 runes; initialization $effect has known plan-downgrade issue (previously flagged).

Reviews (6): Last reviewed commit: "fix: skip membership lookup for owners a..." | Re-trigger Greptile

Comment thread src/routes/(console)/organization-[organization]/members/+page.ts
Comment thread src/routes/(console)/organization-[organization]/projectAccessSelector.svelte Outdated
- listProjects in members page load now uses .catch(() => null) so a
  permissions or network error does not break the entire page
- projectAccessSelector disables "Add project" when projects list is
  empty (prevents unusable required dropdown row)
- createMember resets orgProjects on modal close so subsequent opens
  fetch a fresh list instead of showing a stale one
- edit modal init effect now checks supportsProjectRoles before setting
  accessType to 'specific', preventing a silent role re-submission when
  the org plan is downgraded below the project-roles threshold
If any project's platform API call fails (e.g. project deleted or
inaccessible), fall back to empty platforms instead of crashing the
entire org page with a 404.
Projects whose platform API call fails (inaccessible/deleted on backend)
are now filtered out of the org projects list instead of shown as broken
cards. Also corrects the displayed total to match the visible count.
Restore graceful "No apps" degradation on org overview so pagination
stays correct. Add a try/catch around project.get() in the project
layout so clicking an inaccessible project card redirects back to the
org page rather than rendering a 404 error screen.
Fetch all projects in one call (up to 100) before filtering, then
paginate client-side. This ensures the total count and page numbers
reflect only the projects the user can actually access, avoiding the
broken pagination that occurred when filtering server-paginated results.
listProjects already returns only the projects a user has access to
based on their org/project roles. Client-side filtering was wrong.
Keep graceful degradation on listPlatforms so a flaky call can't
crash the entire org page.
Members with project-specific roles (project-{id}-{role}) now only see
the projects they have explicit access to on the org overview page.
Org-level roles (owner, developer, etc.) continue to see all projects.
getScopes without projectId collapses project-specific roles to
org-level scopes, so we query the membership directly to get the real
roles (e.g. project-{id}-developer). Members with project-specific
roles only see the projects they have explicit access to.
Comment thread src/routes/(console)/organization-[organization]/members/+page.svelte Outdated
When listProjects fails, orgProjects is null. The optional chain on
.projects alone leaves .find() called on undefined, crashing the
table render. Add ?. before .find() to handle this safely.
Instead of wrapping all project-specific role badges in the members
table cell, show only the first badge and a compact '+N' badge for
the remainder — matching the overflow pattern used in tables DB.
Members with multiple project-specific roles show the first role badge
inline. Hovering the '+N' badge reveals a tooltip listing all remaining
project roles, keeping the table clean while preserving discoverability.
Apply BODY_TOOLTIP_MAX_WIDTH and BODY_TOOLTIP_WRAPPER_STYLE to the
+N project roles tooltip so content stays inside bounds and long
project names wrap instead of overflowing.
- Skip the extra listMemberships call for org owners (they always see
  all projects), avoiding unnecessary latency on every org page load
- Simplify projectScopeQueries type from ReturnType<typeof Query.equal>[]
  to string[]
- Track hasFetchedProjects in createMember to prevent Svelte 5 reactivity
  from re-triggering the listProjects fetch when the org has zero projects
  (assigning a new [] reference would re-run the effect indefinitely)
@HarshMN2345 HarshMN2345 merged commit ce9e596 into main May 29, 2026
3 of 4 checks passed
@HarshMN2345 HarshMN2345 deleted the feat-ser542-granular-project-access branch May 29, 2026 08:35
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.

2 participants