@burdenoff/microfe-workspaces 2026.529.1 → 2026.529.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/useMemberMutations.js +2 -1
- package/dist/hooks/useMemberMutations.js.map +1 -1
- package/dist/hooks/useWorkspaceMembers.js +1 -3
- package/dist/hooks/useWorkspaceMembers.js.map +1 -1
- package/dist/pages/MembersListPage.js +166 -164
- package/dist/pages/MembersListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -114,7 +114,8 @@ var v = () => {
|
|
|
114
114
|
return t.data.createInvitation;
|
|
115
115
|
},
|
|
116
116
|
onSuccess: (e) => {
|
|
117
|
-
|
|
117
|
+
let t = e.workspaceId ?? s;
|
|
118
|
+
t && i.invalidateQueries({ queryKey: ["workspaceInvitations", t] }), o("workspace-invitation.created", {
|
|
118
119
|
invitationId: e.id,
|
|
119
120
|
workspaceId: e.workspaceId,
|
|
120
121
|
email: e.email,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMemberMutations.js","names":[],"sources":["../../src/hooks/useMemberMutations.ts"],"sourcesContent":["import { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n AddWorkspaceMemberDocument,\n RemoveWorkspaceMemberDocument,\n RemoveUserFromWorkspaceDocument,\n CreateInvitationDocument,\n UpdateInvitationDocument,\n AcceptInvitationDocument,\n RejectInvitationDocument,\n CancelInvitationDocument,\n ResendInvitationDocument,\n DeleteInvitationDocument,\n BulkCreateInvitationsDocument,\n} from '../generated/wspace-operations';\nimport type {\n WorkspaceMember,\n WorkspaceInvitation,\n AddWorkspaceMemberInput,\n CreateInvitationInput,\n UpdateInvitationInput,\n} from '../types';\nimport { useWorkspacesEventEmitter } from './useWorkspacesEventEmitter';\nimport { safeTelemetryError } from '../utils/telemetryError';\n\n/**\n * Hook to add a member to a workspace\n * Requires x-workspace-id header for workspace context\n */\nexport const useAddWorkspaceMember = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (input: AddWorkspaceMemberInput): Promise<WorkspaceMember> => {\n const result = await graphqlFetch<{ addWorkspaceMember: WorkspaceMember }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(AddWorkspaceMemberDocument),\n variables: { input },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.addWorkspaceMember;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers', data.workspaceId] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-member.added', {\n memberId: data.id,\n workspaceId: data.workspaceId,\n userId: data.userId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, input) => {\n emit('workspace-member.add_failed', {\n workspaceId: workspaceId || undefined,\n userId: input?.userId,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to remove a member from a workspace by member ID\n */\nexport const useRemoveWorkspaceMember = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (id: string): Promise<boolean> => {\n const result = await graphqlFetch<{ removeWorkspaceMember: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RemoveWorkspaceMemberDocument),\n variables: { id },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.removeWorkspaceMember;\n },\n onSuccess: (_, id) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-member.removed', { memberId: id, source: 'microfe-workspaces' });\n },\n onError: (err, id) => {\n emit('workspace-member.remove_failed', {\n memberId: id,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to remove a user from a workspace\n * Requires x-workspace-id header for workspace context\n */\nexport const useRemoveUserFromWorkspace = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (userId: string): Promise<boolean> => {\n const result = await graphqlFetch<{ removeUserFromWorkspace: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RemoveUserFromWorkspaceDocument),\n variables: { userId },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.removeUserFromWorkspace;\n },\n onSuccess: (_, userId) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces', userId] });\n emit('workspace-member.removed', { userId, source: 'microfe-workspaces' });\n },\n onError: (err, userId) => {\n emit('workspace-member.remove_failed', {\n userId,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * NOTE: The updateWorkspaceMemberRoles mutation has been removed from the backend.\n * Role management is now handled via invitations - when creating an invitation,\n * you can specify roleIds that will be assigned when the invitation is accepted.\n *\n * Use CreateInvitationInput.roleIds to assign roles to new members.\n */\n\n/**\n * Hook to create a workspace invitation\n * Uses x-workspace-id header as fallback when input.workspaceId is not provided\n */\nexport const useCreateInvitation = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (input: CreateInvitationInput): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ createInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(CreateInvitationDocument),\n variables: { input },\n workspaceId: input.workspaceId || effectiveWorkspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header fallback\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.createInvitation;\n },\n onSuccess: (data) => {\n if (data.workspaceId) {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations', data.workspaceId] });\n }\n emit('workspace-invitation.created', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n email: data.email,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, input) => {\n emit('workspace-invitation.create_failed', {\n workspaceId: input?.workspaceId || effectiveWorkspaceId || undefined,\n email: input?.email,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to update an invitation\n */\nexport const useUpdateInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async ({\n id,\n input,\n }: {\n id: string;\n input: UpdateInvitationInput;\n }): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ updateInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(UpdateInvitationDocument),\n variables: { id, input },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.updateInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.updated', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, variables) => {\n emit('workspace-invitation.update_failed', {\n invitationId: variables?.id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to accept an invitation\n */\nexport const useAcceptInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ acceptInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(AcceptInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.acceptInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-invitation.accepted', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.accept_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to reject an invitation\n */\nexport const useRejectInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ rejectInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RejectInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.rejectInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.rejected', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.reject_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to cancel an invitation\n */\nexport const useCancelInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ cancelInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(CancelInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.cancelInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.cancelled', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.cancel_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to resend an invitation\n */\nexport const useResendInvitation = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ resendInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(ResendInvitationDocument),\n variables: { id },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.resendInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.resent', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.resend_failed', {\n invitationId: id,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to delete an invitation\n */\nexport const useDeleteInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<boolean> => {\n const result = await graphqlFetch<{ deleteInvitation: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(DeleteInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.deleteInvitation;\n },\n onSuccess: (_, id) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.deleted', { invitationId: id, source: 'microfe-workspaces' });\n },\n onError: (err, id) => {\n emit('workspace-invitation.delete_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to bulk create invitations\n * Uses x-workspace-id header as fallback when inputs[].workspaceId is not provided\n */\nexport const useBulkCreateInvitations = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async (inputs: CreateInvitationInput[]): Promise<WorkspaceInvitation[]> => {\n const result = await graphqlFetch<{ bulkCreateInvitations: WorkspaceInvitation[] }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(BulkCreateInvitationsDocument),\n variables: { inputs },\n workspaceId: inputs[0]?.workspaceId || workspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header fallback\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.bulkCreateInvitations;\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n },\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMemberMutations.js","names":[],"sources":["../../src/hooks/useMemberMutations.ts"],"sourcesContent":["import { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n AddWorkspaceMemberDocument,\n RemoveWorkspaceMemberDocument,\n RemoveUserFromWorkspaceDocument,\n CreateInvitationDocument,\n UpdateInvitationDocument,\n AcceptInvitationDocument,\n RejectInvitationDocument,\n CancelInvitationDocument,\n ResendInvitationDocument,\n DeleteInvitationDocument,\n BulkCreateInvitationsDocument,\n} from '../generated/wspace-operations';\nimport type {\n WorkspaceMember,\n WorkspaceInvitation,\n AddWorkspaceMemberInput,\n CreateInvitationInput,\n UpdateInvitationInput,\n} from '../types';\nimport { useWorkspacesEventEmitter } from './useWorkspacesEventEmitter';\nimport { safeTelemetryError } from '../utils/telemetryError';\n\n/**\n * Hook to add a member to a workspace\n * Requires x-workspace-id header for workspace context\n */\nexport const useAddWorkspaceMember = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (input: AddWorkspaceMemberInput): Promise<WorkspaceMember> => {\n const result = await graphqlFetch<{ addWorkspaceMember: WorkspaceMember }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(AddWorkspaceMemberDocument),\n variables: { input },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.addWorkspaceMember;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers', data.workspaceId] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-member.added', {\n memberId: data.id,\n workspaceId: data.workspaceId,\n userId: data.userId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, input) => {\n emit('workspace-member.add_failed', {\n workspaceId: workspaceId || undefined,\n userId: input?.userId,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to remove a member from a workspace by member ID\n */\nexport const useRemoveWorkspaceMember = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (id: string): Promise<boolean> => {\n const result = await graphqlFetch<{ removeWorkspaceMember: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RemoveWorkspaceMemberDocument),\n variables: { id },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.removeWorkspaceMember;\n },\n onSuccess: (_, id) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-member.removed', { memberId: id, source: 'microfe-workspaces' });\n },\n onError: (err, id) => {\n emit('workspace-member.remove_failed', {\n memberId: id,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to remove a user from a workspace\n * Requires x-workspace-id header for workspace context\n */\nexport const useRemoveUserFromWorkspace = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (userId: string): Promise<boolean> => {\n const result = await graphqlFetch<{ removeUserFromWorkspace: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RemoveUserFromWorkspaceDocument),\n variables: { userId },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.removeUserFromWorkspace;\n },\n onSuccess: (_, userId) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces', userId] });\n emit('workspace-member.removed', { userId, source: 'microfe-workspaces' });\n },\n onError: (err, userId) => {\n emit('workspace-member.remove_failed', {\n userId,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * NOTE: The updateWorkspaceMemberRoles mutation has been removed from the backend.\n * Role management is now handled via invitations - when creating an invitation,\n * you can specify roleIds that will be assigned when the invitation is accepted.\n *\n * Use CreateInvitationInput.roleIds to assign roles to new members.\n */\n\n/**\n * Hook to create a workspace invitation\n * Uses x-workspace-id header as fallback when input.workspaceId is not provided\n */\nexport const useCreateInvitation = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (input: CreateInvitationInput): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ createInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(CreateInvitationDocument),\n variables: { input },\n workspaceId: input.workspaceId || effectiveWorkspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header fallback\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.createInvitation;\n },\n onSuccess: (data) => {\n const wsId = data.workspaceId ?? effectiveWorkspaceId;\n if (wsId) {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations', wsId] });\n }\n emit('workspace-invitation.created', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n email: data.email,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, input) => {\n emit('workspace-invitation.create_failed', {\n workspaceId: input?.workspaceId || effectiveWorkspaceId || undefined,\n email: input?.email,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to update an invitation\n */\nexport const useUpdateInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async ({\n id,\n input,\n }: {\n id: string;\n input: UpdateInvitationInput;\n }): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ updateInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(UpdateInvitationDocument),\n variables: { id, input },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.updateInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.updated', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, variables) => {\n emit('workspace-invitation.update_failed', {\n invitationId: variables?.id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to accept an invitation\n */\nexport const useAcceptInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ acceptInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(AcceptInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.acceptInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n queryClient.invalidateQueries({ queryKey: ['workspaceMembers'] });\n queryClient.invalidateQueries({ queryKey: ['userWorkspaces'] });\n emit('workspace-invitation.accepted', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.accept_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to reject an invitation\n */\nexport const useRejectInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ rejectInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(RejectInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.rejectInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.rejected', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.reject_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to cancel an invitation\n */\nexport const useCancelInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ cancelInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(CancelInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.cancelInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.cancelled', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.cancel_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to resend an invitation\n */\nexport const useResendInvitation = (workspaceIdOverride?: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n const effectiveWorkspaceId = workspaceIdOverride || workspaceId;\n\n return useMutation({\n mutationFn: async (id: string): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ resendInvitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(ResendInvitationDocument),\n variables: { id },\n workspaceId: effectiveWorkspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.resendInvitation;\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.resent', {\n invitationId: data.id,\n workspaceId: data.workspaceId,\n source: 'microfe-workspaces',\n });\n },\n onError: (err, id) => {\n emit('workspace-invitation.resend_failed', {\n invitationId: id,\n workspaceId: effectiveWorkspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to delete an invitation\n */\nexport const useDeleteInvitation = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n const { emit } = useWorkspacesEventEmitter();\n\n return useMutation({\n mutationFn: async (id: string): Promise<boolean> => {\n const result = await graphqlFetch<{ deleteInvitation: boolean }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(DeleteInvitationDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.deleteInvitation;\n },\n onSuccess: (_, id) => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n emit('workspace-invitation.deleted', { invitationId: id, source: 'microfe-workspaces' });\n },\n onError: (err, id) => {\n emit('workspace-invitation.delete_failed', {\n invitationId: id,\n workspaceId: workspaceId || undefined,\n source: 'microfe-workspaces',\n errorMessage: safeTelemetryError(err),\n });\n },\n });\n};\n\n/**\n * Hook to bulk create invitations\n * Uses x-workspace-id header as fallback when inputs[].workspaceId is not provided\n */\nexport const useBulkCreateInvitations = () => {\n const { authToken, workspaceId } = useWorkspacesContext();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async (inputs: CreateInvitationInput[]): Promise<WorkspaceInvitation[]> => {\n const result = await graphqlFetch<{ bulkCreateInvitations: WorkspaceInvitation[] }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(BulkCreateInvitationsDocument),\n variables: { inputs },\n workspaceId: inputs[0]?.workspaceId || workspaceId || undefined,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header fallback\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.bulkCreateInvitations;\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['workspaceInvitations'] });\n },\n });\n};\n"],"mappings":"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"}
|
|
@@ -111,9 +111,7 @@ var l = (t, n) => {
|
|
|
111
111
|
gateway: "workspace",
|
|
112
112
|
authToken: i || void 0,
|
|
113
113
|
query: s(t),
|
|
114
|
-
variables: { id: n }
|
|
115
|
-
workspaceId: a || void 0,
|
|
116
|
-
workspaceToken: !0
|
|
114
|
+
variables: { id: n }
|
|
117
115
|
});
|
|
118
116
|
if (e.errors?.length) throw Error(e.errors[0]?.message || "Failed to fetch invitation");
|
|
119
117
|
if (!e.data?.invitation) throw Error("Invitation not found");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWorkspaceMembers.js","names":[],"sources":["../../src/hooks/useWorkspaceMembers.ts"],"sourcesContent":["import { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n GetWorkspaceMembersDocument,\n GetWorkspaceMemberDocument,\n GetUserWorkspacesDocument,\n GetWorkspaceInvitationsDocument,\n GetInvitationDocument,\n} from '../generated/wspace-operations';\nimport type {\n MemberList,\n WorkspaceMember,\n WorkspaceInvitation,\n InvitationList,\n PaginationInput,\n InvitationStatus,\n} from '../types';\n\n/**\n * Hook to fetch all members of the current workspace\n */\nexport const useWorkspaceMembers = (workspaceId: string, pagination?: PaginationInput) => {\n const { authToken } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceMembers', workspaceId, pagination],\n queryFn: async (): Promise<MemberList> => {\n const result = await graphqlFetch<{ workspaceMembers: MemberList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceMembersDocument),\n variables: { workspaceId, pagination },\n workspaceId,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header (required by gateway RBAC)\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceMembers;\n },\n enabled: !!workspaceId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch a single workspace member by ID\n */\nexport const useWorkspaceMember = (id: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceMember', id],\n queryFn: async (): Promise<WorkspaceMember> => {\n const result = await graphqlFetch<{ workspaceMember: WorkspaceMember }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceMemberDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceMember;\n },\n enabled: !!id,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch all workspaces a user is a member of\n */\nexport const useUserWorkspaces = (userId: string, pagination?: PaginationInput) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['userWorkspaces', userId, pagination],\n queryFn: async (): Promise<MemberList> => {\n const result = await graphqlFetch<{ userWorkspaces: MemberList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetUserWorkspacesDocument),\n variables: { userId, pagination },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.userWorkspaces;\n },\n enabled: !!userId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch workspace invitations\n * Requires x-workspace-id header for workspace context\n */\nexport const useWorkspaceInvitations = (\n workspaceId: string,\n status?: InvitationStatus,\n pagination?: PaginationInput\n) => {\n const { authToken } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceInvitations', workspaceId, status, pagination],\n queryFn: async (): Promise<InvitationList> => {\n const result = await graphqlFetch<{ workspaceInvitations: InvitationList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceInvitationsDocument),\n variables: { status, pagination },\n workspaceId,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceInvitations;\n },\n enabled: !!workspaceId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to get a single invitation by ID (for acceptance page)\n */\nexport const useInvitation = (id: string, enabled = true) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['invitation', id],\n queryFn: async (): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ invitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetInvitationDocument),\n variables: { id },\n
|
|
1
|
+
{"version":3,"file":"useWorkspaceMembers.js","names":[],"sources":["../../src/hooks/useWorkspaceMembers.ts"],"sourcesContent":["import { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n GetWorkspaceMembersDocument,\n GetWorkspaceMemberDocument,\n GetUserWorkspacesDocument,\n GetWorkspaceInvitationsDocument,\n GetInvitationDocument,\n} from '../generated/wspace-operations';\nimport type {\n MemberList,\n WorkspaceMember,\n WorkspaceInvitation,\n InvitationList,\n PaginationInput,\n InvitationStatus,\n} from '../types';\n\n/**\n * Hook to fetch all members of the current workspace\n */\nexport const useWorkspaceMembers = (workspaceId: string, pagination?: PaginationInput) => {\n const { authToken } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceMembers', workspaceId, pagination],\n queryFn: async (): Promise<MemberList> => {\n const result = await graphqlFetch<{ workspaceMembers: MemberList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceMembersDocument),\n variables: { workspaceId, pagination },\n workspaceId,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header (required by gateway RBAC)\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceMembers;\n },\n enabled: !!workspaceId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch a single workspace member by ID\n */\nexport const useWorkspaceMember = (id: string) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceMember', id],\n queryFn: async (): Promise<WorkspaceMember> => {\n const result = await graphqlFetch<{ workspaceMember: WorkspaceMember }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceMemberDocument),\n variables: { id },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceMember;\n },\n enabled: !!id,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch all workspaces a user is a member of\n */\nexport const useUserWorkspaces = (userId: string, pagination?: PaginationInput) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['userWorkspaces', userId, pagination],\n queryFn: async (): Promise<MemberList> => {\n const result = await graphqlFetch<{ userWorkspaces: MemberList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetUserWorkspacesDocument),\n variables: { userId, pagination },\n workspaceId: workspaceId || undefined,\n workspaceToken: true, // Required by gateway RBAC for wspace-scoped operations\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.userWorkspaces;\n },\n enabled: !!userId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to fetch workspace invitations\n * Requires x-workspace-id header for workspace context\n */\nexport const useWorkspaceInvitations = (\n workspaceId: string,\n status?: InvitationStatus,\n pagination?: PaginationInput\n) => {\n const { authToken } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['workspaceInvitations', workspaceId, status, pagination],\n queryFn: async (): Promise<InvitationList> => {\n const result = await graphqlFetch<{ workspaceInvitations: InvitationList }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetWorkspaceInvitationsDocument),\n variables: { status, pagination },\n workspaceId,\n workspaceToken: true, // Auto-read from profile context for x-workspace-id header\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'GraphQL error');\n }\n\n return result.data!.workspaceInvitations;\n },\n enabled: !!workspaceId,\n staleTime: 5 * 60 * 1000,\n });\n};\n\n/**\n * Hook to get a single invitation by ID (for acceptance page)\n */\nexport const useInvitation = (id: string, enabled = true) => {\n const { authToken, workspaceId } = useWorkspacesContext();\n\n return useQuery({\n queryKey: ['invitation', id],\n queryFn: async (): Promise<WorkspaceInvitation> => {\n const result = await graphqlFetch<{ invitation: WorkspaceInvitation }>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: print(GetInvitationDocument),\n variables: { id },\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to fetch invitation');\n }\n\n if (!result.data?.invitation) {\n throw new Error('Invitation not found');\n }\n\n return result.data.invitation;\n },\n enabled: !!id && enabled,\n retry: 1,\n staleTime: 5 * 60 * 1000,\n });\n};\n"],"mappings":"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"}
|
|
@@ -2,34 +2,34 @@ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
|
|
|
2
2
|
import { useWorkspacesStore as t } from "../store/workspacesStore.js";
|
|
3
3
|
import { WORKSPACE_PERMISSIONS as n } from "../constants/permissions.js";
|
|
4
4
|
import { tWithFallback as r } from "../utils/i18n.js";
|
|
5
|
-
import { useWorkspacesPageView as
|
|
6
|
-
import { nativeConfirm as
|
|
7
|
-
import { useWorkspaceInvitations as
|
|
8
|
-
import { useRemoveUserFromWorkspace as
|
|
9
|
-
import { MemberListItem as
|
|
10
|
-
import { InviteMemberDialog as
|
|
11
|
-
import { useWorkspacePermissions as
|
|
12
|
-
import { useEffect as
|
|
13
|
-
import { AlertCircle as
|
|
14
|
-
import { jsx as
|
|
15
|
-
import { Badge as
|
|
16
|
-
import { useParams as
|
|
17
|
-
import { useDebounce as
|
|
18
|
-
import { PermissionButton as
|
|
19
|
-
import { useI18n as
|
|
20
|
-
import { useActorProfiles as
|
|
5
|
+
import { useWorkspacesPageView as i } from "../hooks/useWorkspacesPageView.js";
|
|
6
|
+
import { nativeConfirm as a, nativeImpact as o } from "../utils/nativeBridge.js";
|
|
7
|
+
import { useWorkspaceInvitations as s, useWorkspaceMembers as ee } from "../hooks/useWorkspaceMembers.js";
|
|
8
|
+
import { useRemoveUserFromWorkspace as te, useResendInvitation as ne } from "../hooks/useMemberMutations.js";
|
|
9
|
+
import { MemberListItem as re } from "../components/MemberListItem.js";
|
|
10
|
+
import { InviteMemberDialog as ie } from "../components/InviteMemberDialog.js";
|
|
11
|
+
import { useWorkspacePermissions as ae } from "../hooks/useWorkspacePermissions.js";
|
|
12
|
+
import { useEffect as c, useMemo as l, useState as u } from "react";
|
|
13
|
+
import { AlertCircle as oe, RefreshCw as se, Search as d, UserPlus as f, Users as p } from "lucide-react";
|
|
14
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
15
|
+
import { Badge as g, Button as _, Input as ce, PageContainer as le, Skeleton as v } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
import { useParams as ue, useSearchParams as de } from "react-router-dom";
|
|
17
|
+
import { useDebounce as fe } from "@burdenoff/fe-libs/shared/hooks";
|
|
18
|
+
import { PermissionButton as y } from "@burdenoff/fe-libs/shared/components";
|
|
19
|
+
import { useI18n as pe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
20
|
+
import { useActorProfiles as me } from "@burdenoff/fe-libs/shared";
|
|
21
21
|
//#region src/pages/MembersListPage.tsx
|
|
22
|
-
function
|
|
23
|
-
let { t:
|
|
24
|
-
|
|
25
|
-
workspaceId:
|
|
22
|
+
function b() {
|
|
23
|
+
let { t: b } = pe(), { memberFilters: he, setMemberFilters: x, selectedMemberIds: S, toggleMemberSelection: C, clearMemberSelection: w, openInviteMemberDialog: T } = t(), { workspaceId: ge } = e(), { workspaceId: _e } = ue(), E = _e || ge;
|
|
24
|
+
i("workspaces.members", {
|
|
25
|
+
workspaceId: E,
|
|
26
26
|
requireParams: !0
|
|
27
27
|
});
|
|
28
|
-
let [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}, [
|
|
32
|
-
let { data:
|
|
28
|
+
let [D, ve] = u(he.search || ""), O = fe(D, 500);
|
|
29
|
+
c(() => {
|
|
30
|
+
x({ search: O });
|
|
31
|
+
}, [O, x]);
|
|
32
|
+
let { data: k, isLoading: ye, error: A } = ee(E || ""), { data: j, isLoading: be } = s(E || "", "PENDING"), { mutate: M, isPending: N } = ne(), { mutate: xe, isPending: Se } = te(), [P, F] = u(null), [I, L] = u(/* @__PURE__ */ new Set()), Ce = (e) => {
|
|
33
33
|
F(e), M(e, {
|
|
34
34
|
onSuccess: () => {
|
|
35
35
|
F(null);
|
|
@@ -38,7 +38,7 @@ function _() {
|
|
|
38
38
|
console.error("Failed to resend invitation:", e), F(null);
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
|
-
}, R =
|
|
41
|
+
}, R = l(() => k?.items || [], [k?.items]), z = l(() => j?.items || [], [j?.items]), B = ye || be, V = R.length > 0, H = z.length > 0, { apiGatewayUrl: we, authToken: Te, organizationId: Ee } = e(), De = me(l(() => R.map((e) => ({
|
|
42
42
|
actorId: e.userId,
|
|
43
43
|
actorType: "user"
|
|
44
44
|
})), [R]), {
|
|
@@ -46,47 +46,49 @@ function _() {
|
|
|
46
46
|
authToken: Te,
|
|
47
47
|
orgId: Ee
|
|
48
48
|
}), U = R.filter((e) => {
|
|
49
|
-
if (!
|
|
50
|
-
let t =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
if (!O) return !0;
|
|
50
|
+
let t = O.toLowerCase(), n = De[e.userId];
|
|
51
|
+
if (n && (n.displayName.toLowerCase().includes(t) || n.email?.toLowerCase().includes(t))) return !0;
|
|
52
|
+
let r = e.context?.metadata, i = typeof r?.email == "string" ? r.email : "", a = typeof r?.displayName == "string" ? r.displayName : typeof r?.name == "string" ? r.name : "";
|
|
53
|
+
return i.toLowerCase().includes(t) || a.toLowerCase().includes(t) ? !0 : e.userId.toLowerCase().includes(t);
|
|
54
|
+
}), W = l(() => U.filter((e) => S.has(e.id)), [U, S]), G = l(() => z.filter((e) => I.has(e.id)), [z, I]), K = U.length > 0 && U.every((e) => S.has(e.id)), Oe = U.some((e) => S.has(e.id)) && !K, q = z.length > 0 && z.every((e) => I.has(e.id)), J = z.some((e) => I.has(e.id)) && !q;
|
|
55
|
+
c(() => () => w(), [w]);
|
|
56
|
+
let [Y, X] = de();
|
|
57
|
+
c(() => {
|
|
58
|
+
if (Y.get("invite") === "true" && E) {
|
|
59
|
+
T({ id: E });
|
|
58
60
|
let e = new URLSearchParams(Y);
|
|
59
61
|
e.delete("invite"), X(e, { replace: !0 });
|
|
60
62
|
}
|
|
61
63
|
}, [
|
|
62
64
|
Y,
|
|
63
65
|
X,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
E,
|
|
67
|
+
T
|
|
66
68
|
]);
|
|
67
|
-
let { canListMembers: ke, canInviteMember: Z, canRemoveMember: Q, isLoading: Ae } =
|
|
68
|
-
if (Ae) return /* @__PURE__ */
|
|
69
|
+
let { canListMembers: ke, canInviteMember: Z, canRemoveMember: Q, isLoading: Ae } = ae();
|
|
70
|
+
if (Ae) return /* @__PURE__ */ h("div", {
|
|
69
71
|
className: "space-y-4 p-6",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
72
|
+
children: [/* @__PURE__ */ m(v, { className: "h-10 w-1/3" }), /* @__PURE__ */ m(v, { className: "h-64" })]
|
|
71
73
|
});
|
|
72
|
-
if (!ke && !Z) return /* @__PURE__ */
|
|
74
|
+
if (!ke && !Z) return /* @__PURE__ */ m("div", {
|
|
73
75
|
className: "flex items-center justify-center min-h-[400px]",
|
|
74
|
-
children: /* @__PURE__ */
|
|
76
|
+
children: /* @__PURE__ */ h("div", {
|
|
75
77
|
className: "text-center space-y-2",
|
|
76
|
-
children: [/* @__PURE__ */
|
|
78
|
+
children: [/* @__PURE__ */ m("h2", {
|
|
77
79
|
className: "text-lg font-semibold",
|
|
78
|
-
children: r(
|
|
79
|
-
}), /* @__PURE__ */
|
|
80
|
+
children: r(b, "membersList.accessDenied", "Access Denied")
|
|
81
|
+
}), /* @__PURE__ */ m("p", {
|
|
80
82
|
className: "text-sm text-muted-foreground",
|
|
81
|
-
children: r(
|
|
83
|
+
children: r(b, "membersList.noPermissionToViewMembers", "You don't have permission to view workspace members.")
|
|
82
84
|
})]
|
|
83
85
|
})
|
|
84
86
|
});
|
|
85
87
|
let $ = () => {
|
|
86
|
-
|
|
88
|
+
E && T({ id: E });
|
|
87
89
|
}, je = (e) => {
|
|
88
90
|
U.forEach((t) => {
|
|
89
|
-
e !==
|
|
91
|
+
e !== S.has(t.id) && C(t.id);
|
|
90
92
|
});
|
|
91
93
|
}, Me = (e, t) => {
|
|
92
94
|
L((n) => {
|
|
@@ -102,8 +104,8 @@ function _() {
|
|
|
102
104
|
});
|
|
103
105
|
}, Pe = async () => {
|
|
104
106
|
if (W.length === 0) return;
|
|
105
|
-
|
|
106
|
-
let e = `${r(
|
|
107
|
+
o("medium");
|
|
108
|
+
let e = `${r(b, "membersList.bulkRemoveConfirmPrefix", "Remove")} ${W.length} ${W.length === 1 ? r(b, "membersList.bulkRemoveConfirmSingular", "selected member") : r(b, "membersList.bulkRemoveConfirmPlural", "selected members")} ${r(b, "membersList.bulkRemoveConfirmSuffix", "from this workspace?")}`, t = await a({
|
|
107
109
|
title: "Remove members",
|
|
108
110
|
message: e,
|
|
109
111
|
okButtonTitle: "Remove",
|
|
@@ -111,7 +113,7 @@ function _() {
|
|
|
111
113
|
});
|
|
112
114
|
(t === null ? window.confirm(e) : t) && (W.forEach((e) => {
|
|
113
115
|
xe(e.userId);
|
|
114
|
-
}),
|
|
116
|
+
}), w());
|
|
115
117
|
}, Fe = () => {
|
|
116
118
|
G.length !== 0 && G.forEach((e) => {
|
|
117
119
|
M(e.id, { onSuccess: () => {
|
|
@@ -122,102 +124,102 @@ function _() {
|
|
|
122
124
|
} });
|
|
123
125
|
});
|
|
124
126
|
};
|
|
125
|
-
return /* @__PURE__ */
|
|
127
|
+
return /* @__PURE__ */ h(le, {
|
|
126
128
|
padded: !1,
|
|
127
|
-
children: [/* @__PURE__ */
|
|
129
|
+
children: [/* @__PURE__ */ h("div", {
|
|
128
130
|
className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
|
|
129
131
|
children: [
|
|
130
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ h("div", {
|
|
131
133
|
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
133
135
|
className: "text-2xl sm:text-3xl font-bold",
|
|
134
|
-
children: r(
|
|
135
|
-
}), /* @__PURE__ */
|
|
136
|
+
children: r(b, "membersList.pageTitle", "Members")
|
|
137
|
+
}), /* @__PURE__ */ m("p", {
|
|
136
138
|
className: "text-muted-foreground text-sm sm:text-base",
|
|
137
|
-
children: r(
|
|
138
|
-
})] }), /* @__PURE__ */
|
|
139
|
+
children: r(b, "membersList.pageSubtitle", "Manage workspace members and invitations")
|
|
140
|
+
})] }), /* @__PURE__ */ m("div", {
|
|
139
141
|
className: "flex gap-2",
|
|
140
|
-
children: /* @__PURE__ */
|
|
142
|
+
children: /* @__PURE__ */ m(y, {
|
|
141
143
|
permission: n.MEMBER_CREATE,
|
|
142
144
|
deniedBehavior: "disable",
|
|
143
|
-
deniedTooltip: r(
|
|
144
|
-
children: /* @__PURE__ */
|
|
145
|
+
deniedTooltip: r(b, "membersList.noPermissionToInvite", "You don't have permission to invite members"),
|
|
146
|
+
children: /* @__PURE__ */ h(_, {
|
|
145
147
|
onClick: $,
|
|
146
|
-
disabled: !
|
|
147
|
-
children: [/* @__PURE__ */
|
|
148
|
+
disabled: !E,
|
|
149
|
+
children: [/* @__PURE__ */ m(f, { className: "size-4 sm:mr-2" }), /* @__PURE__ */ m("span", {
|
|
148
150
|
className: "hidden sm:inline",
|
|
149
|
-
children: r(
|
|
151
|
+
children: r(b, "membersList.inviteMember", "Invite Member")
|
|
150
152
|
})]
|
|
151
153
|
})
|
|
152
154
|
})
|
|
153
155
|
})]
|
|
154
156
|
}),
|
|
155
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ h("div", {
|
|
156
158
|
className: "flex flex-col sm:flex-row gap-3 sm:gap-4",
|
|
157
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ h("div", {
|
|
158
160
|
className: "relative flex-1",
|
|
159
|
-
children: [/* @__PURE__ */ d
|
|
160
|
-
placeholder: r(
|
|
161
|
-
value:
|
|
162
|
-
onChange: (e) =>
|
|
161
|
+
children: [/* @__PURE__ */ m(d, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" }), /* @__PURE__ */ m(ce, {
|
|
162
|
+
placeholder: r(b, "membersList.searchPlaceholder", "Search members..."),
|
|
163
|
+
value: D,
|
|
164
|
+
onChange: (e) => ve(e.target.value),
|
|
163
165
|
className: "pl-9"
|
|
164
166
|
})]
|
|
165
|
-
}), H && /* @__PURE__ */
|
|
167
|
+
}), H && /* @__PURE__ */ m("div", {
|
|
166
168
|
className: "flex items-center gap-2",
|
|
167
|
-
children: /* @__PURE__ */
|
|
169
|
+
children: /* @__PURE__ */ h(g, {
|
|
168
170
|
variant: "secondary",
|
|
169
171
|
className: "text-sm",
|
|
170
172
|
children: [
|
|
171
173
|
z.length,
|
|
172
174
|
" ",
|
|
173
|
-
z.length === 1 ? r(
|
|
175
|
+
z.length === 1 ? r(b, "membersList.pendingInvitation", "Pending Invitation") : r(b, "membersList.pendingInvitations", "Pending Invitations")
|
|
174
176
|
]
|
|
175
177
|
})
|
|
176
178
|
})]
|
|
177
179
|
}),
|
|
178
|
-
U.length > 0 && /* @__PURE__ */
|
|
180
|
+
U.length > 0 && /* @__PURE__ */ h("div", {
|
|
179
181
|
className: "flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
180
|
-
children: [/* @__PURE__ */
|
|
182
|
+
children: [/* @__PURE__ */ h("label", {
|
|
181
183
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
182
|
-
children: [/* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ m("input", {
|
|
183
185
|
type: "checkbox",
|
|
184
186
|
checked: K,
|
|
185
187
|
ref: (e) => {
|
|
186
188
|
e && (e.indeterminate = Oe && !K);
|
|
187
189
|
},
|
|
188
190
|
onChange: (e) => je(e.target.checked),
|
|
189
|
-
"aria-label": r(
|
|
191
|
+
"aria-label": r(b, "membersList.selectAllMembersAriaLabel", "Select all workspace members"),
|
|
190
192
|
className: "size-4 rounded border-border text-primary"
|
|
191
|
-
}), /* @__PURE__ */
|
|
192
|
-
}), /* @__PURE__ */
|
|
193
|
+
}), /* @__PURE__ */ m("span", { children: r(b, "membersList.selectAllVisibleMembers", "Select all visible members") })]
|
|
194
|
+
}), /* @__PURE__ */ h("div", {
|
|
193
195
|
className: "flex flex-wrap items-center gap-2",
|
|
194
196
|
children: [
|
|
195
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ h("span", {
|
|
196
198
|
className: "text-sm text-muted-foreground",
|
|
197
199
|
children: [
|
|
198
|
-
|
|
200
|
+
S.size,
|
|
199
201
|
" ",
|
|
200
|
-
r(
|
|
202
|
+
r(b, "membersList.selected", "selected")
|
|
201
203
|
]
|
|
202
204
|
}),
|
|
203
|
-
|
|
205
|
+
S.size > 0 && /* @__PURE__ */ m(_, {
|
|
204
206
|
variant: "ghost",
|
|
205
207
|
size: "sm",
|
|
206
|
-
onClick:
|
|
207
|
-
children: r(
|
|
208
|
+
onClick: w,
|
|
209
|
+
children: r(b, "membersList.clearSelection", "Clear selection")
|
|
208
210
|
}),
|
|
209
|
-
Q && /* @__PURE__ */
|
|
211
|
+
Q && /* @__PURE__ */ h(_, {
|
|
210
212
|
variant: "outline",
|
|
211
213
|
size: "sm",
|
|
212
214
|
onClick: Pe,
|
|
213
215
|
disabled: W.length === 0 || Se,
|
|
214
216
|
className: "text-destructive",
|
|
215
|
-
children: [r(
|
|
217
|
+
children: [r(b, "membersList.removeSelected", "Remove Selected"), W.length > 0 ? ` (${W.length})` : ""]
|
|
216
218
|
})
|
|
217
219
|
]
|
|
218
220
|
})]
|
|
219
221
|
}),
|
|
220
|
-
B && /* @__PURE__ */
|
|
222
|
+
B && /* @__PURE__ */ m("div", {
|
|
221
223
|
className: "space-y-3",
|
|
222
224
|
children: [...[
|
|
223
225
|
,
|
|
@@ -225,217 +227,217 @@ function _() {
|
|
|
225
227
|
,
|
|
226
228
|
,
|
|
227
229
|
,
|
|
228
|
-
]].map((e, t) => /* @__PURE__ */
|
|
230
|
+
]].map((e, t) => /* @__PURE__ */ m(v, { className: "h-20 w-full" }, t))
|
|
229
231
|
}),
|
|
230
|
-
!B &&
|
|
232
|
+
!B && A && /* @__PURE__ */ h("div", {
|
|
231
233
|
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
232
234
|
children: [
|
|
233
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ m("div", {
|
|
234
236
|
className: "rounded-full bg-destructive/10 p-6 mb-4",
|
|
235
|
-
children: /* @__PURE__ */
|
|
237
|
+
children: /* @__PURE__ */ m(oe, {
|
|
236
238
|
size: 48,
|
|
237
239
|
className: "text-destructive"
|
|
238
240
|
})
|
|
239
241
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ m("h3", {
|
|
241
243
|
className: "text-lg font-semibold mb-2",
|
|
242
|
-
children: r(
|
|
244
|
+
children: r(b, "membersList.failedToLoad", "Failed to Load Members")
|
|
243
245
|
}),
|
|
244
|
-
/* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ m("p", {
|
|
245
247
|
className: "text-muted-foreground max-w-sm",
|
|
246
|
-
children:
|
|
248
|
+
children: A instanceof Error ? A.message : r(b, "membersList.errorLoadingMembers", "An error occurred while loading members")
|
|
247
249
|
})
|
|
248
250
|
]
|
|
249
251
|
}),
|
|
250
|
-
!B && !
|
|
252
|
+
!B && !A && !E && /* @__PURE__ */ h("div", {
|
|
251
253
|
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
252
254
|
children: [
|
|
253
|
-
/* @__PURE__ */
|
|
255
|
+
/* @__PURE__ */ m("div", {
|
|
254
256
|
className: "rounded-full bg-muted p-6 mb-4",
|
|
255
|
-
children: /* @__PURE__ */
|
|
257
|
+
children: /* @__PURE__ */ m(p, {
|
|
256
258
|
size: 48,
|
|
257
259
|
className: "text-muted-foreground"
|
|
258
260
|
})
|
|
259
261
|
}),
|
|
260
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ m("h3", {
|
|
261
263
|
className: "text-lg font-semibold mb-2",
|
|
262
|
-
children: r(
|
|
264
|
+
children: r(b, "membersList.noWorkspaceSelected", "No Workspace Selected")
|
|
263
265
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
266
|
+
/* @__PURE__ */ m("p", {
|
|
265
267
|
className: "text-muted-foreground max-w-sm",
|
|
266
|
-
children: r(
|
|
268
|
+
children: r(b, "membersList.selectWorkspacePrompt", "Select a workspace to view and manage its members")
|
|
267
269
|
})
|
|
268
270
|
]
|
|
269
271
|
}),
|
|
270
|
-
!B && !
|
|
272
|
+
!B && !A && E && !V && /* @__PURE__ */ h("div", {
|
|
271
273
|
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
272
274
|
children: [
|
|
273
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ m("div", {
|
|
274
276
|
className: "rounded-full bg-muted p-6 mb-4",
|
|
275
|
-
children: /* @__PURE__ */
|
|
277
|
+
children: /* @__PURE__ */ m(p, {
|
|
276
278
|
size: 48,
|
|
277
279
|
className: "text-muted-foreground"
|
|
278
280
|
})
|
|
279
281
|
}),
|
|
280
|
-
/* @__PURE__ */
|
|
282
|
+
/* @__PURE__ */ m("h3", {
|
|
281
283
|
className: "text-lg font-semibold mb-2",
|
|
282
|
-
children: r(
|
|
284
|
+
children: r(b, "membersList.noMembersYet", "No Members Yet")
|
|
283
285
|
}),
|
|
284
|
-
/* @__PURE__ */
|
|
286
|
+
/* @__PURE__ */ m("p", {
|
|
285
287
|
className: "text-muted-foreground max-w-sm mb-4",
|
|
286
|
-
children: r(
|
|
288
|
+
children: r(b, "membersList.noMembersPrompt", "Start building your team by inviting members to this workspace")
|
|
287
289
|
}),
|
|
288
|
-
/* @__PURE__ */
|
|
290
|
+
/* @__PURE__ */ m(y, {
|
|
289
291
|
permission: n.MEMBER_CREATE,
|
|
290
292
|
deniedBehavior: "disable",
|
|
291
|
-
deniedTooltip: r(
|
|
292
|
-
children: /* @__PURE__ */
|
|
293
|
+
deniedTooltip: r(b, "membersList.noPermissionToInvite", "You don't have permission to invite members"),
|
|
294
|
+
children: /* @__PURE__ */ h(_, {
|
|
293
295
|
onClick: $,
|
|
294
|
-
children: [/* @__PURE__ */
|
|
296
|
+
children: [/* @__PURE__ */ m(f, { className: "mr-2 size-4" }), r(b, "membersList.inviteFirstMember", "Invite Your First Member")]
|
|
295
297
|
})
|
|
296
298
|
})
|
|
297
299
|
]
|
|
298
300
|
}),
|
|
299
|
-
!B && !
|
|
301
|
+
!B && !A && E && V && U.length === 0 && /* @__PURE__ */ h("div", {
|
|
300
302
|
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
301
303
|
children: [
|
|
302
|
-
/* @__PURE__ */
|
|
304
|
+
/* @__PURE__ */ m("div", {
|
|
303
305
|
className: "rounded-full bg-muted p-6 mb-4",
|
|
304
|
-
children: /* @__PURE__ */ d
|
|
306
|
+
children: /* @__PURE__ */ m(d, {
|
|
305
307
|
size: 48,
|
|
306
308
|
className: "text-muted-foreground"
|
|
307
309
|
})
|
|
308
310
|
}),
|
|
309
|
-
/* @__PURE__ */
|
|
311
|
+
/* @__PURE__ */ m("h3", {
|
|
310
312
|
className: "text-lg font-semibold mb-2",
|
|
311
|
-
children: r(
|
|
313
|
+
children: r(b, "membersList.noMembersFound", "No Members Found")
|
|
312
314
|
}),
|
|
313
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ m("p", {
|
|
314
316
|
className: "text-muted-foreground max-w-sm",
|
|
315
|
-
children: r(
|
|
317
|
+
children: r(b, "membersList.noMembersFoundPrompt", "No members match your search criteria. Try adjusting your search.")
|
|
316
318
|
})
|
|
317
319
|
]
|
|
318
320
|
}),
|
|
319
|
-
!B && U.length > 0 && /* @__PURE__ */
|
|
321
|
+
!B && U.length > 0 && /* @__PURE__ */ h("div", {
|
|
320
322
|
className: "space-y-3",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
323
|
+
children: [/* @__PURE__ */ m("div", {
|
|
322
324
|
className: "flex items-center justify-between",
|
|
323
|
-
children: /* @__PURE__ */
|
|
325
|
+
children: /* @__PURE__ */ h("h2", {
|
|
324
326
|
className: "text-lg font-semibold",
|
|
325
327
|
children: [
|
|
326
|
-
r(
|
|
328
|
+
r(b, "membersList.activeMembers", "Active Members"),
|
|
327
329
|
" (",
|
|
328
330
|
U.length,
|
|
329
331
|
")"
|
|
330
332
|
]
|
|
331
333
|
})
|
|
332
|
-
}), /* @__PURE__ */
|
|
334
|
+
}), /* @__PURE__ */ m("div", {
|
|
333
335
|
className: "space-y-2",
|
|
334
|
-
children: U.map((e) => /* @__PURE__ */
|
|
336
|
+
children: U.map((e) => /* @__PURE__ */ m(re, {
|
|
335
337
|
member: e,
|
|
336
|
-
selected:
|
|
338
|
+
selected: S.has(e.id),
|
|
337
339
|
onToggleSelect: (t) => {
|
|
338
|
-
t !==
|
|
340
|
+
t !== S.has(e.id) && C(e.id);
|
|
339
341
|
},
|
|
340
342
|
canRemove: Q
|
|
341
343
|
}, e.id))
|
|
342
344
|
})]
|
|
343
345
|
}),
|
|
344
|
-
!B && H && /* @__PURE__ */
|
|
346
|
+
!B && H && /* @__PURE__ */ h("div", {
|
|
345
347
|
className: "space-y-3",
|
|
346
348
|
children: [
|
|
347
|
-
/* @__PURE__ */
|
|
349
|
+
/* @__PURE__ */ m("h2", {
|
|
348
350
|
className: "text-lg font-semibold",
|
|
349
|
-
children: r(
|
|
351
|
+
children: r(b, "membersList.pendingInvitationsHeader", "Pending Invitations")
|
|
350
352
|
}),
|
|
351
|
-
/* @__PURE__ */
|
|
353
|
+
/* @__PURE__ */ h("div", {
|
|
352
354
|
className: "flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
353
|
-
children: [/* @__PURE__ */
|
|
355
|
+
children: [/* @__PURE__ */ h("label", {
|
|
354
356
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
355
|
-
children: [/* @__PURE__ */
|
|
357
|
+
children: [/* @__PURE__ */ m("input", {
|
|
356
358
|
type: "checkbox",
|
|
357
359
|
checked: q,
|
|
358
360
|
ref: (e) => {
|
|
359
361
|
e && (e.indeterminate = J && !q);
|
|
360
362
|
},
|
|
361
363
|
onChange: (e) => Ne(e.target.checked),
|
|
362
|
-
"aria-label": r(
|
|
364
|
+
"aria-label": r(b, "membersList.selectAllInvitationsAriaLabel", "Select all pending invitations"),
|
|
363
365
|
className: "size-4 rounded border-border text-primary"
|
|
364
|
-
}), /* @__PURE__ */
|
|
365
|
-
}), /* @__PURE__ */
|
|
366
|
+
}), /* @__PURE__ */ m("span", { children: r(b, "membersList.selectAllInvitations", "Select all invitations") })]
|
|
367
|
+
}), /* @__PURE__ */ h("div", {
|
|
366
368
|
className: "flex flex-wrap items-center gap-2",
|
|
367
369
|
children: [
|
|
368
|
-
/* @__PURE__ */
|
|
370
|
+
/* @__PURE__ */ h("span", {
|
|
369
371
|
className: "text-sm text-muted-foreground",
|
|
370
372
|
children: [
|
|
371
373
|
I.size,
|
|
372
374
|
" ",
|
|
373
|
-
r(
|
|
375
|
+
r(b, "membersList.selected", "selected")
|
|
374
376
|
]
|
|
375
377
|
}),
|
|
376
|
-
I.size > 0 && /* @__PURE__ */
|
|
378
|
+
I.size > 0 && /* @__PURE__ */ m(_, {
|
|
377
379
|
variant: "ghost",
|
|
378
380
|
size: "sm",
|
|
379
381
|
onClick: () => L(/* @__PURE__ */ new Set()),
|
|
380
|
-
children: r(
|
|
382
|
+
children: r(b, "membersList.clearSelection", "Clear selection")
|
|
381
383
|
}),
|
|
382
|
-
Z && /* @__PURE__ */
|
|
384
|
+
Z && /* @__PURE__ */ h(_, {
|
|
383
385
|
variant: "outline",
|
|
384
386
|
size: "sm",
|
|
385
387
|
onClick: Fe,
|
|
386
388
|
disabled: G.length === 0 || N,
|
|
387
|
-
children: [r(
|
|
389
|
+
children: [r(b, "membersList.resendSelected", "Resend Selected"), G.length > 0 ? ` (${G.length})` : ""]
|
|
388
390
|
})
|
|
389
391
|
]
|
|
390
392
|
})]
|
|
391
393
|
}),
|
|
392
|
-
/* @__PURE__ */
|
|
394
|
+
/* @__PURE__ */ m("div", {
|
|
393
395
|
className: "space-y-2",
|
|
394
|
-
children: z.map((e) => /* @__PURE__ */
|
|
396
|
+
children: z.map((e) => /* @__PURE__ */ h("div", {
|
|
395
397
|
className: "flex items-center justify-between p-4 border rounded-lg bg-card",
|
|
396
|
-
children: [/* @__PURE__ */
|
|
398
|
+
children: [/* @__PURE__ */ h("div", {
|
|
397
399
|
className: "flex items-center gap-3",
|
|
398
400
|
children: [
|
|
399
|
-
/* @__PURE__ */
|
|
401
|
+
/* @__PURE__ */ m("input", {
|
|
400
402
|
type: "checkbox",
|
|
401
403
|
checked: I.has(e.id),
|
|
402
404
|
onChange: (t) => Me(e.id, t.target.checked),
|
|
403
|
-
"aria-label": `${r(
|
|
405
|
+
"aria-label": `${r(b, "membersList.selectInvitationAriaLabel", "Select invitation")} ${e.email}`,
|
|
404
406
|
className: "size-4 rounded border-border text-primary"
|
|
405
407
|
}),
|
|
406
|
-
/* @__PURE__ */
|
|
408
|
+
/* @__PURE__ */ m("div", {
|
|
407
409
|
className: "size-10 rounded-full bg-status-warning/10 flex items-center justify-center transition-colors",
|
|
408
|
-
children: /* @__PURE__ */
|
|
410
|
+
children: /* @__PURE__ */ m(p, { className: "size-5 text-status-warning" })
|
|
409
411
|
}),
|
|
410
|
-
/* @__PURE__ */
|
|
412
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
411
413
|
className: "font-medium text-sm",
|
|
412
414
|
children: e.email
|
|
413
|
-
}), /* @__PURE__ */
|
|
415
|
+
}), /* @__PURE__ */ h("p", {
|
|
414
416
|
className: "text-xs text-muted-foreground",
|
|
415
417
|
children: [
|
|
416
|
-
r(
|
|
418
|
+
r(b, "membersList.invited", "Invited"),
|
|
417
419
|
" ",
|
|
418
420
|
new Date(e.createdAt).toLocaleDateString()
|
|
419
421
|
]
|
|
420
422
|
})] })
|
|
421
423
|
]
|
|
422
|
-
}), /* @__PURE__ */
|
|
424
|
+
}), /* @__PURE__ */ h("div", {
|
|
423
425
|
className: "flex items-center gap-2",
|
|
424
|
-
children: [/* @__PURE__ */
|
|
426
|
+
children: [/* @__PURE__ */ m(y, {
|
|
425
427
|
permission: n.MEMBER_CREATE,
|
|
426
428
|
deniedBehavior: "disable",
|
|
427
|
-
deniedTooltip: r(
|
|
428
|
-
children: /* @__PURE__ */
|
|
429
|
+
deniedTooltip: r(b, "membersList.noPermissionToResend", "You don't have permission to resend invitations"),
|
|
430
|
+
children: /* @__PURE__ */ h(_, {
|
|
429
431
|
variant: "outline",
|
|
430
432
|
size: "sm",
|
|
431
433
|
onClick: () => Ce(e.id),
|
|
432
434
|
disabled: N && P === e.id,
|
|
433
|
-
children: [/* @__PURE__ */
|
|
435
|
+
children: [/* @__PURE__ */ m(se, { className: `size-4 mr-1 ${N && P === e.id ? "animate-spin" : ""}` }), N && P === e.id ? r(b, "membersList.sending", "Sending...") : r(b, "membersList.resend", "Resend")]
|
|
434
436
|
})
|
|
435
|
-
}), /* @__PURE__ */
|
|
437
|
+
}), /* @__PURE__ */ m(g, {
|
|
436
438
|
variant: "outline",
|
|
437
439
|
className: "bg-status-warning/10 text-status-warning border-status-warning/30 transition-colors",
|
|
438
|
-
children: r(
|
|
440
|
+
children: r(b, "membersList.pending", "Pending")
|
|
439
441
|
})]
|
|
440
442
|
})]
|
|
441
443
|
}, e.id))
|
|
@@ -443,10 +445,10 @@ function _() {
|
|
|
443
445
|
]
|
|
444
446
|
})
|
|
445
447
|
]
|
|
446
|
-
}), /* @__PURE__ */
|
|
448
|
+
}), /* @__PURE__ */ m(ie, { workspaceId: E })]
|
|
447
449
|
});
|
|
448
450
|
}
|
|
449
451
|
//#endregion
|
|
450
|
-
export {
|
|
452
|
+
export { b as MembersListPage };
|
|
451
453
|
|
|
452
454
|
//# sourceMappingURL=MembersListPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../src/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { Users, UserPlus, Search, AlertCircle, RefreshCw } from 'lucide-react';\nimport { Button, Input, Skeleton, Badge, PageContainer } from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useActorProfiles, type ActorInput, type ActorProfileMap } from '@burdenoff/fe-libs/shared';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useWorkspaceMembers, useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport { useRemoveUserFromWorkspace, useResendInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { MemberListItem } from '../components/MemberListItem';\nimport { InviteMemberDialog } from '../components/InviteMemberDialog';\nimport type { Workspace } from '../types';\n\nexport function MembersListPage() {\n const { t } = useI18n();\n\n const {\n memberFilters,\n setMemberFilters,\n selectedMemberIds,\n toggleMemberSelection,\n clearMemberSelection,\n openInviteMemberDialog,\n } = useWorkspacesStore();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { workspaceId: urlWorkspaceId } = useParams<{ workspaceId: string }>();\n\n // Prefer URL workspaceId over context (user navigated to specific workspace)\n const workspaceId = urlWorkspaceId || contextWorkspaceId;\n\n useWorkspacesPageView('workspaces.members', { workspaceId, requireParams: true });\n\n const [searchValue, setSearchValue] = useState(memberFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setMemberFilters({ search: debouncedSearch });\n }, [debouncedSearch, setMemberFilters]);\n\n // Fetch members and invitations\n const {\n data: membersData,\n isLoading: isLoadingMembers,\n error: membersError,\n } = useWorkspaceMembers(workspaceId || '');\n\n const { data: invitationsData, isLoading: isLoadingInvitations } = useWorkspaceInvitations(\n workspaceId || '',\n 'PENDING'\n );\n\n const { mutate: resendInvitation, isPending: isResending } = useResendInvitation();\n const { mutate: removeUserFromWorkspace, isPending: isRemovingMembers } =\n useRemoveUserFromWorkspace();\n const [resendingId, setResendingId] = useState<string | null>(null);\n const [selectedInvitationIds, setSelectedInvitationIds] = useState<Set<string>>(new Set());\n\n const handleResendInvitation = (invitationId: string) => {\n setResendingId(invitationId);\n resendInvitation(invitationId, {\n onSuccess: () => {\n setResendingId(null);\n },\n onError: (error) => {\n console.error('Failed to resend invitation:', error);\n setResendingId(null);\n },\n });\n };\n\n const members = useMemo(() => membersData?.items || [], [membersData?.items]);\n const pendingInvitations = useMemo(() => invitationsData?.items || [], [invitationsData?.items]);\n const isLoading = isLoadingMembers || isLoadingInvitations;\n const hasMembers = members.length > 0;\n const hasPendingInvitations = pendingInvitations.length > 0;\n\n // Resolve every member's userId to displayName + email via the shared\n // ActorProfile cache so search can match human-friendly fields instead of\n // raw UUIDs.\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap: ActorProfileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n // Filter members by resolved identity (displayName, email) — fall back to\n // raw userId only if the profile hasn't resolved yet so search still works\n // during the brief loading window.\n const filteredMembers = members.filter((member) => {\n if (!debouncedSearch) return true;\n const needle = debouncedSearch.toLowerCase();\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n return member.userId.toLowerCase().includes(needle);\n });\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const selectedInvitations = useMemo(\n () => pendingInvitations.filter((invitation) => selectedInvitationIds.has(invitation.id)),\n [pendingInvitations, selectedInvitationIds]\n );\n\n const allVisibleMembersSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelectedMembers =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) &&\n !allVisibleMembersSelected;\n\n const allVisibleInvitationsSelected =\n pendingInvitations.length > 0 &&\n pendingInvitations.every((invitation) => selectedInvitationIds.has(invitation.id));\n const partiallySelectedInvitations =\n pendingInvitations.some((invitation) => selectedInvitationIds.has(invitation.id)) &&\n !allVisibleInvitationsSelected;\n\n // Clear selection on unmount\n useEffect(() => {\n return () => clearMemberSelection();\n }, [clearMemberSelection]);\n\n // Auto-open the invite dialog when arriving via /workspaces/members?invite=true\n // (used by dashboard widgets and deep links). Strip the param after opening so\n // the back button returns to the previous page rather than re-opening.\n const [searchParams, setSearchParams] = useSearchParams();\n useEffect(() => {\n if (searchParams.get('invite') === 'true' && workspaceId) {\n openInviteMemberDialog({ id: workspaceId } as Workspace);\n const next = new URLSearchParams(searchParams);\n next.delete('invite');\n setSearchParams(next, { replace: true });\n }\n }, [searchParams, setSearchParams, workspaceId, openInviteMemberDialog]);\n\n const {\n canListMembers,\n canInviteMember,\n canRemoveMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canListMembers && !canInviteMember) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'membersList.noPermissionToViewMembers',\n \"You don't have permission to view workspace members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleInviteMember = () => {\n if (workspaceId) {\n openInviteMemberDialog({ id: workspaceId } as Workspace);\n }\n };\n\n const toggleAllMembers = (checked: boolean) => {\n filteredMembers.forEach((member) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n });\n };\n\n const toggleInvitationSelection = (invitationId: string, checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(invitationId);\n } else {\n next.delete(invitationId);\n }\n return next;\n });\n };\n\n const toggleAllInvitations = (checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n pendingInvitations.forEach((invitation) => {\n if (checked) {\n next.add(invitation.id);\n } else {\n next.delete(invitation.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkRemoveMembers = async () => {\n if (selectedMembers.length === 0) return;\n void nativeImpact('medium');\n const message = `${tWithFallback(t, 'membersList.bulkRemoveConfirmPrefix', 'Remove')} ${selectedMembers.length} ${\n selectedMembers.length === 1\n ? tWithFallback(t, 'membersList.bulkRemoveConfirmSingular', 'selected member')\n : tWithFallback(t, 'membersList.bulkRemoveConfirmPlural', 'selected members')\n } ${tWithFallback(t, 'membersList.bulkRemoveConfirmSuffix', 'from this workspace?')}`;\n const native = await nativeConfirm({\n title: 'Remove members',\n message,\n okButtonTitle: 'Remove',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n selectedMembers.forEach((member) => {\n removeUserFromWorkspace(member.userId);\n });\n clearMemberSelection();\n };\n\n const handleBulkResendInvitations = () => {\n if (selectedInvitations.length === 0) return;\n selectedInvitations.forEach((invitation) => {\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n next.delete(invitation.id);\n return next;\n });\n },\n });\n });\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'membersList.pageTitle', 'Members')}\n </h1>\n <p className=\"text-muted-foreground text-sm sm:text-base\">\n {tWithFallback(\n t,\n 'membersList.pageSubtitle',\n 'Manage workspace members and invitations'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember} disabled={!workspaceId}>\n <UserPlus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'membersList.inviteMember', 'Invite Member')}\n </span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Search & Stats */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\" />\n <Input\n placeholder={tWithFallback(t, 'membersList.searchPlaceholder', 'Search members...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n {hasPendingInvitations && (\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-sm\">\n {pendingInvitations.length}{' '}\n {pendingInvitations.length !== 1\n ? tWithFallback(t, 'membersList.pendingInvitations', 'Pending Invitations')\n : tWithFallback(t, 'membersList.pendingInvitation', 'Pending Invitation')}\n </Badge>\n </div>\n )}\n </div>\n\n {filteredMembers.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleMembersSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelectedMembers && !allVisibleMembersSelected;\n }\n }}\n onChange={(event) => toggleAllMembers(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllMembersAriaLabel',\n 'Select all workspace members'\n )}\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>\n {tWithFallback(\n t,\n 'membersList.selectAllVisibleMembers',\n 'Select all visible members'\n )}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedMemberIds.size} {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedMemberIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearMemberSelection}>\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canRemoveMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkRemoveMembers}\n disabled={selectedMembers.length === 0 || isRemovingMembers}\n className=\"text-destructive\"\n >\n {tWithFallback(t, 'membersList.removeSelected', 'Remove Selected')}\n {selectedMembers.length > 0 ? ` (${selectedMembers.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <Skeleton key={i} className=\"h-20 w-full\" />\n ))}\n </div>\n )}\n\n {/* Error State */}\n {!isLoading && membersError && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-destructive/10 p-6 mb-4\">\n <AlertCircle size={48} className=\"text-destructive\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.failedToLoad', 'Failed to Load Members')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {membersError instanceof Error\n ? membersError.message\n : tWithFallback(\n t,\n 'membersList.errorLoadingMembers',\n 'An error occurred while loading members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Workspace */}\n {!isLoading && !membersError && !workspaceId && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Users size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noWorkspaceSelected', 'No Workspace Selected')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {tWithFallback(\n t,\n 'membersList.selectWorkspacePrompt',\n 'Select a workspace to view and manage its members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Members */}\n {!isLoading && !membersError && workspaceId && !hasMembers && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Users size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noMembersYet', 'No Members Yet')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mb-4\">\n {tWithFallback(\n t,\n 'membersList.noMembersPrompt',\n 'Start building your team by inviting members to this workspace'\n )}\n </p>\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'membersList.inviteFirstMember', 'Invite Your First Member')}\n </Button>\n </PermissionButton>\n </div>\n )}\n\n {/* Empty State - No Search Results */}\n {!isLoading &&\n !membersError &&\n workspaceId &&\n hasMembers &&\n filteredMembers.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Search size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noMembersFound', 'No Members Found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {tWithFallback(\n t,\n 'membersList.noMembersFoundPrompt',\n 'No members match your search criteria. Try adjusting your search.'\n )}\n </p>\n </div>\n )}\n\n {/* Members List */}\n {!isLoading && filteredMembers.length > 0 && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.activeMembers', 'Active Members')} (\n {filteredMembers.length})\n </h2>\n </div>\n <div className=\"space-y-2\">\n {filteredMembers.map((member) => (\n <MemberListItem\n key={member.id}\n member={member}\n selected={selectedMemberIds.has(member.id)}\n onToggleSelect={(checked) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n }}\n canRemove={canRemoveMember}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Pending Invitations Section - Now at bottom */}\n {!isLoading && hasPendingInvitations && (\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.pendingInvitationsHeader', 'Pending Invitations')}\n </h2>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleInvitationsSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate =\n partiallySelectedInvitations && !allVisibleInvitationsSelected;\n }\n }}\n onChange={(event) => toggleAllInvitations(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllInvitationsAriaLabel',\n 'Select all pending invitations'\n )}\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>\n {tWithFallback(t, 'membersList.selectAllInvitations', 'Select all invitations')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedInvitationIds.size}{' '}\n {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedInvitationIds.size > 0 && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSelectedInvitationIds(new Set())}\n >\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canInviteMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkResendInvitations}\n disabled={selectedInvitations.length === 0 || isResending}\n >\n {tWithFallback(t, 'membersList.resendSelected', 'Resend Selected')}\n {selectedInvitations.length > 0 ? ` (${selectedInvitations.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n <div className=\"space-y-2\">\n {pendingInvitations.map((invitation) => (\n <div\n key={invitation.id}\n className=\"flex items-center justify-between p-4 border rounded-lg bg-card\"\n >\n <div className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={selectedInvitationIds.has(invitation.id)}\n onChange={(event) =>\n toggleInvitationSelection(invitation.id, event.target.checked)\n }\n aria-label={`${tWithFallback(t, 'membersList.selectInvitationAriaLabel', 'Select invitation')} ${invitation.email}`}\n className=\"size-4 rounded border-border text-primary\"\n />\n <div className=\"size-10 rounded-full bg-status-warning/10 flex items-center justify-center transition-colors\">\n <Users className=\"size-5 text-status-warning\" />\n </div>\n <div>\n <p className=\"font-medium text-sm\">{invitation.email}</p>\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(t, 'membersList.invited', 'Invited')}{' '}\n {new Date(invitation.createdAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToResend',\n \"You don't have permission to resend invitations\"\n )}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleResendInvitation(invitation.id)}\n disabled={isResending && resendingId === invitation.id}\n >\n <RefreshCw\n className={`size-4 mr-1 ${isResending && resendingId === invitation.id ? 'animate-spin' : ''}`}\n />\n {isResending && resendingId === invitation.id\n ? tWithFallback(t, 'membersList.sending', 'Sending...')\n : tWithFallback(t, 'membersList.resend', 'Resend')}\n </Button>\n </PermissionButton>\n <Badge\n variant=\"outline\"\n className=\"bg-status-warning/10 text-status-warning border-status-warning/30 transition-colors\"\n >\n {tWithFallback(t, 'membersList.pending', 'Pending')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Dialogs */}\n <InviteMemberDialog workspaceId={workspaceId} />\n </PageContainer>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../src/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { Users, UserPlus, Search, AlertCircle, RefreshCw } from 'lucide-react';\nimport { Button, Input, Skeleton, Badge, PageContainer } from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useActorProfiles, type ActorInput, type ActorProfileMap } from '@burdenoff/fe-libs/shared';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useWorkspaceMembers, useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport { useRemoveUserFromWorkspace, useResendInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { MemberListItem } from '../components/MemberListItem';\nimport { InviteMemberDialog } from '../components/InviteMemberDialog';\nimport type { Workspace } from '../types';\n\nexport function MembersListPage() {\n const { t } = useI18n();\n\n const {\n memberFilters,\n setMemberFilters,\n selectedMemberIds,\n toggleMemberSelection,\n clearMemberSelection,\n openInviteMemberDialog,\n } = useWorkspacesStore();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { workspaceId: urlWorkspaceId } = useParams<{ workspaceId: string }>();\n\n // Prefer URL workspaceId over context (user navigated to specific workspace)\n const workspaceId = urlWorkspaceId || contextWorkspaceId;\n\n useWorkspacesPageView('workspaces.members', { workspaceId, requireParams: true });\n\n const [searchValue, setSearchValue] = useState(memberFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setMemberFilters({ search: debouncedSearch });\n }, [debouncedSearch, setMemberFilters]);\n\n // Fetch members and invitations\n const {\n data: membersData,\n isLoading: isLoadingMembers,\n error: membersError,\n } = useWorkspaceMembers(workspaceId || '');\n\n const { data: invitationsData, isLoading: isLoadingInvitations } = useWorkspaceInvitations(\n workspaceId || '',\n 'PENDING'\n );\n\n const { mutate: resendInvitation, isPending: isResending } = useResendInvitation();\n const { mutate: removeUserFromWorkspace, isPending: isRemovingMembers } =\n useRemoveUserFromWorkspace();\n const [resendingId, setResendingId] = useState<string | null>(null);\n const [selectedInvitationIds, setSelectedInvitationIds] = useState<Set<string>>(new Set());\n\n const handleResendInvitation = (invitationId: string) => {\n setResendingId(invitationId);\n resendInvitation(invitationId, {\n onSuccess: () => {\n setResendingId(null);\n },\n onError: (error) => {\n console.error('Failed to resend invitation:', error);\n setResendingId(null);\n },\n });\n };\n\n const members = useMemo(() => membersData?.items || [], [membersData?.items]);\n const pendingInvitations = useMemo(() => invitationsData?.items || [], [invitationsData?.items]);\n const isLoading = isLoadingMembers || isLoadingInvitations;\n const hasMembers = members.length > 0;\n const hasPendingInvitations = pendingInvitations.length > 0;\n\n // Resolve every member's userId to displayName + email via the shared\n // ActorProfile cache so search can match human-friendly fields instead of\n // raw UUIDs.\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap: ActorProfileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n // Filter members by resolved identity (displayName, email) — fall back to\n // raw userId only if the profile hasn't resolved yet so search still works\n // during the brief loading window.\n const filteredMembers = members.filter((member) => {\n if (!debouncedSearch) return true;\n const needle = debouncedSearch.toLowerCase();\n\n // Primary: the shared ActorProfile cache — canonical, works for every\n // member regardless of how they joined.\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n // Fallback: the denormalized identity surfaced by the backend through\n // `WorkspaceMember.context.metadata` (populated from\n // `WorkspaceMember.email` for members who joined via invitation — PR #66).\n // Available synchronously, so search still matches during the brief window\n // before the ActorProfile lookup resolves.\n //\n // The metadata field is typed `Record<string, unknown>`, so we MUST guard\n // every read with `typeof === 'string'`. A non-string value (e.g. a number,\n // null, or a nested object accidentally surfaced by a future backend\n // mapper) would otherwise crash the page on `.toLowerCase()`. Reviewed:\n // PR #24 codex (P2).\n const meta = member.context?.metadata;\n const metaEmail = typeof meta?.email === 'string' ? meta.email : '';\n const metaDisplayRaw =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : '';\n if (metaEmail.toLowerCase().includes(needle)) return true;\n if (metaDisplayRaw.toLowerCase().includes(needle)) return true;\n\n // Last resort: raw userId — keeps the previous behaviour for members with\n // neither a resolved profile nor a denormalized identity yet.\n return member.userId.toLowerCase().includes(needle);\n });\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const selectedInvitations = useMemo(\n () => pendingInvitations.filter((invitation) => selectedInvitationIds.has(invitation.id)),\n [pendingInvitations, selectedInvitationIds]\n );\n\n const allVisibleMembersSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelectedMembers =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) &&\n !allVisibleMembersSelected;\n\n const allVisibleInvitationsSelected =\n pendingInvitations.length > 0 &&\n pendingInvitations.every((invitation) => selectedInvitationIds.has(invitation.id));\n const partiallySelectedInvitations =\n pendingInvitations.some((invitation) => selectedInvitationIds.has(invitation.id)) &&\n !allVisibleInvitationsSelected;\n\n // Clear selection on unmount\n useEffect(() => {\n return () => clearMemberSelection();\n }, [clearMemberSelection]);\n\n // Auto-open the invite dialog when arriving via /workspaces/members?invite=true\n // (used by dashboard widgets and deep links). Strip the param after opening so\n // the back button returns to the previous page rather than re-opening.\n const [searchParams, setSearchParams] = useSearchParams();\n useEffect(() => {\n if (searchParams.get('invite') === 'true' && workspaceId) {\n openInviteMemberDialog({ id: workspaceId } as Workspace);\n const next = new URLSearchParams(searchParams);\n next.delete('invite');\n setSearchParams(next, { replace: true });\n }\n }, [searchParams, setSearchParams, workspaceId, openInviteMemberDialog]);\n\n const {\n canListMembers,\n canInviteMember,\n canRemoveMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canListMembers && !canInviteMember) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'membersList.noPermissionToViewMembers',\n \"You don't have permission to view workspace members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleInviteMember = () => {\n if (workspaceId) {\n openInviteMemberDialog({ id: workspaceId } as Workspace);\n }\n };\n\n const toggleAllMembers = (checked: boolean) => {\n filteredMembers.forEach((member) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n });\n };\n\n const toggleInvitationSelection = (invitationId: string, checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(invitationId);\n } else {\n next.delete(invitationId);\n }\n return next;\n });\n };\n\n const toggleAllInvitations = (checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n pendingInvitations.forEach((invitation) => {\n if (checked) {\n next.add(invitation.id);\n } else {\n next.delete(invitation.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkRemoveMembers = async () => {\n if (selectedMembers.length === 0) return;\n void nativeImpact('medium');\n const message = `${tWithFallback(t, 'membersList.bulkRemoveConfirmPrefix', 'Remove')} ${selectedMembers.length} ${\n selectedMembers.length === 1\n ? tWithFallback(t, 'membersList.bulkRemoveConfirmSingular', 'selected member')\n : tWithFallback(t, 'membersList.bulkRemoveConfirmPlural', 'selected members')\n } ${tWithFallback(t, 'membersList.bulkRemoveConfirmSuffix', 'from this workspace?')}`;\n const native = await nativeConfirm({\n title: 'Remove members',\n message,\n okButtonTitle: 'Remove',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n selectedMembers.forEach((member) => {\n removeUserFromWorkspace(member.userId);\n });\n clearMemberSelection();\n };\n\n const handleBulkResendInvitations = () => {\n if (selectedInvitations.length === 0) return;\n selectedInvitations.forEach((invitation) => {\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n next.delete(invitation.id);\n return next;\n });\n },\n });\n });\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'membersList.pageTitle', 'Members')}\n </h1>\n <p className=\"text-muted-foreground text-sm sm:text-base\">\n {tWithFallback(\n t,\n 'membersList.pageSubtitle',\n 'Manage workspace members and invitations'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember} disabled={!workspaceId}>\n <UserPlus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'membersList.inviteMember', 'Invite Member')}\n </span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Search & Stats */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\" />\n <Input\n placeholder={tWithFallback(t, 'membersList.searchPlaceholder', 'Search members...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n {hasPendingInvitations && (\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-sm\">\n {pendingInvitations.length}{' '}\n {pendingInvitations.length !== 1\n ? tWithFallback(t, 'membersList.pendingInvitations', 'Pending Invitations')\n : tWithFallback(t, 'membersList.pendingInvitation', 'Pending Invitation')}\n </Badge>\n </div>\n )}\n </div>\n\n {filteredMembers.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleMembersSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelectedMembers && !allVisibleMembersSelected;\n }\n }}\n onChange={(event) => toggleAllMembers(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllMembersAriaLabel',\n 'Select all workspace members'\n )}\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>\n {tWithFallback(\n t,\n 'membersList.selectAllVisibleMembers',\n 'Select all visible members'\n )}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedMemberIds.size} {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedMemberIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearMemberSelection}>\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canRemoveMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkRemoveMembers}\n disabled={selectedMembers.length === 0 || isRemovingMembers}\n className=\"text-destructive\"\n >\n {tWithFallback(t, 'membersList.removeSelected', 'Remove Selected')}\n {selectedMembers.length > 0 ? ` (${selectedMembers.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <Skeleton key={i} className=\"h-20 w-full\" />\n ))}\n </div>\n )}\n\n {/* Error State */}\n {!isLoading && membersError && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-destructive/10 p-6 mb-4\">\n <AlertCircle size={48} className=\"text-destructive\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.failedToLoad', 'Failed to Load Members')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {membersError instanceof Error\n ? membersError.message\n : tWithFallback(\n t,\n 'membersList.errorLoadingMembers',\n 'An error occurred while loading members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Workspace */}\n {!isLoading && !membersError && !workspaceId && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Users size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noWorkspaceSelected', 'No Workspace Selected')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {tWithFallback(\n t,\n 'membersList.selectWorkspacePrompt',\n 'Select a workspace to view and manage its members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Members */}\n {!isLoading && !membersError && workspaceId && !hasMembers && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Users size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noMembersYet', 'No Members Yet')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mb-4\">\n {tWithFallback(\n t,\n 'membersList.noMembersPrompt',\n 'Start building your team by inviting members to this workspace'\n )}\n </p>\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'membersList.inviteFirstMember', 'Invite Your First Member')}\n </Button>\n </PermissionButton>\n </div>\n )}\n\n {/* Empty State - No Search Results */}\n {!isLoading &&\n !membersError &&\n workspaceId &&\n hasMembers &&\n filteredMembers.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <Search size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.noMembersFound', 'No Members Found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm\">\n {tWithFallback(\n t,\n 'membersList.noMembersFoundPrompt',\n 'No members match your search criteria. Try adjusting your search.'\n )}\n </p>\n </div>\n )}\n\n {/* Members List */}\n {!isLoading && filteredMembers.length > 0 && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.activeMembers', 'Active Members')} (\n {filteredMembers.length})\n </h2>\n </div>\n <div className=\"space-y-2\">\n {filteredMembers.map((member) => (\n <MemberListItem\n key={member.id}\n member={member}\n selected={selectedMemberIds.has(member.id)}\n onToggleSelect={(checked) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n }}\n canRemove={canRemoveMember}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Pending Invitations Section - Now at bottom */}\n {!isLoading && hasPendingInvitations && (\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.pendingInvitationsHeader', 'Pending Invitations')}\n </h2>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleInvitationsSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate =\n partiallySelectedInvitations && !allVisibleInvitationsSelected;\n }\n }}\n onChange={(event) => toggleAllInvitations(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllInvitationsAriaLabel',\n 'Select all pending invitations'\n )}\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>\n {tWithFallback(t, 'membersList.selectAllInvitations', 'Select all invitations')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedInvitationIds.size}{' '}\n {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedInvitationIds.size > 0 && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSelectedInvitationIds(new Set())}\n >\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canInviteMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkResendInvitations}\n disabled={selectedInvitations.length === 0 || isResending}\n >\n {tWithFallback(t, 'membersList.resendSelected', 'Resend Selected')}\n {selectedInvitations.length > 0 ? ` (${selectedInvitations.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n <div className=\"space-y-2\">\n {pendingInvitations.map((invitation) => (\n <div\n key={invitation.id}\n className=\"flex items-center justify-between p-4 border rounded-lg bg-card\"\n >\n <div className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={selectedInvitationIds.has(invitation.id)}\n onChange={(event) =>\n toggleInvitationSelection(invitation.id, event.target.checked)\n }\n aria-label={`${tWithFallback(t, 'membersList.selectInvitationAriaLabel', 'Select invitation')} ${invitation.email}`}\n className=\"size-4 rounded border-border text-primary\"\n />\n <div className=\"size-10 rounded-full bg-status-warning/10 flex items-center justify-center transition-colors\">\n <Users className=\"size-5 text-status-warning\" />\n </div>\n <div>\n <p className=\"font-medium text-sm\">{invitation.email}</p>\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(t, 'membersList.invited', 'Invited')}{' '}\n {new Date(invitation.createdAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToResend',\n \"You don't have permission to resend invitations\"\n )}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleResendInvitation(invitation.id)}\n disabled={isResending && resendingId === invitation.id}\n >\n <RefreshCw\n className={`size-4 mr-1 ${isResending && resendingId === invitation.id ? 'animate-spin' : ''}`}\n />\n {isResending && resendingId === invitation.id\n ? tWithFallback(t, 'membersList.sending', 'Sending...')\n : tWithFallback(t, 'membersList.resend', 'Resend')}\n </Button>\n </PermissionButton>\n <Badge\n variant=\"outline\"\n className=\"bg-status-warning/10 text-status-warning border-status-warning/30 transition-colors\"\n >\n {tWithFallback(t, 'membersList.pending', 'Pending')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Dialogs */}\n <InviteMemberDialog workspaceId={workspaceId} />\n </PageContainer>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,IAAS,EAEjB,EACJ,mBACA,qBACA,sBACA,0BACA,yBACA,8BACE,GAAoB,EAClB,EAAE,aAAa,OAAuB,GAAsB,EAC5D,EAAE,aAAa,OAAmB,IAAoC,EAGtE,IAAc,MAAkB;AAEtC,GAAsB,sBAAsB;EAAE;EAAa,eAAe;EAAM,CAAC;CAEjF,IAAM,CAAC,GAAa,MAAkB,EAAS,GAAc,UAAU,GAAG,EACpE,IAAkB,GAAY,GAAa,IAAI;AAGrD,SAAgB;AACd,IAAiB,EAAE,QAAQ,GAAiB,CAAC;IAC5C,CAAC,GAAiB,EAAiB,CAAC;CAGvC,IAAM,EACJ,MAAM,GACN,WAAW,IACX,OAAO,MACL,GAAoB,KAAe,GAAG,EAEpC,EAAE,MAAM,GAAiB,WAAW,OAAyB,EACjE,KAAe,IACf,UACD,EAEK,EAAE,QAAQ,GAAkB,WAAW,MAAgB,IAAqB,EAC5E,EAAE,QAAQ,IAAyB,WAAW,OAClD,IAA4B,EACxB,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAuB,KAA4B,kBAAsB,IAAI,KAAK,CAAC,EAEpF,MAA0B,MAAyB;AAEvD,EADA,EAAe,EAAa,EAC5B,EAAiB,GAAc;GAC7B,iBAAiB;AACf,MAAe,KAAK;;GAEtB,UAAU,MAAU;AAElB,IADA,QAAQ,MAAM,gCAAgC,EAAM,EACpD,EAAe,KAAK;;GAEvB,CAAC;IAGE,IAAU,QAAc,GAAa,SAAS,EAAE,EAAE,CAAC,GAAa,MAAM,CAAC,EACvE,IAAqB,QAAc,GAAiB,SAAS,EAAE,EAAE,CAAC,GAAiB,MAAM,CAAC,EAC1F,IAAY,MAAoB,IAChC,IAAa,EAAQ,SAAS,GAC9B,IAAwB,EAAmB,SAAS,GAKpD,EAAE,mBAAe,eAAW,uBAAmB,GAAsB,EAKrE,KAA8B,GAJf,QACb,EAAQ,KAAK,OAAO;EAAE,SAAS,EAAE;EAAQ,WAAW;EAAQ,EAAE,EACpE,CAAC,EAAQ,CACV,EACkE;EACjE;EACA;EACA,OAAO;EACR,CAAC,EAKI,IAAkB,EAAQ,QAAQ,MAAW;AACjD,MAAI,CAAC,EAAiB,QAAO;EAC7B,IAAM,IAAS,EAAgB,aAAa,EAItC,IAAU,GAAW,EAAO;AAClC,MAAI,MACE,EAAQ,YAAY,aAAa,CAAC,SAAS,EAAO,IAClD,EAAQ,OAAO,aAAa,CAAC,SAAS,EAAO,EAAE,QAAO;EAc5D,IAAM,IAAO,EAAO,SAAS,UACvB,IAAY,OAAO,GAAM,SAAU,WAAW,EAAK,QAAQ,IAC3D,IACJ,OAAO,GAAM,eAAgB,WACzB,EAAK,cACL,OAAO,GAAM,QAAS,WACpB,EAAK,OACL;AAMR,SALI,EAAU,aAAa,CAAC,SAAS,EAAO,IACxC,EAAe,aAAa,CAAC,SAAS,EAAO,GAAS,KAInD,EAAO,OAAO,aAAa,CAAC,SAAS,EAAO;GACnD,EAEI,IAAkB,QAChB,EAAgB,QAAQ,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC1E,CAAC,GAAiB,EAAkB,CACrC,EAEK,IAAsB,QACpB,EAAmB,QAAQ,MAAe,EAAsB,IAAI,EAAW,GAAG,CAAC,EACzF,CAAC,GAAoB,EAAsB,CAC5C,EAEK,IACJ,EAAgB,SAAS,KACzB,EAAgB,OAAO,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC/D,KACJ,EAAgB,MAAM,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,IAClE,CAAC,GAEG,IACJ,EAAmB,SAAS,KAC5B,EAAmB,OAAO,MAAe,EAAsB,IAAI,EAAW,GAAG,CAAC,EAC9E,IACJ,EAAmB,MAAM,MAAe,EAAsB,IAAI,EAAW,GAAG,CAAC,IACjF,CAAC;AAGH,eACe,GAAsB,EAClC,CAAC,EAAqB,CAAC;CAK1B,IAAM,CAAC,GAAc,KAAmB,IAAiB;AACzD,SAAgB;AACd,MAAI,EAAa,IAAI,SAAS,KAAK,UAAU,GAAa;AACxD,KAAuB,EAAE,IAAI,GAAa,CAAc;GACxD,IAAM,IAAO,IAAI,gBAAgB,EAAa;AAE9C,GADA,EAAK,OAAO,SAAS,EACrB,EAAgB,GAAM,EAAE,SAAS,IAAM,CAAC;;IAEzC;EAAC;EAAc;EAAiB;EAAa;EAAuB,CAAC;CAExE,IAAM,EACJ,oBACA,oBACA,oBACA,WAAW,OACT,IAAyB;AAE7B,KAAI,GACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,EACnC,kBAAC,GAAD,EAAU,WAAU,QAAS,CAAA,CACzB;;AAIV,KAAI,CAAC,MAAkB,CAAC,EACtB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAc,GAAG,4BAA4B,gBAAgB;IAC3D,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,GACA,yCACA,uDACD;IACC,CAAA,CACA;;EACF,CAAA;CAIV,IAAM,UAA2B;AAC/B,EAAI,KACF,EAAuB,EAAE,IAAI,GAAa,CAAc;IAItD,MAAoB,MAAqB;AAC7C,IAAgB,SAAS,MAAW;AAElC,GAAI,MADe,EAAkB,IAAI,EAAO,GAAG,IAEjD,EAAsB,EAAO,GAAG;IAElC;IAGE,MAA6B,GAAsB,MAAqB;AAC5E,KAA0B,MAAS;GACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,IACF,EAAK,IAAI,EAAa,GAEtB,EAAK,OAAO,EAAa,EAEpB;IACP;IAGE,MAAwB,MAAqB;AACjD,KAA0B,MAAS;GACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAQ1B,UAPA,EAAmB,SAAS,MAAe;AACzC,IAAI,IACF,EAAK,IAAI,EAAW,GAAG,GAEvB,EAAK,OAAO,EAAW,GAAG;KAE5B,EACK;IACP;IAGE,KAA0B,YAAY;AAC1C,MAAI,EAAgB,WAAW,EAAG;AAC7B,IAAa,SAAS;EAC3B,IAAM,IAAU,GAAG,EAAc,GAAG,uCAAuC,SAAS,CAAC,GAAG,EAAgB,OAAO,GAC7G,EAAgB,WAAW,IACvB,EAAc,GAAG,yCAAyC,kBAAkB,GAC5E,EAAc,GAAG,uCAAuC,mBAAmB,CAChF,GAAG,EAAc,GAAG,uCAAuC,uBAAuB,IAC7E,IAAS,MAAM,EAAc;GACjC,OAAO;GACP;GACA,eAAe;GACf,mBAAmB;GACpB,CAAC;AACgB,SAAW,OAAO,OAAO,QAAQ,EAAQ,GAAG,OAG9D,EAAgB,SAAS,MAAW;AAClC,MAAwB,EAAO,OAAO;IACtC,EACF,GAAsB;IAGlB,WAAoC;AACpC,IAAoB,WAAW,KACnC,EAAoB,SAAS,MAAe;AAC1C,KAAiB,EAAW,IAAI,EAC9B,iBAAiB;AACf,OAA0B,MAAS;KACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAE1B,YADA,EAAK,OAAO,EAAW,GAAG,EACnB;MACP;MAEL,CAAC;IACF;;AAGJ,QACE,kBAAC,IAAD;EAAe,QAAQ;YAAvB,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAc,GAAG,yBAAyB,UAAU;MAClD,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,GACA,4BACA,2CACD;MACC,CAAA,CACA,EAAA,CAAA,EAEN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,YAAY,EAAsB;OAClC,gBAAe;OACf,eAAe,EACb,GACA,oCACA,8CACD;iBAED,kBAAC,GAAD;QAAQ,SAAS;QAAoB,UAAU,CAAC;kBAAhD,CACE,kBAAC,GAAD,EAAU,WAAU,kBAAmB,CAAA,EACvC,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAc,GAAG,4BAA4B,gBAAgB;SACzD,CAAA,CACA;;OACQ,CAAA;MACf,CAAA,CACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,yEAA0E,CAAA,EAC5F,kBAAC,IAAD;OACE,aAAa,EAAc,GAAG,iCAAiC,oBAAoB;OACnF,OAAO;OACP,WAAW,MAAM,GAAe,EAAE,OAAO,MAAM;OAC/C,WAAU;OACV,CAAA,CACE;SAEL,KACC,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAO,SAAQ;OAAY,WAAU;iBAArC;QACG,EAAmB;QAAQ;QAC3B,EAAmB,WAAW,IAE3B,EAAc,GAAG,iCAAiC,qBAAqB,GADvE,EAAc,GAAG,kCAAkC,sBAAsB;QAEvE;;MACJ,CAAA,CAEJ;;IAEL,EAAgB,SAAS,KACxB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD;OACE,MAAK;OACL,SAAS;OACT,MAAM,MAAU;AACd,QAAI,MACF,EAAM,gBAAgB,MAA4B,CAAC;;OAGvD,WAAW,MAAU,GAAiB,EAAM,OAAO,QAAQ;OAC3D,cAAY,EACV,GACA,yCACA,+BACD;OACD,WAAU;OACV,CAAA,EACF,kBAAC,QAAD,EAAA,UACG,EACC,GACA,uCACA,6BACD,EACI,CAAA,CACD;SAER,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACG,EAAkB;SAAK;SAAE,EAAc,GAAG,wBAAwB,WAAW;SACzE;;OACN,EAAkB,OAAO,KACxB,kBAAC,GAAD;QAAQ,SAAQ;QAAQ,MAAK;QAAK,SAAS;kBACxC,EAAc,GAAG,8BAA8B,kBAAkB;QAC3D,CAAA;OAEV,KACC,kBAAC,GAAD;QACE,SAAQ;QACR,MAAK;QACL,SAAS;QACT,UAAU,EAAgB,WAAW,KAAK;QAC1C,WAAU;kBALZ,CAOG,EAAc,GAAG,8BAA8B,kBAAkB,EACjE,EAAgB,SAAS,IAAI,KAAK,EAAgB,OAAO,KAAK,GACxD;;OAEP;QACF;;IAIP,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ,CAAC,GAAG;;;;;;MAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,GAAD,EAAkB,WAAU,eAAgB,EAA7B,EAA6B,CAC5C;KACE,CAAA;IAIP,CAAC,KAAa,KACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,IAAD;QAAa,MAAM;QAAI,WAAU;QAAqB,CAAA;OAClD,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,4BAA4B,yBAAyB;OACpE,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,aAAwB,QACrB,EAAa,UACb,EACE,GACA,mCACA,0CACD;OACH,CAAA;MACA;;IAIP,CAAC,KAAa,CAAC,KAAgB,CAAC,KAC/B,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAO,MAAM;QAAI,WAAU;QAA0B,CAAA;OACjD,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,mCAAmC,wBAAwB;OAC1E,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,GACA,qCACA,oDACD;OACC,CAAA;MACA;;IAIP,CAAC,KAAa,CAAC,KAAgB,KAAe,CAAC,KAC9C,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAO,MAAM;QAAI,WAAU;QAA0B,CAAA;OACjD,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,4BAA4B,iBAAiB;OAC5D,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,GACA,+BACA,iEACD;OACC,CAAA;MACJ,kBAAC,GAAD;OACE,YAAY,EAAsB;OAClC,gBAAe;OACf,eAAe,EACb,GACA,oCACA,8CACD;iBAED,kBAAC,GAAD;QAAQ,SAAS;kBAAjB,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACnC,EAAc,GAAG,iCAAiC,2BAA2B,CACvE;;OACQ,CAAA;MACf;;IAIP,CAAC,KACA,CAAC,KACD,KACA,KACA,EAAgB,WAAW,KACzB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAQ,MAAM;QAAI,WAAU;QAA0B,CAAA;OAClD,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,8BAA8B,mBAAmB;OAChE,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,GACA,oCACA,oEACD;OACC,CAAA;MACA;;IAIT,CAAC,KAAa,EAAgB,SAAS,KACtC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACG,EAAc,GAAG,6BAA6B,iBAAiB;QAAC;QAChE,EAAgB;QAAO;QACrB;;MACD,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAgB,KAAK,MACpB,kBAAC,IAAD;OAEU;OACR,UAAU,EAAkB,IAAI,EAAO,GAAG;OAC1C,iBAAiB,MAAY;AAE3B,QAAI,MADe,EAAkB,IAAI,EAAO,GAAG,IAEjD,EAAsB,EAAO,GAAG;;OAGpC,WAAW;OACX,EAVK,EAAO,GAUZ,CACF;MACE,CAAA,CACF;;IAIP,CAAC,KAAa,KACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,wCAAwC,sBAAsB;OAC7E,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,SAAD;SACE,MAAK;SACL,SAAS;SACT,MAAM,MAAU;AACd,UAAI,MACF,EAAM,gBACJ,KAAgC,CAAC;;SAGvC,WAAW,MAAU,GAAqB,EAAM,OAAO,QAAQ;SAC/D,cAAY,EACV,GACA,6CACA,iCACD;SACD,WAAU;SACV,CAAA,EACF,kBAAC,QAAD,EAAA,UACG,EAAc,GAAG,oCAAoC,yBAAyB,EAC1E,CAAA,CACD;WAER,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAsB;WAAM;WAC5B,EAAc,GAAG,wBAAwB,WAAW;WAChD;;SACN,EAAsB,OAAO,KAC5B,kBAAC,GAAD;UACE,SAAQ;UACR,MAAK;UACL,eAAe,kBAAyB,IAAI,KAAK,CAAC;oBAEjD,EAAc,GAAG,8BAA8B,kBAAkB;UAC3D,CAAA;SAEV,KACC,kBAAC,GAAD;UACE,SAAQ;UACR,MAAK;UACL,SAAS;UACT,UAAU,EAAoB,WAAW,KAAK;oBAJhD,CAMG,EAAc,GAAG,8BAA8B,kBAAkB,EACjE,EAAoB,SAAS,IAAI,KAAK,EAAoB,OAAO,KAAK,GAChE;;SAEP;UACF;;MACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAmB,KAAK,MACvB,kBAAC,OAAD;QAEE,WAAU;kBAFZ,CAIE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,SAAD;WACE,MAAK;WACL,SAAS,EAAsB,IAAI,EAAW,GAAG;WACjD,WAAW,MACT,GAA0B,EAAW,IAAI,EAAM,OAAO,QAAQ;WAEhE,cAAY,GAAG,EAAc,GAAG,yCAAyC,oBAAoB,CAAC,GAAG,EAAW;WAC5G,WAAU;WACV,CAAA;UACF,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAO,WAAU,8BAA+B,CAAA;WAC5C,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAuB,EAAW;WAAU,CAAA,EACzD,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAc,GAAG,uBAAuB,UAAU;YAAE;YACpD,IAAI,KAAK,EAAW,UAAU,CAAC,oBAAoB;YAClD;aACA,EAAA,CAAA;UACF;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UACE,YAAY,EAAsB;UAClC,gBAAe;UACf,eAAe,EACb,GACA,oCACA,kDACD;oBAED,kBAAC,GAAD;WACE,SAAQ;WACR,MAAK;WACL,eAAe,GAAuB,EAAW,GAAG;WACpD,UAAU,KAAe,MAAgB,EAAW;qBAJtD,CAME,kBAAC,IAAD,EACE,WAAW,eAAe,KAAe,MAAgB,EAAW,KAAK,iBAAiB,MAC1F,CAAA,EACD,KAAe,MAAgB,EAAW,KACvC,EAAc,GAAG,uBAAuB,aAAa,GACrD,EAAc,GAAG,sBAAsB,SAAS,CAC7C;;UACQ,CAAA,EACnB,kBAAC,GAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAc,GAAG,uBAAuB,UAAU;UAC7C,CAAA,CACJ;WACF;UAvDC,EAAW,GAuDZ,CACN;OACE,CAAA;MACF;;IAEJ;MAGN,kBAAC,IAAD,EAAiC,gBAAe,CAAA,CAClC"}
|