-
Notifications
You must be signed in to change notification settings - Fork 3.4k
[WEB-4959]chore: refactor members page #8475
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // store | ||
| import type { IBaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store"; | ||
| import { BaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store"; | ||
| import type { RootStore } from "@/plane-web/store/root.store"; | ||
| import type { IMemberRootStore } from "@/store/member"; | ||
|
|
||
| export type IWorkspaceMemberStore = IBaseWorkspaceMemberStore; | ||
|
|
||
| export class WorkspaceMemberStore extends BaseWorkspaceMemberStore implements IWorkspaceMemberStore { | ||
| constructor(_memberRoot: IMemberRootStore, _rootStore: RootStore) { | ||
| super(_memberRoot, _rootStore); | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ export interface IWorkspaceMembership { | |
| is_active?: boolean; | ||
| } | ||
|
|
||
| export interface IWorkspaceMemberStore { | ||
| export interface IBaseWorkspaceMemberStore { | ||
| // observables | ||
| workspaceMemberMap: Record<string, Record<string, IWorkspaceMembership>>; | ||
| workspaceMemberInvitations: Record<string, IWorkspaceMemberInvitation[]>; | ||
|
|
@@ -57,7 +57,7 @@ export interface IWorkspaceMemberStore { | |
| isUserSuspended: (userId: string, workspaceSlug: string) => boolean; | ||
| } | ||
|
|
||
| export class WorkspaceMemberStore implements IWorkspaceMemberStore { | ||
| export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberStore { | ||
| // observables | ||
| workspaceMemberMap: { | ||
| [workspaceSlug: string]: Record<string, IWorkspaceMembership>; | ||
|
|
@@ -251,7 +251,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { | |
| * @param userId | ||
| * @param data | ||
| */ | ||
| updateMember = async (workspaceSlug: string, userId: string, data: { role: EUserPermissions }) => { | ||
| async updateMember(workspaceSlug: string, userId: string, data: { role: EUserPermissions }) { | ||
| const memberDetails = this.getWorkspaceMemberDetails(userId); | ||
| if (!memberDetails) throw new Error("Member not found"); | ||
| // original data to revert back in case of error | ||
|
|
@@ -261,30 +261,28 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { | |
| set(this.workspaceMemberMap, [workspaceSlug, userId, "role"], data.role); | ||
| }); | ||
| await this.workspaceService.updateWorkspaceMember(workspaceSlug, memberDetails.id, data); | ||
| void this.rootStore.workspaceRoot.mutateWorkspaceMembersActivity(workspaceSlug); | ||
| } catch (error) { | ||
| // revert back to original members in case of error | ||
| runInAction(() => { | ||
| set(this.workspaceMemberMap, [workspaceSlug, userId], originalProjectMemberData); | ||
| }); | ||
| throw error; | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * @description remove a member from workspace | ||
| * @param workspaceSlug | ||
| * @param userId | ||
| */ | ||
| removeMemberFromWorkspace = async (workspaceSlug: string, userId: string) => { | ||
| async removeMemberFromWorkspace(workspaceSlug: string, userId: string) { | ||
| const memberDetails = this.getWorkspaceMemberDetails(userId); | ||
| if (!memberDetails) throw new Error("Member not found"); | ||
| await this.workspaceService.deleteWorkspaceMember(workspaceSlug, memberDetails?.id); | ||
| runInAction(() => { | ||
| set(this.workspaceMemberMap, [workspaceSlug, userId, "is_active"], false); | ||
| }); | ||
| void this.rootStore.workspaceRoot.mutateWorkspaceMembersActivity(workspaceSlug); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * @description fetch all the member invitations of a workspace | ||
|
|
@@ -303,11 +301,10 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { | |
| * @param workspaceSlug | ||
| * @param data | ||
| */ | ||
| inviteMembersToWorkspace = async (workspaceSlug: string, data: IWorkspaceBulkInviteFormData) => { | ||
| async inviteMembersToWorkspace(workspaceSlug: string, data: IWorkspaceBulkInviteFormData) { | ||
| await this.workspaceService.inviteWorkspace(workspaceSlug, data); | ||
| await this.fetchWorkspaceMemberInvitations(workspaceSlug); | ||
| void this.rootStore.workspaceRoot.mutateWorkspaceMembersActivity(workspaceSlug); | ||
| }; | ||
| } | ||
|
Comment on lines
+304
to
+307
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing MobX action decorator for inviteMembersToWorkspace. The 🔎 Proposed fixAdd the action decorator in the makeObservable(this, {
// observables
workspaceMemberMap: observable,
workspaceMemberInvitations: observable,
// computed
workspaceMemberIds: computed,
workspaceMemberInvitationIds: computed,
memberMap: computed,
// actions
fetchWorkspaceMembers: action,
updateMember: action,
removeMemberFromWorkspace: action,
fetchWorkspaceMemberInvitations: action,
+ inviteMembersToWorkspace: action,
updateMemberInvitation: action,
deleteMemberInvitation: action,
});
🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * @description update the role of a member invitation | ||
|
|
@@ -345,15 +342,14 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { | |
| * @param workspaceSlug | ||
| * @param memberId | ||
| */ | ||
| deleteMemberInvitation = async (workspaceSlug: string, invitationId: string) => { | ||
| async deleteMemberInvitation(workspaceSlug: string, invitationId: string) { | ||
| await this.workspaceService.deleteWorkspaceInvitations(workspaceSlug.toString(), invitationId); | ||
| runInAction(() => { | ||
| this.workspaceMemberInvitations[workspaceSlug] = this.workspaceMemberInvitations[workspaceSlug].filter( | ||
| (inv) => inv.id !== invitationId | ||
| ); | ||
| }); | ||
| void this.rootStore.workspaceRoot.mutateWorkspaceMembersActivity(workspaceSlug); | ||
| }; | ||
| } | ||
|
|
||
| isUserSuspended = computedFn((userId: string, workspaceSlug: string) => { | ||
| if (!workspaceSlug) return false; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Methods lose
thisbinding after arrow-to-method conversionSeveral methods (
updateMember,removeMemberFromWorkspace,inviteMembersToWorkspace,deleteMemberInvitation,bulkAddMembersToProject,updateMemberRole,removeMemberFromProject) were converted from arrow functions to regular class methods. Throughout the codebase, these methods are destructured from stores and called as standalone functions (e.g.,const { removeMemberFromProject } = useMember().project; await removeMemberFromProject(...)). Regular methods lose theirthisbinding when destructured, causingthisto be undefined at runtime. This will crash when users try to update roles, remove members, or invite members.Additional Locations (2)
apps/web/core/store/member/project/base-project-member.store.ts#L306-L327apps/web/core/store/member/project/base-project-member.store.ts#L347-L401