Skip to content

feat: manage project members #284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Conversation

MoShizzle
Copy link
Contributor

@MoShizzle MoShizzle commented Jun 3, 2025

Closes #167

Summary by CodeRabbit

  • New Features

    • Added user access control management, allowing users to view their permissions and enabling project owners to remove project members via the settings page.
    • Introduced a new access control store and service for managing and checking permissions across projects, workspaces, spaces, and lists.
    • Added utility for error handling with user-friendly notifications.
  • Improvements

    • Enhanced project member management with context menus and permission-guarded actions.
    • Improved snackbar transitions and animations for a smoother user experience.
    • Enabled persistent storage for several frontend stores to retain state across sessions.
    • Updated project user permission handling to grant and revoke permissions recursively for child resources.
    • Added automatic loading of related project data when fetching project users.
  • Bug Fixes

    • Updated user roles assigned during project invitation to ensure correct permission levels.
  • Style

    • Added fade-in and fade-out transitions for UI components.
  • Chores

    • Removed inline comments and improved code structure without affecting functionality.

@MoShizzle MoShizzle added the enhancement Changes or additions to existing features label Jun 3, 2025
Copy link

coderabbitai bot commented Jun 3, 2025

Walkthrough

This update introduces project member management features, including the ability for project owners to remove members via the frontend UI. Supporting changes span backend access control, new API endpoints, frontend stores, composables, and UI enhancements. Additional improvements include persistent state for several Pinia stores and animated transitions for snackbars.

Changes

File(s) Change Summary
.../auth/auth.module.ts, .../auth/controllers/auth.controller.ts Updated controller imports and registration; added AccessControlController.
.../auth/controllers/access.control.controller.ts Added new AccessControlController with a secured "me" endpoint for user permissions.
.../auth/services/access.control.service.ts Refactored child resource logic, added findAllForUser, improved grant/revoke permission methods.
.../auth/services/auth.service.ts Changed default project role on invite from "admin" to "editor".
.../projects/project-users/project.user.entity.ts Made project relation eager-loaded in ProjectUser entity.
.../projects/project-users/project.users.service.ts Updated update/remove methods to grant/revoke permissions and update user entity.
.../base/BaseSnackbar.vue, .../main.ts Removed comments; in main.ts, disabled Vue Query retries.
.../base/BaseSnackbarWrapper.vue, .../styles/styles.scss Refactored snackbar rendering with transitions and teleport; added transition CSS.
.../settings/ProjectMembersSettings.vue Added member removal UI, permission checks, confirmation dialog, and error handling.
.../composables/useTryCatch.ts Added useTryCatch composable for error handling with snackbars.
.../services/useAccessControlService.ts Added service to fetch user permissions from backend.
.../services/useProjectUsersService.ts Added deleteProjectUser API call and mutation hook.
.../stores/access.control.ts Added Pinia store for access control and permission checks.
.../stores/field.query.ts, .../stores/notification.ts, .../stores/query.ts Enabled persistence for field query, notification, and query stores.
.../shared/src/lib/auth/index.ts Added AccessControl type definition.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant AccessControlService
    participant ProjectUsersService

    User->>Frontend: Click "Remove member"
    Frontend->>Frontend: Show confirmation dialog
    User->>Frontend: Confirm removal
    Frontend->>Backend: DELETE /projects/:projectId/users/:projectUserId
    Backend->>ProjectUsersService: remove(projectUserId)
    ProjectUsersService->>AccessControlService: revokePermissions(userId, projectId)
    ProjectUsersService->>DB: Remove ProjectUser, update User.project=null
    Backend-->>Frontend: Success/Failure
    Frontend->>Frontend: Show snackbar with result
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement ability to manage (remove) project members from the Manage Members section (#167)
Add UI controls and permission checks for member removal (#167)
Ensure backend supports permission revocation and proper updates on member removal (#167)
Provide user feedback and error handling on member removal actions (#167)

Poem

A bunny hopped through code so bright,
Now project owners wield their might!
With menus, checks, and snackbars too,
They manage members—swift and true.
Permissions set and roles in hand,
The warren’s order, neatly planned.
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-03T10_44_28_734Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77212c1 and 88c7670.

📒 Files selected for processing (4)
  • packages/backend/src/app/common/projects/project-users/project.users.service.ts (2 hunks)
  • packages/frontend/src/components/common/settings/ProjectMembersSettings.vue (2 hunks)
  • packages/frontend/src/stores/access.control.ts (1 hunks)
  • packages/shared/src/lib/auth/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/backend/src/app/common/projects/project-users/project.users.service.ts
  • packages/frontend/src/components/common/settings/ProjectMembersSettings.vue
  • packages/shared/src/lib/auth/index.ts
  • packages/frontend/src/stores/access.control.ts
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (15)
packages/frontend/src/components/common/base/BaseSnackbar.vue (2)

13-13: Consider retaining helpful comments for maintainability.

The removed comment explained the countdown step calculation logic, which could be valuable for future maintenance. Consider keeping such explanatory comments, especially for mathematical calculations.


21-21: Consider retaining helpful comments for maintainability.

The removed comment documented the interval update frequency (100ms), which is useful context for understanding the countdown behavior.

packages/frontend/src/main.ts (1)

43-43: Consider selective retry configuration for different query types.

The global retry: false setting improves error handling control but disables retries for all queries. Consider if some queries (like fetching project data) might benefit from retries while authentication-related queries should fail fast.

You could configure retry behavior per query type:

// In individual services
useQuery({
  queryKey: ['projects'],
  queryFn: fetchProjects,
  retry: 3, // Override global setting for this query
})
packages/frontend/src/composables/useTryCatch.ts (2)

6-11: Consider making error messages configurable.

The error message is hardcoded to a generic message. Consider allowing custom error messages for better user experience.

-function showErrorSnackbar() {
+function showErrorSnackbar(message?: string) {
   showSnackbar({
-    message: 'Something went wrong, please try again.',
+    message: message || 'Something went wrong, please try again.',
     color: 'error',
   });
 }

13-19: Enhance function handling for better flexibility.

The current implementation has limitations: it doesn't handle return values, async functions, or provide error details for debugging.

Consider this more flexible implementation:

-function tryCatch(fn: () => unknown) {
+function tryCatch<T>(fn: () => T, errorMessage?: string): T | undefined {
   try {
-    fn();
+    return fn();
   } catch (error) {
+    console.error('tryCatch error:', error);
-    showErrorSnackbar();
+    showErrorSnackbar(errorMessage);
+    return undefined;
   }
 }
+
+async function tryCatchAsync<T>(fn: () => Promise<T>, errorMessage?: string): Promise<T | undefined> {
+  try {
+    return await fn();
+  } catch (error) {
+    console.error('tryCatchAsync error:', error);
+    showErrorSnackbar(errorMessage);
+    return undefined;
+  }
+}
packages/shared/src/lib/auth/index.ts (1)

34-43: Well-designed AccessControl type with good flexibility.

The type definition properly models access control permissions with:

  • Clear permission levels using existing enums
  • Flexible resource targeting via optional properties
  • Proper metadata tracking (id, createdAt)
  • Integration with existing shared types

However, consider adding validation to ensure at least one resource is specified:

 export type AccessControl = {
   id: number;
   permissionLevel: PermissionLevel;
   user: User;
   project?: Project;
   workspace?: Workspace;
   space?: Space;
   list?: List;
   createdAt: string;
-};
+} & (
+  | { project: Project; workspace?: never; space?: never; list?: never }
+  | { workspace: Workspace; project?: never; space?: never; list?: never }
+  | { space: Space; project?: never; workspace?: never; list?: never }
+  | { list: List; project?: never; workspace?: never; space?: never }
+);

This ensures exactly one resource type is specified while maintaining type safety.

packages/frontend/src/services/useAccessControlService.ts (1)

14-19: Consider enhancing query configuration for better caching behavior.

The service implementation is correct and follows established patterns. However, consider these improvements:

  1. Query key specificity: The current key ['accessControl'] is global. Consider making it user-specific like ['accessControl', { userId }] if multiple users' permissions might be cached.

  2. Stale time configuration: Adding a staleTime property would improve performance by reducing unnecessary refetches, similar to other services in the codebase.

  function useGetUserPermissions() {
    return useQuery({
-     queryKey: ['accessControl'],
+     queryKey: ['accessControl', 'me'],
      queryFn: getUserPermissions,
+     staleTime: 5 * 60 * 1000, // 5 minutes, consistent with other services
    });
  }
packages/frontend/src/services/useProjectUsersService.ts (1)

38-48: Clarify parameter naming for better code readability.

The delete function parameter projectUserId could be misleading. Based on the RESTful endpoint pattern /projects/${projectId}/users/${projectUserId}, this appears to be the ProjectUser entity ID, but the name suggests it's a User ID.

Consider renaming for clarity:

  async function deleteProjectUser({
    projectId,
-   projectUserId,
+   userId, // or projectUserEntityId if it's the ProjectUser.id
  }: {
    projectId: number;
-   projectUserId: number;
+   userId: number; // or projectUserEntityId: number
  }): Promise<void> {
-   return sendRequest(`/projects/${projectId}/users/${projectUserId}`, {
+   return sendRequest(`/projects/${projectId}/users/${userId}`, {
      method: 'DELETE',
    });
  }
packages/frontend/src/components/common/base/BaseSnackbarWrapper.vue (2)

11-11: Consider extracting closingSnackbars to the snackbar store for better state management.

Since this closing state is integral to the snackbar lifecycle, it would be more appropriate to manage it within the snackbar store itself rather than as local component state. This would centralize all snackbar-related state management.


37-40: Extract inline styles to a computed property for better maintainability.

The inline style object could be extracted to a computed property, especially if you plan to add more dynamic styles in the future.

+const getSnackbarStyle = (index: number) => ({
+  bottom: `${snackbarOffsets[index]}px`,
+  transition: 'all ease 0.2s',
+});

 <base-snackbar
   v-if="!closingSnackbars.has(snackbar.id)"
   :color="snackbar.options.color"
   :timeout="snackbar.options.timeout ?? 4000"
-  :style="{
-    bottom: `${snackbarOffsets[index]}px`,
-    transition: 'all ease 0.2s',
-  }"
+  :style="getSnackbarStyle(index)"
   @close="handleSnackbarClose(snackbar)"
 >
packages/frontend/src/stores/access.control.ts (1)

31-36: Consider returning PermissionLevel.NONE instead of null for consistency.

Since PermissionLevel.NONE exists in the enum, returning it instead of null would provide better type consistency and avoid null checks in consuming code.

 function getPermissionLevel(
   type: 'project' | 'workspace' | 'space' | 'list',
   id: number
-): PermissionLevel | null {
-  return permissionsMap.value[type]?.[id]?.permissionLevel ?? null;
+): PermissionLevel {
+  return permissionsMap.value[type]?.[id]?.permissionLevel ?? PermissionLevel.NONE;
 }
packages/frontend/src/components/common/settings/ProjectMembersSettings.vue (1)

40-50: Consider adding more member management actions to the menu.

The menu currently only has a delete action. You might want to add actions like changing roles, viewing member details, etc.

Do you want me to suggest additional menu items that could enhance the member management functionality?

packages/backend/src/app/common/auth/services/access.control.service.ts (3)

186-199: Sequential recursion may slow bulk grants

for ... of combined with await runs the DB write serially for every child item; granting hundreds of descendants could become noticeably slow.

-        for (const child of children) {
-            for (const item of child.items) {
-                await this.grantPermission(user, child.type, item.id, permissionLevel);
-            }
-        }
+        // Execute grants in parallel on each recursion layer
+        for (const child of children) {
+            await Promise.all(
+                child.items.map((item) =>
+                    this.grantPermission(user, child.type, item.id, permissionLevel),
+                ),
+            );
+        }

Using Promise.all preserves the recursive structure while significantly improving throughput.


217-226: Same performance caveat for revocations

Revocation mirrors the grant logic and inherits the same serial-execution penalty. Consider parallelising with Promise.all, analogous to the previous suggestion.


264-273: Eager-loading all relations could be heavy

findAllForUser fetches every access-control row together with project, workspace, space, and list relations. In large organisations this may:

• Load thousands of rows into memory.
• Cause N+1 queries if relations aren’t properly joined.

Consider adding pagination or allowing the caller to specify which relations it needs:

-    async findAllForUser(userId: number) {
-        return this.accessControlRepository.find({
-            where: { user: { id: userId } },
-            relations: ["project", "workspace", "space", "list"],
-        });
-    }
+    async findAllForUser(
+        userId: number,
+        relations: ("project" | "workspace" | "space" | "list")[] = [],
+    ) {
+        return this.accessControlRepository.find({
+            where: { user: { id: userId } },
+            relations,
+        });
+    }

This keeps the method flexible and avoids unnecessary load on the DB.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 177c974 and 77212c1.

📒 Files selected for processing (20)
  • packages/backend/src/app/common/auth/auth.module.ts (3 hunks)
  • packages/backend/src/app/common/auth/controllers/access.control.controller.ts (1 hunks)
  • packages/backend/src/app/common/auth/controllers/auth.controller.ts (1 hunks)
  • packages/backend/src/app/common/auth/services/access.control.service.ts (4 hunks)
  • packages/backend/src/app/common/auth/services/auth.service.ts (2 hunks)
  • packages/backend/src/app/common/projects/project-users/project.user.entity.ts (1 hunks)
  • packages/backend/src/app/common/projects/project-users/project.users.service.ts (2 hunks)
  • packages/frontend/src/components/common/base/BaseSnackbar.vue (1 hunks)
  • packages/frontend/src/components/common/base/BaseSnackbarWrapper.vue (1 hunks)
  • packages/frontend/src/components/common/settings/ProjectMembersSettings.vue (2 hunks)
  • packages/frontend/src/composables/useTryCatch.ts (1 hunks)
  • packages/frontend/src/main.ts (1 hunks)
  • packages/frontend/src/services/useAccessControlService.ts (1 hunks)
  • packages/frontend/src/services/useProjectUsersService.ts (2 hunks)
  • packages/frontend/src/stores/access.control.ts (1 hunks)
  • packages/frontend/src/stores/field.query.ts (1 hunks)
  • packages/frontend/src/stores/notification.ts (1 hunks)
  • packages/frontend/src/stores/query.ts (1 hunks)
  • packages/frontend/src/styles/styles.scss (1 hunks)
  • packages/shared/src/lib/auth/index.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (12)
packages/backend/src/app/common/projects/project-users/project.users.service.ts (1)
packages/shared/src/lib/users/index.ts (1)
  • User (3-13)
packages/frontend/src/stores/field.query.ts (4)
packages/frontend/src/stores/state.ts (1)
  • getCurrentList (63-67)
packages/shared/src/lib/lists/lists.ts (1)
  • List (4-15)
packages/shared/src/lib/card-types/index.ts (1)
  • CardType (8-20)
packages/frontend/src/composables/useFields.ts (1)
  • useFields (5-188)
packages/backend/src/app/common/projects/project-users/project.user.entity.ts (1)
packages/shared/src/lib/projects/index.ts (1)
  • Project (3-9)
packages/frontend/src/stores/notification.ts (2)
packages/frontend/src/stores/auth.ts (1)
  • useAuthStore (8-174)
packages/frontend/src/services/useNotificationService.ts (1)
  • useNotificationService (6-56)
packages/backend/src/app/common/auth/controllers/access.control.controller.ts (2)
packages/backend/src/app/common/auth/decorators/current.user.decorator.ts (1)
  • CurrentUser (4-15)
packages/shared/src/lib/users/index.ts (1)
  • User (3-13)
packages/frontend/src/services/useProjectUsersService.ts (1)
packages/frontend/src/composables/useHttp.ts (1)
  • useHttp (70-98)
packages/shared/src/lib/auth/index.ts (5)
packages/shared/src/lib/users/index.ts (1)
  • User (3-13)
packages/shared/src/lib/projects/index.ts (1)
  • Project (3-9)
packages/shared/src/lib/workspaces/index.ts (1)
  • Workspace (4-16)
packages/shared/src/lib/spaces/index.ts (1)
  • Space (3-10)
packages/shared/src/lib/lists/lists.ts (1)
  • List (4-15)
packages/frontend/src/stores/query.ts (9)
packages/frontend/src/stores/auth.ts (2)
  • useAuthStore (8-174)
  • isAuthenticated (90-92)
packages/frontend/src/stores/state.ts (2)
  • useStateStore (12-126)
  • getCurrentList (63-67)
packages/frontend/src/services/useViewsService.ts (1)
  • useViewsService (11-119)
packages/frontend/src/services/useProjectUsersService.ts (1)
  • useProjectUsersService (6-66)
packages/frontend/src/services/useWorkspacesService.ts (1)
  • useWorkspacesService (6-111)
packages/frontend/src/services/useSpacesService.ts (1)
  • useSpacesService (11-119)
packages/frontend/src/services/useListsService.ts (1)
  • useListsService (6-132)
packages/shared/src/lib/card-types/index.ts (1)
  • CardType (8-20)
packages/shared/src/lib/lists/lists.ts (2)
  • List (4-15)
  • ListStage (17-24)
packages/frontend/src/services/useAccessControlService.ts (2)
packages/frontend/src/composables/useHttp.ts (1)
  • useHttp (70-98)
packages/shared/src/lib/auth/index.ts (1)
  • AccessControl (34-43)
packages/frontend/src/composables/useTryCatch.ts (1)
packages/frontend/src/stores/snackbar.ts (2)
  • useSnackbarStore (16-46)
  • showSnackbar (28-34)
packages/frontend/src/stores/access.control.ts (1)
packages/frontend/src/services/useAccessControlService.ts (1)
  • useAccessControlService (7-24)
packages/backend/src/app/common/auth/services/access.control.service.ts (4)
packages/shared/src/lib/auth/index.ts (1)
  • AccessControlResourceType (28-32)
packages/shared/src/lib/workspaces/index.ts (1)
  • Workspace (4-16)
packages/shared/src/lib/spaces/index.ts (1)
  • Space (3-10)
packages/shared/src/lib/lists/lists.ts (1)
  • List (4-15)
🔇 Additional comments (16)
packages/frontend/src/stores/notification.ts (1)

4-26: LGTM! Consistent persistence enhancement.

Adding { persist: true } to the notification store aligns with the pattern established in other stores like useAuthStore and provides a better user experience by persisting notification state across browser sessions.

packages/frontend/src/styles/styles.scss (1)

102-110: LGTM! Proper Vue transition implementation.

The CSS transition classes are correctly structured for Vue 3's transition system, providing smooth fade-in/fade-out effects for UI components like snackbars. The 0.5s duration provides good visual feedback without being too slow.

packages/backend/src/app/common/auth/services/auth.service.ts (1)

197-197: Verify the impact of role change across the system.

Similar to the registerWithInvite method, changing the default role from "admin" to "editor" for users joining invitations improves security. Ensure consistency across the access control system.

packages/backend/src/app/common/auth/controllers/auth.controller.ts (1)

2-8: Import path refactoring looks correct.

The relative import paths are properly structured and align with the directory reorganization mentioned in the AI summary. This change improves code maintainability by using relative imports within the auth module.

packages/backend/src/app/common/projects/project-users/project.user.entity.ts (1)

16-19:

❓ Verification inconclusive

Verify performance impact of eager loading both relations.

Adding eager: true to the project relation creates consistency with the user relation, but now both relations are eagerly loaded. This could lead to performance issues with complex queries or large datasets.

Please verify that this doesn't cause performance issues, especially in queries that fetch multiple ProjectUser entities:


🏁 Script executed:

#!/bin/bash
# Description: Check for ProjectUser queries that might be affected by double eager loading

# Search for ProjectUser repository usage and queries
ast-grep --pattern 'find$_($$$)' | grep -A 5 -B 5 ProjectUser
rg -A 10 'createQueryBuilder.*ProjectUser|find.*ProjectUser' --type ts

Length of output: 5832


Verify performance impact of double eager loading

Eager-loading both the project and user relations means every ProjectUser lookup will JOIN two tables. This may degrade performance on large result sets or complex filters.

Affected code paths:

  • packages/backend/src/app/common/lists/list-groups/list.groups.service.ts
    await this.listGroupsRepository.manager.find(ProjectUser, { … })
  • packages/backend/src/app/common/projects/project-users/project.users.service.ts
    findAll using this.projectUsersRepository.find()
    findOne using this.projectUsersRepository.findOne()

Recommendations:

  • Benchmark these queries with representative data volumes.
  • Introduce pagination or select-only-needed columns.
  • Consider switching to lazy loading or explicit joins for heavy queries.
packages/backend/src/app/common/auth/auth.module.ts (1)

4-4: Controller reorganization improves module structure.

The move to a controllers subdirectory and addition of AccessControlController properly organizes the authentication module for the new project member management features.

Also applies to: 20-20, 47-47

packages/frontend/src/stores/field.query.ts (1)

6-32: LGTM! Store persistence and naming improvements.

The changes properly update the store to use the standard two-argument defineStore format with explicit naming and add persistence functionality. This follows the same pattern applied to other stores in the codebase and will improve both debugging capabilities and user experience by maintaining state across sessions.

packages/backend/src/app/common/projects/project-users/project.users.service.ts (2)

102-115: Good permission management integration.

The update method properly saves the entity and maintains permission consistency through the AccessControlService. This ensures that role changes are reflected in the access control system.


128-135:

✅ Verification successful

Verify user update operation consistency.

The direct user update via repository manager could potentially conflict with other user operations or leave inconsistent state if the user is concurrently being modified elsewhere.


🏁 Script executed:

#!/bin/bash
# Description: Check if there are other places where user.project is being modified
# to ensure consistency across the codebase

# Search for other locations where user.project field is being updated
rg -A 5 -B 5 "project.*=.*null|project.*null" --type ts
rg -A 5 -B 5 "\.project\s*=" --type ts

Length of output: 4752


No conflicting backend updates to User.project detected

  • Backend: packages/backend/src/app/common/projects/project-users/project.users.service.ts (lines 128–135)

Our search found no other server‐side writes to User.project—only this update and client‐side state changes—so the direct repository update here won’t conflict with concurrent user operations.

packages/frontend/src/stores/query.ts (1)

17-19: LGTM! Persistence enhancement follows established patterns.

The update to enable store persistence is correctly implemented using Pinia's two-argument form with the { persist: true } option. This aligns with similar persistence enhancements made to other stores in the frontend and ensures a consistent user experience by retaining query state across sessions.

Also applies to: 88-89

packages/backend/src/app/common/auth/controllers/access.control.controller.ts (1)

1-20: LGTM! Well-structured controller with proper authentication.

The controller implementation follows NestJS best practices:

  • Security: JWT authentication guard ensures only authenticated users can access permissions
  • Structure: Proper dependency injection and controller decoration
  • Endpoint design: RESTful /me endpoint for user-specific data
  • Type safety: Correct use of User entity type with CurrentUser decorator

The authentication flow is secure as the user ID is extracted from the JWT token rather than being passed as a parameter, preventing unauthorized access to other users' permissions.

packages/frontend/src/services/useProjectUsersService.ts (1)

50-59: LGTM! Proper mutation implementation with cache invalidation.

The mutation hook is well-implemented:

  • Cache consistency: Correctly invalidates the project users query on successful deletion
  • Error handling: Relies on Vue Query's built-in error handling, which is appropriate
  • Type safety: Proper TypeScript parameter typing

The query invalidation ensures the UI stays synchronized with the backend state after user removal.

packages/frontend/src/components/common/base/BaseSnackbarWrapper.vue (1)

31-58: LGTM! The animation implementation is well-structured.

The implementation correctly uses <Teleport> to render snackbars in the overlay container and <Transition> for smooth animations. The two-phase close handling (marking as closing, then removing after animation) ensures proper cleanup.

packages/frontend/src/components/common/settings/ProjectMembersSettings.vue (3)

109-124: LGTM! Access control integration is well-implemented.

The conditional rendering of the context menu based on owner permissions is correctly implemented, ensuring that only project owners can see the remove member option.


52-60: Consider checking permissions before showing error for owner removal.

Since only owners can see the remove option, this check might be redundant. However, it's good defensive programming to keep it.


62-76: Good implementation of confirmation dialog and error handling.

The deletion flow properly uses a confirmation dialog and the tryCatch composable for error handling, providing a good user experience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Changes or additions to existing features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feat(Project): Ability to manage member
1 participant