@burdenoff/microfe-organization 2026.601.7 → 2026.601.8
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/organization-management/modules/members/pages/MemberDetailPage.js +74 -58
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js +202 -199
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js +92 -82
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +180 -162
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Eye, Mail, RefreshCw, Shield, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter((member) => {\n const profile = profileMap[member.userId];\n if (profile?.displayName.toLowerCase().includes(normalizedQuery)) return true;\n if (profile?.email?.toLowerCase().includes(normalizedQuery)) return true;\n return (\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n });\n }, [debouncedSearchQuery, members, profileMap]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.list.title', 'Members')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'members.list.subtitle',\n 'Manage organization members, inspect their roles, and invite more people into this organization.'\n )}\n </p>\n </div>\n\n {inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members/invite`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">{tr('members.invite.button', 'Invite Member')}</span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('members.list.searchPlaceholder', 'Search by member ID or role...')}\n label={tr('members.list.searchLabel', 'Search organization members')}\n inputId=\"organization-members-search\"\n className=\"flex-1\"\n />\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('members.list.refresh', 'Refresh members')}\n title={tr('members.list.refresh', 'Refresh members')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {filteredMembers.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedMemberIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedMemberIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedMemberIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={handleBulkRemove}\n disabled={selectedMembers.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.removeSelected', 'Remove Selected')}</span>\n {selectedMembers.length > 0 ? <span>({selectedMembers.length})</span> : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {filteredMembers.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Shield size={40} aria-hidden=\"true\" />}\n title={tr('members.list.emptyTitle', 'No members found')}\n description={tr(\n 'members.list.emptyDescription',\n 'Invite someone to this organization to grant them access and assign their role.'\n )}\n action={\n inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? {\n label: tr('members.invite.button', 'Invite Member'),\n onClick: () => navigate(`${basePath}/${organizationId}/members/invite`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"break-all text-base font-semibold text-[var(--color-text-primary)]\">\n {profileMap[member.userId]?.displayName ??\n `User ${member.userId.slice(0, 8)}…`}\n </h2>\n <MemberRoleBadge role={member.role} />\n </div>\n {profileMap[member.userId]?.email ? (\n <p className=\"mt-0.5 break-all text-xs text-[var(--color-text-secondary)]\">\n {profileMap[member.userId]?.email}\n </p>\n ) : null}\n <details className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.list.showIds', 'Show IDs')}\n </summary>\n <p className=\"mt-1 break-all font-mono\">\n {tr('members.detail.userId', 'User ID')}: {member.userId}\n </p>\n <p className=\"mt-0.5 break-all font-mono\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </details>\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-primary)]\">\n {formatDate(member.joinedAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('members.remove.confirm', 'Remove')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.user', 'User')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"break-all font-medium text-[var(--color-text-primary)]\">\n {member.userId}\n </p>\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,KAAgB;CACvB,IAAM,IAAK,GAAO;AAClB,SAAQ,MACD,IACE,IAAI,KAAK,EAAK,CAAC,oBAAoB,GADxB,EAAG,yBAAyB,UAAU;;AAK5D,SAAgB,IAAkB;CAChC,IAAM,IAAK,GAAO,EACZ,IAAa,IAAe,EAC5B,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,WAAW,OAAuB,IAAgB,EAGnE,IAAgB,GAAsB,sBAAsB,EAC5D,IAAgB,IAAgB,EAAQ,EAAc,QAAS,IAC/D,EAAE,sBAAmB,IAAuC,EAC5D,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,IAAuB,EAAkB,GAAa,IAAI,EAC1D,CAAC,GAAmB,KAAwB,kBAAsB,IAAI,KAAK,CAAC,EAC5E,EAAE,sBAAmB,qBAAkB,IAAkB,EAEzD,EAAE,YAAS,YAAS,UAAO,eAAY,EAAW,KAAkB,IAAI,IAAI,EAAE,EAC9E,EAAE,iBAAc,SAAS,MAAkB,GAAiB,EAE5D,EAAE,kBAAe,iBAAc,GAAe,EAK9C,IAAa,EAJE,QACb,EAAQ,KAAK,OAAO;EAAE,SAAS,EAAE;EAAQ,WAAW;EAAQ,EAAE,EACpE,CAAC,EAAQ,CACV,EACiD;EAChD;EACA;EACA,OAAO;EACR,CAAC,EAEI,IAAkB,QAAc;AACpC,MAAI,CAAC,EACH,QAAO;EAGT,IAAM,IAAkB,EAAqB,aAAa;AAC1D,SAAO,EAAQ,QAAQ,MAAW;GAChC,IAAM,IAAU,EAAW,EAAO;AAGlC,UAFI,GAAS,YAAY,aAAa,CAAC,SAAS,EAAgB,IAC5D,GAAS,OAAO,aAAa,CAAC,SAAS,EAAgB,GAAS,KAElE,EAAO,OAAO,aAAa,CAAC,SAAS,EAAgB,IACrD,EAAO,KAAK,aAAa,CAAC,SAAS,EAAgB;IAErD;IACD;EAAC;EAAsB;EAAS;EAAW,CAAC;AAE/C,UAAgB;AACd,KAAsB,MAAsB;GAC1C,IAAM,IAAa,IAAI,IAAI,EAAgB,KAAK,MAAW,EAAO,GAAG,CAAC,EAChE,IAAgB,IAAI,IACxB,CAAC,GAAG,EAAkB,CAAC,QAAQ,MAAa,EAAW,IAAI,EAAS,CAAC,CACtE;AAMD,UAJI,EAAc,SAAS,EAAkB,OACpC,IAGF;IACP;IACD,CAAC,EAAgB,CAAC;CAErB,IAAM,IAAkB,QAChB,EAAgB,QAAQ,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC1E,CAAC,GAAiB,EAAkB,CACrC,EAEK,IACJ,EAAgB,SAAS,KACzB,EAAgB,OAAO,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC/D,IACJ,EAAgB,MAAM,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,IAAI,CAAC,GAEnE,IAAuB,OAAO,MAAsB;AACnD,QAEL,KAAI;GAUF,IAAM,KATU,MAAM,QAAQ,WAC5B,EAAQ,IAAI,OAAO,MAAW;IAC5B,IAAM,IAAS,MAAM,EAAa,GAAgB,EAAO;AACzD,QAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,0BAA0B;KAEzE,CACH,EAE8B,MAAM,MAAW,EAAO,WAAW,WAAW;AAC7E,OAAI,GAAgB,WAAW,WAC7B,OAAM,EAAe;AAKvB,GAFA,kBAAqB,IAAI,KAAK,CAAC,EAC/B,MAAM,GAAS,EACf,EAAM,QACJ,EAAQ,WAAW,IACf,EAAG,gCAAgC,8BAA8B,GACjE,GAAG,EAAQ,OAAO,GAAG,EAAG,8BAA8B,+BAA+B,GAC1F;WACM,GAAa;AACpB,KAAM,MACJ,aAAuB,QACnB,EAAY,UACZ,EAAG,yBAAyB,0BAA0B,CAC3D;;IAIC,KAAgB,MAAmB;AACvC,IAAkB;GAChB,OAAO,EAAG,wBAAwB,gBAAgB;GAClD,aAAa,GAAG,EAAG,oCAAoC,SAAS,CAAC,GAAG,EAAO,GAAG,EAAG,oCAAoC,4FAA4F;GACjN,aAAa,EAAG,0BAA0B,SAAS;GACnD,SAAS;GACT,iBAAiB;AACV,MAAqB,CAAC,EAAO,CAAC;;GAEtC,CAAC;IAGE,WAAyB;AACzB,IAAgB,WAAW,KAE/B,EAAkB;GAChB,OAAO,EAAG,4BAA4B,0BAA0B;GAChE,aAAa,GAAG,EAAG,6BAA6B,SAAS,CAAC,GAAG,EAAgB,OAAO,GAAG,EACrF,EAAgB,WAAW,IACvB,oCACA,iCACJ,EAAgB,WAAW,IACvB,4CACA,2CACL;GACD,aAAa,GAAG,EAAG,0BAA0B,SAAS,CAAC,GAAG,EAAgB;GAC1E,SAAS;GACT,iBAAiB;AACV,MAAqB,EAAgB,KAAK,MAAW,EAAO,OAAO,CAAC;;GAE5E,CAAC;IAGE,KAAyB,GAAkB,MAAqB;AACpE,KAAsB,MAAsB;GAC1C,IAAM,IAAgB,IAAI,IAAI,EAAkB;AAMhD,UALI,IACF,EAAc,IAAI,EAAS,GAE3B,EAAc,OAAO,EAAS,EAEzB;IACP;IAGE,KAAoB,MAAqB;AAC7C,KAAsB,MAAsB;GAC1C,IAAM,IAAgB,IAAI,IAAI,EAAkB;AAQhD,UAPA,EAAgB,SAAS,MAAW;AAClC,IAAI,IACF,EAAc,IAAI,EAAO,GAAG,GAE5B,EAAc,OAAO,EAAO,GAAG;KAEjC,EACK;IACP;;AA2DJ,QAxDK,IAcD,KAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIL,EAAc,EAAY,YAAY,GAUvC,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,wBAAwB,qBAAqB,EAAI,CAAA;EACvE,CAAA,GAIN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,SAAS,EAAM;GACf,QAAQ;IACN,OAAO,EAAG,gBAAgB,QAAQ;IAClC,eAAe;AACR,QAAS;;IAEjB;GACD,CAAA;EACE,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,sBAAsB,UAAU;OACjC,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,yBACA,mGACD;OACC,CAAA,CACA;SAEL,KAAiB,EAAc,EAAY,cAAc,IACxD,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,iBAAiB;MACvE,WAAU;gBAHZ,CAKE,kBAAC,GAAD;OAAM,MAAM;OAAI,eAAY;OAAS,CAAA,EACrC,kBAAC,QAAD;OAAM,WAAU;iBAAe,EAAG,yBAAyB,gBAAgB;OAAQ,CAAA,CAC5E;QAEP;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,aAAa,EAAG,kCAAkC,iCAAiC;OACnF,OAAO,EAAG,4BAA4B,8BAA8B;OACpE,SAAQ;OACR,WAAU;OACV,CAAA,EAEF,kBAAC,UAAD;OACE,MAAK;OACL,eAAe;AACR,WAAS;;OAEhB,WAAU;OACV,cAAY,EAAG,wBAAwB,kBAAkB;OACzD,OAAO,EAAG,wBAAwB,kBAAkB;iBAEpD,kBAAC,GAAD;QAAW,MAAM;QAAI,eAAY;QAAS,CAAA;OACnC,CAAA,CACL;SAEL,EAAgB,SAAS,IACxB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,GAAD;SACE,SAAS;SACT,eAAe;SACf,UAAU;SACV,WAAW,EAAG,0BAA0B,qBAAqB;SAC7D,CAAA,EACF,kBAAC,QAAD,EAAA,UAAO,EAAG,2BAA2B,qBAAqB,EAAQ,CAAA,CAC5D;WAEP,EAAkB,OAAO,IACxB,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,kBAAqB,IAAI,KAAK,CAAC;QAC9C,WAAU;kBAET,EAAG,yBAAyB,kBAAkB;QACxC,CAAA,GACP,KACA;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACG,EAAkB;SAAK;SAAE,EAAG,mBAAmB,WAAW;SACtD;WACN,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU,EAAgB,WAAW,KAAK;QAC1C,WAAU;kBAJZ;SAME,kBAAC,GAAD;UAAQ,MAAM;UAAI,eAAY;UAAS,CAAA;SACvC,kBAAC,QAAD,EAAA,UAAO,EAAG,yBAAyB,kBAAkB,EAAQ,CAAA;SAC5D,EAAgB,SAAS,IAAI,kBAAC,QAAD,EAAA,UAAA;UAAM;UAAE,EAAgB;UAAO;UAAQ,EAAA,CAAA,GAAG;SACjE;UAEP;SACF;UACJ,KACA;;IAEL,EAAgB,WAAW,IAC1B,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD;OAAQ,MAAM;OAAI,eAAY;OAAS,CAAA;MAC7C,OAAO,EAAG,2BAA2B,mBAAmB;MACxD,aAAa,EACX,iCACA,kFACD;MACD,QACE,KAAiB,EAAc,EAAY,cAAc,GACrD;OACE,OAAO,EAAG,yBAAyB,gBAAgB;OACnD,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,iBAAiB;OACxE,GACD,KAAA;MAEN,CAAA;KACE,CAAA,GAEN,kBAAA,IAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAgB,KAAK,MACpB,kBAAC,WAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,SAAS,EAAkB,IAAI,EAAO,GAAG;QACzC,WAAW,MAAY,EAAsB,EAAO,IAAI,EAAQ;QAChE,WAAW,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;QACzE,CAAA,EAEF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD;UAAQ,MAAM;UAAI,eAAY;UAAS,CAAA;SACnC,CAAA,EAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,MAAD;YAAI,WAAU;sBACX,EAAW,EAAO,SAAS,eAC1B,QAAQ,EAAO,OAAO,MAAM,GAAG,EAAE,CAAC;YACjC,CAAA,EACL,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA,CAClC;;UACL,EAAW,EAAO,SAAS,QAC1B,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAW,EAAO,SAAS;WAC1B,CAAA,GACF;UACJ,kBAAC,WAAD;WAAS,WAAU;qBAAnB;YACE,kBAAC,WAAD;aAAS,WAAU;uBAChB,EAAG,wBAAwB,WAAW;aAC/B,CAAA;YACV,kBAAC,KAAD;aAAG,WAAU;uBAAb;cACG,EAAG,yBAAyB,UAAU;cAAC;cAAG,EAAO;cAChD;;YACJ,kBAAC,KAAD;aAAG,WAAU;uBAAb;cACG,EAAG,yBAAyB,YAAY;cAAC;cAAG,EAAO;cAClD;;YACI;;UACN,EAAA,CAAA,EAEN,kBAAC,MAAD;UAAI,WAAU;oBAAd,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAG,0BAA0B,aAAa;WACxC,CAAA,EACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAO,aAAa,EAAG,uBAAuB,MAAM;WAClD,CAAA,CACD,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAG,uBAAuB,SAAS;WACjC,CAAA,EACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAW,EAAO,SAAS;WACzB,CAAA,CACD,EAAA,CAAA,CACH;YACD;WACF;UACF;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eACE,EAAS,GAAG,EAAS,GAAG,EAAe,WAAW,EAAO,SAAS;QAEpE,WAAU;kBALZ,CAOE,kBAAC,GAAD;SAAK,MAAM;SAAI,eAAY;SAAS,CAAA,EACnC,EAAG,eAAe,OAAO,CACnB;WACR,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAa,EAAO,OAAO;QAC1C,UAAU;QACV,WAAU;kBAJZ,CAME,kBAAC,GAAD;SAAQ,MAAM;SAAI,eAAY;SAAS,CAAA,EACtC,EAAG,0BAA0B,SAAS,CAChC;UAEP;SACE;QAvFH,EAAO,GAuFJ,CACV;KACE,CAAA,EAEN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,SAAD;QAAO,WAAU;kBACf,kBAAC,MAAD,EAAA,UAAA;SACE,kBAAC,MAAD;UAAI,WAAU;oBACZ,kBAAC,GAAD;WACE,SAAS;WACT,eAAe;WACf,UAAU;WACV,WAAW,EAAG,0BAA0B,qBAAqB;WAC7D,CAAA;UACC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,4BAA4B,OAAO;UACpC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,4BAA4B,OAAO;UACpC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,0BAA0B,aAAa;UACxC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,uBAAuB,SAAS;UACjC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,kBAAkB,UAAU;UAC7B,CAAA;SACF,EAAA,CAAA;QACC,CAAA,EACR,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAgB,KAAK,MACpB,kBAAC,MAAD;SAEE,WAAU;mBAFZ;UAIE,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,GAAD;YACE,SAAS,EAAkB,IAAI,EAAO,GAAG;YACzC,WAAW,MAAY,EAAsB,EAAO,IAAI,EAAQ;YAChE,WAAW,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;YACzE,CAAA;WACC,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,OAAD;aAAK,WAAU;uBACb,kBAAC,GAAD;cAAQ,MAAM;cAAI,eAAY;cAAS,CAAA;aACnC,CAAA,EACN,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAO;cACN,CAAA,EACJ,kBAAC,KAAD;cAAG,WAAU;wBAAb;eACG,EAAG,kBAAkB,KAAK;eAAC;eAAG,EAAO;eACpC;gBACA;eACF;;WACH,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA;WACnC,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAO,aAAa,EAAG,uBAAuB,MAAM;WAClD,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAW,EAAO,SAAS;WACzB,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,UAAD;aACE,MAAK;aACL,eACE,EAAS,GAAG,EAAS,GAAG,EAAe,WAAW,EAAO,SAAS;aAEpE,WAAU;aACV,cAAY,GAAG,EAAG,2BAA2B,cAAc,CAAC,GAAG,EAAO;aACtE,OAAO,GAAG,EAAG,2BAA2B,cAAc,CAAC,GAAG,EAAO;uBAEjE,kBAAC,GAAD;cAAK,MAAM;cAAI,eAAY;cAAS,CAAA;aAC7B,CAAA,EACR,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;aACE,MAAK;aACL,eAAe,EAAa,EAAO,OAAO;aAC1C,UAAU;aACV,WAAU;aACV,cAAY,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;aAC1E,OAAO,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;uBAErE,kBAAC,GAAD;cAAQ,MAAM;cAAI,eAAY;cAAS,CAAA;aAChC,CAAA,CAEP;;WACH,CAAA;UACF;WA7DE,EAAO,GA6DT,CACL;QACI,CAAA,CACF;;MACJ,CAAA;KACF,CAAA,CACL,EAAA,CAAA;IAED;MAEL,EACG;MA7VJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,6BAA6B,6CAA6C;GAC1E,CAAA;EACF,CAAA,GA1BN,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,SAAS,EAAG,2BAA2B,8CAA8C;GACrF,QAAQ;IACN,OAAO,EAAG,kCAAkC,sBAAsB;IAClE,eAAe,EAAS,EAAS;IAClC;GACD,CAAA;EACE,CAAA"}
|
|
1
|
+
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Eye, Mail, RefreshCw, Shield, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n ActorIdentity,\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n} from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter((member) => {\n const profile = profileMap[member.userId];\n if (profile?.displayName.toLowerCase().includes(normalizedQuery)) return true;\n if (profile?.email?.toLowerCase().includes(normalizedQuery)) return true;\n return (\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n });\n }, [debouncedSearchQuery, members, profileMap]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.list.title', 'Members')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'members.list.subtitle',\n 'Manage organization members, inspect their roles, and invite more people into this organization.'\n )}\n </p>\n </div>\n\n {inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members/invite`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">{tr('members.invite.button', 'Invite Member')}</span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('members.list.searchPlaceholder', 'Search by member ID or role...')}\n label={tr('members.list.searchLabel', 'Search organization members')}\n inputId=\"organization-members-search\"\n className=\"flex-1\"\n />\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('members.list.refresh', 'Refresh members')}\n title={tr('members.list.refresh', 'Refresh members')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {filteredMembers.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedMemberIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedMemberIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedMemberIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={handleBulkRemove}\n disabled={selectedMembers.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.removeSelected', 'Remove Selected')}</span>\n {selectedMembers.length > 0 ? <span>({selectedMembers.length})</span> : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {filteredMembers.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Shield size={40} aria-hidden=\"true\" />}\n title={tr('members.list.emptyTitle', 'No members found')}\n description={tr(\n 'members.list.emptyDescription',\n 'Invite someone to this organization to grant them access and assign their role.'\n )}\n action={\n inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? {\n label: tr('members.invite.button', 'Invite Member'),\n onClick: () => navigate(`${basePath}/${organizationId}/members/invite`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"break-all text-base font-semibold text-[var(--color-text-primary)]\">\n {profileMap[member.userId]?.displayName ??\n `User ${member.userId.slice(0, 8)}…`}\n </h2>\n <MemberRoleBadge role={member.role} />\n </div>\n {profileMap[member.userId]?.email ? (\n <p className=\"mt-0.5 break-all text-xs text-[var(--color-text-secondary)]\">\n {profileMap[member.userId]?.email}\n </p>\n ) : null}\n <details className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.list.showIds', 'Show IDs')}\n </summary>\n <p className=\"mt-1 break-all font-mono\">\n {tr('members.detail.userId', 'User ID')}: {member.userId}\n </p>\n <p className=\"mt-0.5 break-all font-mono\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </details>\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-primary)]\">\n {formatDate(member.joinedAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('members.remove.confirm', 'Remove')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.user', 'User')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"cell\"\n />\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js
CHANGED
|
@@ -1,155 +1,165 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
|
|
2
|
+
import { toast as t } from "../../../../node_modules/sonner/dist/index.js";
|
|
3
|
+
import { useTr as n } from "../../../shared/hooks/useTr.js";
|
|
4
|
+
import { useMembers as r } from "../../members/hooks/useMembers.js";
|
|
5
|
+
import { useAddWorkspaceMember as i } from "../hooks/useWorkspaceMembers.js";
|
|
6
|
+
import { useEffect as a, useMemo as o, useState as s } from "react";
|
|
7
|
+
import { AlertCircle as c, Loader2 as l, UserPlus as u } from "lucide-react";
|
|
8
|
+
import { ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogFooter as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g } from "@burdenoff/fe-libs/ui";
|
|
9
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { useActorProfiles as y } from "@burdenoff/fe-libs/shared";
|
|
9
11
|
//#region src/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.tsx
|
|
10
|
-
function
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
function b({ open: b, onOpenChange: x, workspaceId: S, organizationId: C, existingMembers: w, onInviteOrganizationMember: T }) {
|
|
13
|
+
let E = n(), [D, O] = s(""), [k, A] = s("MEMBER"), [j, M] = s(null), { members: N, loading: P } = r(C, 100, 0, b), { addWorkspaceMember: F, loading: I } = i(), L = o(() => new Set(w.map((e) => e.userId)), [w]), R = o(() => N.filter((e) => !L.has(e.userId)), [L, N]), { apiGatewayUrl: z, authToken: B } = e(), V = y(o(() => R.map((e) => ({
|
|
14
|
+
actorId: e.userId,
|
|
15
|
+
actorType: "user"
|
|
16
|
+
})), [R]), {
|
|
17
|
+
apiGatewayUrl: z,
|
|
18
|
+
authToken: B,
|
|
19
|
+
orgId: C,
|
|
20
|
+
workspaceId: S
|
|
21
|
+
});
|
|
22
|
+
return a(() => {
|
|
23
|
+
if (!b) {
|
|
24
|
+
O(""), A("MEMBER"), M(null);
|
|
15
25
|
return;
|
|
16
26
|
}
|
|
17
|
-
!
|
|
27
|
+
!D && R.length > 0 && O(R[0]?.userId ?? "");
|
|
18
28
|
}, [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
]), /* @__PURE__ */
|
|
23
|
-
open:
|
|
24
|
-
onOpenChange:
|
|
25
|
-
children: /* @__PURE__ */
|
|
29
|
+
R,
|
|
30
|
+
b,
|
|
31
|
+
D
|
|
32
|
+
]), /* @__PURE__ */ _(d, {
|
|
33
|
+
open: b,
|
|
34
|
+
onOpenChange: x,
|
|
35
|
+
children: /* @__PURE__ */ v(f, {
|
|
26
36
|
className: "sm:max-w-[520px] border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]",
|
|
27
|
-
children: [/* @__PURE__ */
|
|
37
|
+
children: [/* @__PURE__ */ v(h, { children: [/* @__PURE__ */ v(g, {
|
|
28
38
|
className: "flex items-center gap-2 text-[var(--color-text-primary)]",
|
|
29
|
-
children: [/* @__PURE__ */
|
|
39
|
+
children: [/* @__PURE__ */ _(u, {
|
|
30
40
|
size: 18,
|
|
31
41
|
"aria-hidden": "true"
|
|
32
|
-
}),
|
|
33
|
-
}), /* @__PURE__ */
|
|
42
|
+
}), E("workspaces.addMember.title", "Add workspace member")]
|
|
43
|
+
}), /* @__PURE__ */ _(p, {
|
|
34
44
|
className: "text-[var(--color-text-secondary)]",
|
|
35
|
-
children:
|
|
36
|
-
})] }), /* @__PURE__ */
|
|
37
|
-
onSubmit: async (
|
|
38
|
-
if (
|
|
39
|
-
|
|
45
|
+
children: E("workspaces.addMember.description", "Choose an existing organization member and assign their workspace role.")
|
|
46
|
+
})] }), /* @__PURE__ */ v("form", {
|
|
47
|
+
onSubmit: async (e) => {
|
|
48
|
+
if (e.preventDefault(), !D) {
|
|
49
|
+
M(E("workspaces.addMember.selectRequired", "Select an organization member to add to this workspace."));
|
|
40
50
|
return;
|
|
41
51
|
}
|
|
42
52
|
try {
|
|
43
|
-
|
|
44
|
-
let
|
|
45
|
-
workspaceId:
|
|
46
|
-
userId:
|
|
47
|
-
role:
|
|
53
|
+
M(null);
|
|
54
|
+
let e = await F({
|
|
55
|
+
workspaceId: S,
|
|
56
|
+
userId: D,
|
|
57
|
+
role: k
|
|
48
58
|
});
|
|
49
|
-
if (
|
|
50
|
-
|
|
59
|
+
if (e.errors?.length) throw Error(e.errors[0]?.message ?? E("workspaces.addMember.failed", "Failed to add workspace member"));
|
|
60
|
+
t.success(E("workspaces.addMember.success", "Workspace member added successfully")), x(!1);
|
|
51
61
|
} catch (e) {
|
|
52
|
-
|
|
62
|
+
M(e instanceof Error ? e.message : E("workspaces.addMember.failed", "Failed to add workspace member"));
|
|
53
63
|
}
|
|
54
64
|
},
|
|
55
65
|
className: "space-y-4",
|
|
56
66
|
children: [
|
|
57
|
-
|
|
67
|
+
j ? /* @__PURE__ */ v("div", {
|
|
58
68
|
className: "flex items-start gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3 text-sm text-[var(--color-status-error-text)]",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ _(c, {
|
|
60
70
|
size: 16,
|
|
61
71
|
className: "mt-0.5 shrink-0",
|
|
62
72
|
"aria-hidden": "true"
|
|
63
|
-
}), /* @__PURE__ */
|
|
73
|
+
}), /* @__PURE__ */ _("span", { children: j })]
|
|
64
74
|
}) : null,
|
|
65
|
-
/* @__PURE__ */
|
|
66
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ v("div", { children: [
|
|
76
|
+
/* @__PURE__ */ _("label", {
|
|
67
77
|
htmlFor: "workspace-member-user",
|
|
68
78
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
69
|
-
children:
|
|
79
|
+
children: E("workspaces.addMember.memberLabel", "Organization member")
|
|
70
80
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ v("select", {
|
|
72
82
|
id: "workspace-member-user",
|
|
73
|
-
value:
|
|
74
|
-
onChange: (e) =>
|
|
75
|
-
disabled:
|
|
83
|
+
value: D,
|
|
84
|
+
onChange: (e) => O(e.target.value),
|
|
85
|
+
disabled: P || I || R.length === 0,
|
|
76
86
|
className: "w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]",
|
|
77
|
-
children: [
|
|
87
|
+
children: [R.length === 0 ? /* @__PURE__ */ _("option", {
|
|
78
88
|
value: "",
|
|
79
|
-
children:
|
|
80
|
-
}) : null,
|
|
89
|
+
children: E("workspaces.addMember.noneAvailable", "No eligible organization members available")
|
|
90
|
+
}) : null, R.map((e) => /* @__PURE__ */ v("option", {
|
|
81
91
|
value: e.userId,
|
|
82
92
|
children: [
|
|
83
|
-
e.userId,
|
|
93
|
+
V[e.userId]?.displayName ?? e.userId,
|
|
84
94
|
" (",
|
|
85
95
|
e.role,
|
|
86
96
|
")"
|
|
87
97
|
]
|
|
88
98
|
}, e.id))]
|
|
89
99
|
}),
|
|
90
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ _("p", {
|
|
91
101
|
className: "mt-1 text-xs text-[var(--color-text-muted)]",
|
|
92
|
-
children:
|
|
102
|
+
children: E("workspaces.addMember.filterHelp", "Only members that belong to this organization and are not already in the workspace are listed.")
|
|
93
103
|
}),
|
|
94
|
-
|
|
104
|
+
R.length === 0 && T ? /* @__PURE__ */ _("button", {
|
|
95
105
|
type: "button",
|
|
96
106
|
onClick: () => {
|
|
97
|
-
|
|
107
|
+
x(!1), T();
|
|
98
108
|
},
|
|
99
109
|
className: "mt-2 inline-flex items-center gap-2 text-sm font-medium text-[var(--color-action-primary-bg)] hover:underline",
|
|
100
|
-
children:
|
|
110
|
+
children: E("workspaces.addMember.inviteInstead", "Invite a new organization member instead")
|
|
101
111
|
}) : null,
|
|
102
|
-
|
|
112
|
+
T ? /* @__PURE__ */ v("p", {
|
|
103
113
|
className: "mt-2 text-xs text-[var(--color-text-muted)]",
|
|
104
114
|
children: [
|
|
105
|
-
|
|
115
|
+
E("workspaces.addMember.bulkInviteHint", "Need to add many people who are not in this organization yet?"),
|
|
106
116
|
" ",
|
|
107
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ _("button", {
|
|
108
118
|
type: "button",
|
|
109
119
|
onClick: () => {
|
|
110
|
-
|
|
120
|
+
x(!1), T();
|
|
111
121
|
},
|
|
112
122
|
className: "font-medium text-[var(--color-action-primary-bg)] hover:underline",
|
|
113
|
-
children:
|
|
123
|
+
children: E("workspaces.addMember.bulkInviteLink", "Bulk-invite them from the organization invite page")
|
|
114
124
|
}),
|
|
115
125
|
"."
|
|
116
126
|
]
|
|
117
127
|
}) : null
|
|
118
128
|
] }),
|
|
119
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
120
130
|
htmlFor: "workspace-member-role",
|
|
121
131
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
122
|
-
children:
|
|
123
|
-
}), /* @__PURE__ */
|
|
132
|
+
children: E("workspaces.addMember.roleLabel", "Workspace role")
|
|
133
|
+
}), /* @__PURE__ */ v("select", {
|
|
124
134
|
id: "workspace-member-role",
|
|
125
|
-
value:
|
|
126
|
-
onChange: (e) =>
|
|
127
|
-
disabled:
|
|
135
|
+
value: k,
|
|
136
|
+
onChange: (e) => A(e.target.value),
|
|
137
|
+
disabled: I,
|
|
128
138
|
className: "w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ _("option", {
|
|
130
140
|
value: "MEMBER",
|
|
131
|
-
children:
|
|
132
|
-
}), /* @__PURE__ */
|
|
141
|
+
children: E("members.role.member", "Member")
|
|
142
|
+
}), /* @__PURE__ */ _("option", {
|
|
133
143
|
value: "WORKSPACE_ADMIN",
|
|
134
|
-
children:
|
|
144
|
+
children: E("members.role.workspaceAdmin", "Workspace Admin")
|
|
135
145
|
})]
|
|
136
146
|
})] }),
|
|
137
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ v(m, {
|
|
138
148
|
className: "gap-3 sm:gap-0",
|
|
139
|
-
children: [/* @__PURE__ */
|
|
149
|
+
children: [/* @__PURE__ */ _("button", {
|
|
140
150
|
type: "button",
|
|
141
|
-
onClick: () =>
|
|
151
|
+
onClick: () => x(!1),
|
|
142
152
|
className: "rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]",
|
|
143
|
-
disabled:
|
|
144
|
-
children:
|
|
145
|
-
}), /* @__PURE__ */
|
|
153
|
+
disabled: I,
|
|
154
|
+
children: E("common.cancel", "Cancel")
|
|
155
|
+
}), /* @__PURE__ */ v("button", {
|
|
146
156
|
type: "submit",
|
|
147
157
|
className: "inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50",
|
|
148
|
-
disabled:
|
|
149
|
-
children: [
|
|
158
|
+
disabled: I || P || R.length === 0,
|
|
159
|
+
children: [I ? /* @__PURE__ */ _(l, {
|
|
150
160
|
size: 16,
|
|
151
161
|
className: "animate-spin"
|
|
152
|
-
}) : null,
|
|
162
|
+
}) : null, E("workspaces.addMember.submit", "Add member")]
|
|
153
163
|
})]
|
|
154
164
|
})
|
|
155
165
|
]
|
|
@@ -158,6 +168,6 @@ function v({ open: v, onOpenChange: y, workspaceId: b, organizationId: x, existi
|
|
|
158
168
|
});
|
|
159
169
|
}
|
|
160
170
|
//#endregion
|
|
161
|
-
export {
|
|
171
|
+
export { b as AddWorkspaceMemberDialog };
|
|
162
172
|
|
|
163
173
|
//# sourceMappingURL=AddWorkspaceMemberDialog.js.map
|
package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddWorkspaceMemberDialog.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Loader2, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useMembers } from '../../members/hooks/useMembers';\nimport { useAddWorkspaceMember } from '../hooks/useWorkspaceMembers';\nimport type { WorkspaceMember } from '../types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AddWorkspaceMemberDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n organizationId: string;\n existingMembers: WorkspaceMember[];\n onInviteOrganizationMember?: () => void;\n}\n\nexport function AddWorkspaceMemberDialog({\n open,\n onOpenChange,\n workspaceId,\n organizationId,\n existingMembers,\n onInviteOrganizationMember,\n}: AddWorkspaceMemberDialogProps) {\n const tr = useTr();\n const [selectedUserId, setSelectedUserId] = useState('');\n const [selectedRole, setSelectedRole] = useState<'MEMBER' | 'WORKSPACE_ADMIN'>('MEMBER');\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n\n const { members: organizationMembers, loading: membersLoading } = useMembers(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId,\n 100,\n 0,\n // react-doctor-disable-next-line react-doctor/no-event-handler\n open\n );\n const { addWorkspaceMember, loading: addingMember } = useAddWorkspaceMember();\n\n const existingUserIds = useMemo(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n () => new Set(existingMembers.map((member) => member.userId)),\n [existingMembers]\n );\n\n const availableMembers = useMemo(\n () => organizationMembers.filter((member) => !existingUserIds.has(member.userId)),\n [existingUserIds, organizationMembers]\n );\n\n useEffect(() => {\n if (!open) {\n setSelectedUserId('');\n setSelectedRole('MEMBER');\n setSubmissionError(null);\n return;\n }\n\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (!selectedUserId && availableMembers.length > 0) {\n setSelectedUserId(availableMembers[0]?.userId ?? '');\n }\n }, [availableMembers, open, selectedUserId]);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!selectedUserId) {\n setSubmissionError(\n tr(\n 'workspaces.addMember.selectRequired',\n 'Select an organization member to add to this workspace.'\n )\n );\n return;\n }\n\n try {\n setSubmissionError(null);\n const result = await addWorkspaceMember({\n workspaceId,\n userId: selectedUserId,\n role: selectedRole,\n });\n\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n\n toast.success(tr('workspaces.addMember.success', 'Workspace member added successfully'));\n onOpenChange(false);\n } catch (error) {\n setSubmissionError(\n error instanceof Error\n ? error.message\n : tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[520px] border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2 text-[var(--color-text-primary)]\">\n <UserPlus size={18} aria-hidden=\"true\" />\n {tr('workspaces.addMember.title', 'Add workspace member')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.addMember.description',\n 'Choose an existing organization member and assign their workspace role.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {submissionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3 text-sm text-[var(--color-status-error-text)]\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" aria-hidden=\"true\" />\n <span>{submissionError}</span>\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"workspace-member-user\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.memberLabel', 'Organization member')}\n </label>\n <select\n id=\"workspace-member-user\"\n value={selectedUserId}\n onChange={(event) => setSelectedUserId(event.target.value)}\n disabled={membersLoading || addingMember || availableMembers.length === 0}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n >\n {availableMembers.length === 0 ? (\n <option value=\"\">\n {tr(\n 'workspaces.addMember.noneAvailable',\n 'No eligible organization members available'\n )}\n </option>\n ) : null}\n {availableMembers.map((member) => (\n <option key={member.id} value={member.userId}>\n {member.userId} ({member.role})\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.filterHelp',\n 'Only members that belong to this organization and are not already in the workspace are listed.'\n )}\n </p>\n {availableMembers.length === 0 && onInviteOrganizationMember ? (\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"mt-2 inline-flex items-center gap-2 text-sm font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.inviteInstead',\n 'Invite a new organization member instead'\n )}\n </button>\n ) : null}\n {onInviteOrganizationMember ? (\n <p className=\"mt-2 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.bulkInviteHint',\n 'Need to add many people who are not in this organization yet?'\n )}{' '}\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.bulkInviteLink',\n 'Bulk-invite them from the organization invite page'\n )}\n </button>\n .\n </p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"workspace-member-role\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.roleLabel', 'Workspace role')}\n </label>\n <select\n id=\"workspace-member-role\"\n value={selectedRole}\n onChange={(event) =>\n setSelectedRole(event.target.value as 'MEMBER' | 'WORKSPACE_ADMIN')\n }\n disabled={addingMember}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n >\n <option value=\"MEMBER\">{tr('members.role.member', 'Member')}</option>\n <option value=\"WORKSPACE_ADMIN\">\n {tr('members.role.workspaceAdmin', 'Workspace Admin')}\n </option>\n </select>\n </div>\n\n <ResponsiveDialogFooter className=\"gap-3 sm:gap-0\">\n <button\n type=\"button\"\n onClick={() => onOpenChange(false)}\n className=\"rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n disabled={addingMember}\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={addingMember || membersLoading || availableMembers.length === 0}\n >\n {addingMember ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {tr('workspaces.addMember.submit', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddWorkspaceMemberDialog.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Loader2, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useMembers } from '../../members/hooks/useMembers';\nimport { useAddWorkspaceMember } from '../hooks/useWorkspaceMembers';\nimport type { WorkspaceMember } from '../types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AddWorkspaceMemberDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n organizationId: string;\n existingMembers: WorkspaceMember[];\n onInviteOrganizationMember?: () => void;\n}\n\nexport function AddWorkspaceMemberDialog({\n open,\n onOpenChange,\n workspaceId,\n organizationId,\n existingMembers,\n onInviteOrganizationMember,\n}: AddWorkspaceMemberDialogProps) {\n const tr = useTr();\n const [selectedUserId, setSelectedUserId] = useState('');\n const [selectedRole, setSelectedRole] = useState<'MEMBER' | 'WORKSPACE_ADMIN'>('MEMBER');\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n\n const { members: organizationMembers, loading: membersLoading } = useMembers(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId,\n 100,\n 0,\n // react-doctor-disable-next-line react-doctor/no-event-handler\n open\n );\n const { addWorkspaceMember, loading: addingMember } = useAddWorkspaceMember();\n\n const existingUserIds = useMemo(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n () => new Set(existingMembers.map((member) => member.userId)),\n [existingMembers]\n );\n\n const availableMembers = useMemo(\n () => organizationMembers.filter((member) => !existingUserIds.has(member.userId)),\n [existingUserIds, organizationMembers]\n );\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => availableMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [availableMembers]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n useEffect(() => {\n if (!open) {\n setSelectedUserId('');\n setSelectedRole('MEMBER');\n setSubmissionError(null);\n return;\n }\n\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (!selectedUserId && availableMembers.length > 0) {\n setSelectedUserId(availableMembers[0]?.userId ?? '');\n }\n }, [availableMembers, open, selectedUserId]);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!selectedUserId) {\n setSubmissionError(\n tr(\n 'workspaces.addMember.selectRequired',\n 'Select an organization member to add to this workspace.'\n )\n );\n return;\n }\n\n try {\n setSubmissionError(null);\n const result = await addWorkspaceMember({\n workspaceId,\n userId: selectedUserId,\n role: selectedRole,\n });\n\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n\n toast.success(tr('workspaces.addMember.success', 'Workspace member added successfully'));\n onOpenChange(false);\n } catch (error) {\n setSubmissionError(\n error instanceof Error\n ? error.message\n : tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[520px] border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2 text-[var(--color-text-primary)]\">\n <UserPlus size={18} aria-hidden=\"true\" />\n {tr('workspaces.addMember.title', 'Add workspace member')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.addMember.description',\n 'Choose an existing organization member and assign their workspace role.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {submissionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3 text-sm text-[var(--color-status-error-text)]\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" aria-hidden=\"true\" />\n <span>{submissionError}</span>\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"workspace-member-user\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.memberLabel', 'Organization member')}\n </label>\n <select\n id=\"workspace-member-user\"\n value={selectedUserId}\n onChange={(event) => setSelectedUserId(event.target.value)}\n disabled={membersLoading || addingMember || availableMembers.length === 0}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n >\n {availableMembers.length === 0 ? (\n <option value=\"\">\n {tr(\n 'workspaces.addMember.noneAvailable',\n 'No eligible organization members available'\n )}\n </option>\n ) : null}\n {availableMembers.map((member) => (\n <option key={member.id} value={member.userId}>\n {profileMap[member.userId]?.displayName ?? member.userId} ({member.role})\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.filterHelp',\n 'Only members that belong to this organization and are not already in the workspace are listed.'\n )}\n </p>\n {availableMembers.length === 0 && onInviteOrganizationMember ? (\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"mt-2 inline-flex items-center gap-2 text-sm font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.inviteInstead',\n 'Invite a new organization member instead'\n )}\n </button>\n ) : null}\n {onInviteOrganizationMember ? (\n <p className=\"mt-2 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.bulkInviteHint',\n 'Need to add many people who are not in this organization yet?'\n )}{' '}\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.bulkInviteLink',\n 'Bulk-invite them from the organization invite page'\n )}\n </button>\n .\n </p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"workspace-member-role\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.roleLabel', 'Workspace role')}\n </label>\n <select\n id=\"workspace-member-role\"\n value={selectedRole}\n onChange={(event) =>\n setSelectedRole(event.target.value as 'MEMBER' | 'WORKSPACE_ADMIN')\n }\n disabled={addingMember}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n >\n <option value=\"MEMBER\">{tr('members.role.member', 'Member')}</option>\n <option value=\"WORKSPACE_ADMIN\">\n {tr('members.role.workspaceAdmin', 'Workspace Admin')}\n </option>\n </select>\n </div>\n\n <ResponsiveDialogFooter className=\"gap-3 sm:gap-0\">\n <button\n type=\"button\"\n onClick={() => onOpenChange(false)}\n className=\"rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n disabled={addingMember}\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={addingMember || membersLoading || availableMembers.length === 0}\n >\n {addingMember ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {tr('workspaces.addMember.submit', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|