@burdenoff/microfe-organization 2026.528.3 → 2026.529.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Member Detail Page\n * Simplified version showing available member fields\n */\nexport function MemberDetailPage() {\n const tr = useTr();\n const { organizationId, userId } = useParams<{ organizationId: string; userId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { member, loading, error } = useMember(organizationId!, userId!);\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_READ)) {\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.detail.noPermission', \"You don't have permission to view member details.\")}\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.detail.loading', 'Loading member...')} />\n </div>\n );\n }\n\n if (error || !member) {\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 || tr('members.detail.notFound', 'Member not found')}\n action={{\n label: tr('common.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/${organizationId}/members`),\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 {/* Header */}\n <div className=\"mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members`)}\n className=\"inline-flex items-center gap-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] active:scale-[0.98] transition-all duration-200 mb-4\"\n >\n <ArrowLeft size={20} />\n {tr('members.detail.backToMembers', 'Back to Members')}\n </button>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-full bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Shield size={32} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div>\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.detail.titlePrefix', 'Member')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-1\">\n {member.role}\n </p>\n <details className=\"mt-2 text-xs text-[var(--color-text-secondary)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.detail.showIds', 'Show technical IDs')}\n </summary>\n <dl className=\"mt-1 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1\">\n <dt>{tr('members.detail.userId', 'User ID')}</dt>\n <dd className=\"break-all font-mono\">{member.userId}</dd>\n <dt>{tr('members.list.memberId', 'Member ID')}</dt>\n <dd className=\"break-all font-mono\">{member.id}</dd>\n </dl>\n </details>\n </div>\n </div>\n </div>\n\n {/* Details */}\n <div className=\"bg-[var(--color-bg-surface)] border border-[var(--color-border-default)] rounded-lg p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.detail.info', 'Member Information')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </p>\n <div className=\"mt-1\">\n <MemberRoleBadge role={member.role} />\n </div>\n </div>\n\n {member.invitedBy && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.invitedBy', 'Invited By')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.invitedBy}</p>\n </div>\n )}\n\n {member.joinedAt && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.joinedAt', 'Joined At')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {new Date(member.joinedAt).toLocaleString()}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Member Detail Page\n * Simplified version showing available member fields\n */\nexport function MemberDetailPage() {\n const tr = useTr();\n const { organizationId, userId } = useParams<{ organizationId: string; userId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { member, loading, error } = useMember(organizationId!, userId!);\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_READ)) {\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.detail.noPermission', \"You don't have permission to view member details.\")}\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.detail.loading', 'Loading member...')} />\n </div>\n );\n }\n\n if (error || !member) {\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 || tr('members.detail.notFound', 'Member not found')}\n action={{\n label: tr('common.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/${organizationId}/members`),\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 {/* Header */}\n <div className=\"mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members`)}\n className=\"inline-flex items-center gap-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] active:scale-[0.98] transition-all duration-200 mb-4\"\n >\n <ArrowLeft size={20} />\n {tr('members.detail.backToMembers', 'Back to Members')}\n </button>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-full bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Shield size={32} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div>\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.detail.titlePrefix', 'Member')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-1\">{member.role}</p>\n <details className=\"mt-2 text-xs text-[var(--color-text-secondary)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.detail.showIds', 'Show technical IDs')}\n </summary>\n <dl className=\"mt-1 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1\">\n <dt>{tr('members.detail.userId', 'User ID')}</dt>\n <dd className=\"break-all font-mono\">{member.userId}</dd>\n <dt>{tr('members.list.memberId', 'Member ID')}</dt>\n <dd className=\"break-all font-mono\">{member.id}</dd>\n </dl>\n </details>\n </div>\n </div>\n </div>\n\n {/* Details */}\n <div className=\"bg-[var(--color-bg-surface)] border border-[var(--color-border-default)] rounded-lg p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.detail.info', 'Member Information')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </p>\n <div className=\"mt-1\">\n <MemberRoleBadge role={member.role} />\n </div>\n </div>\n\n {member.invitedBy && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.invitedBy', 'Invited By')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.invitedBy}</p>\n </div>\n )}\n\n {member.joinedAt && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.joinedAt', 'Joined At')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {new Date(member.joinedAt).toLocaleString()}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -268,8 +268,7 @@ function C() {
268
268
  className: "mt-1 break-all font-mono",
269
269
  children: [
270
270
  C("members.detail.userId", "User ID"),
271
- ":",
272
- " ",
271
+ ": ",
273
272
  e.userId
274
273
  ]
275
274
  }),
@@ -277,8 +276,7 @@ function C() {
277
276
  className: "mt-0.5 break-all font-mono",
278
277
  children: [
279
278
  C("members.list.memberId", "Member ID"),
280
- ":",
281
- " ",
279
+ ": ",
282
280
  e.id
283
281
  ]
284
282
  })
@@ -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 {\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 ?? `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')}:{' '}\n {member.userId}\n </p>\n <p className=\"mt-0.5 break-all font-mono\">\n {tr('members.list.memberId', 'Member ID')}:{' '}\n {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":"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"}
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":"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"}
@@ -21,11 +21,11 @@ function f(e, t = 20, n = 0, r = !0) {
21
21
  nextFetchPolicy: "cache-first"
22
22
  });
23
23
  return {
24
- organizations: a?.organizations?.items || [],
25
- total: a?.organizations?.total || 0,
26
- limit: a?.organizations?.limit || t,
27
- offset: a?.organizations?.offset || n,
28
- hasMore: a?.organizations?.hasMore || !1,
24
+ organizations: a?.allOrganizations?.items || [],
25
+ total: a?.allOrganizations?.total || 0,
26
+ limit: a?.allOrganizations?.limit || t,
27
+ offset: a?.allOrganizations?.offset || n,
28
+ hasMore: a?.allOrganizations?.hasMore || !1,
29
29
  loading: o,
30
30
  error: s,
31
31
  refetch: c,
@@ -1 +1 @@
1
- {"version":3,"file":"useOrganizations.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/hooks/useOrganizations.ts"],"sourcesContent":["import {\n useGetOrganizationsQuery,\n useGetMyOrganizationsQuery,\n useGetOrganizationQuery,\n useGetOrganizationBySlugQuery,\n useGetOrganizationsByTenantQuery,\n useCreateOrganizationMutation,\n useUpdateOrganizationMutation,\n useDeleteOrganizationMutation,\n} from '../../../../generated/global-operations';\nimport type {\n Organization,\n OrganizationFilterInput,\n CreateOrganizationInput,\n UpdateOrganizationInput,\n} from '../types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\nimport { executeRecaptcha } from '../../../shared/recaptcha';\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organizations with filtering and pagination\n *\n * @deprecated Calls the platform-wide `organizations` admin query — which is\n * gated by global RBAC perm and will be removed once callers migrate. For the\n * end-user \"my orgs\" view use `useMyOrganizations`. For the admin \"all orgs\"\n * view (once the admin app pages land) use the upcoming `useAllOrganizations`.\n */\nexport function useOrganizations(\n filter?: OrganizationFilterInput,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetOrganizationsQuery({\n variables: { filter, limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.organizations?.items as Organization[]) || [],\n total: data?.organizations?.total || 0,\n limit: data?.organizations?.limit || limit,\n offset: data?.organizations?.offset || offset,\n hasMore: data?.organizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch organizations the current user is a member of (owned + added to).\n *\n * Backed by the `myOrganizations` query which uses @rbac(scopeType: \"self\") —\n * authenticated user only, no permission check, resolver filters by actorId.\n * Use this in product-app contexts where the user should see *their* orgs.\n *\n * Filter input is intentionally not supported — `myOrganizations` returns the\n * full membership list and any further filtering should be done client-side\n * (or via a future `myOrganizationsFiltered` query if the use-case warrants it).\n */\nexport function useMyOrganizations(\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMyOrganizationsQuery({\n variables: { limit, offset },\n skip: !enabled,\n // First mount: use cache if present, then revalidate from the network in\n // the background. Subsequent renders within the same session reuse the\n // cache. This keeps the list fresh when the user navigates back after\n // accepting an invite in another tab without paying the round-trip on\n // every re-render.\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.myOrganizations?.items as Organization[]) || [],\n total: data?.myOrganizations?.total || 0,\n limit: data?.myOrganizations?.limit || limit,\n offset: data?.myOrganizations?.offset || offset,\n hasMore: data?.myOrganizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single organization by ID\n */\nexport function useOrganization(id: string) {\n const { data, loading, error, refetch } = useGetOrganizationQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const organization = data?.organization as Organization | undefined;\n\n return {\n organization,\n loading: loading && !organization,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch an organization by slug\n */\nexport function useOrganizationBySlug(tenantId: string, slug: string) {\n const { data, loading, error, refetch } = useGetOrganizationBySlugQuery({\n variables: { tenantId, slug },\n skip: !tenantId || !slug,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organization: data?.organizationBySlug as Organization | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch organizations by tenant ID\n */\nexport function useOrganizationsByTenant(tenantId: string, limit: number = 20, offset: number = 0) {\n const { data, loading, error, refetch, fetchMore } = useGetOrganizationsByTenantQuery({\n variables: { tenantId, limit, offset },\n skip: !tenantId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.organizations?.items as Organization[]) || [],\n total: data?.organizations?.total || 0,\n limit: data?.organizations?.limit || limit,\n offset: data?.organizations?.offset || offset,\n hasMore: data?.organizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new organization\n */\nexport function useCreateOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [createOrganization, { data, loading, error }] = useCreateOrganizationMutation();\n\n return {\n createOrganization: async (input: CreateOrganizationInput) => {\n logger.info('mutation.start', { name: 'createOrganization' });\n try {\n const recaptchaToken = await executeRecaptcha('organization_create');\n const result = await createOrganization({\n variables: {\n input: {\n ...input,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n },\n },\n });\n const organizationId = result.data?.createOrganization?.id;\n emit('organization.created', { organizationId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'createOrganization',\n organizationId: redactId(organizationId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.create_failed', {\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n { name: 'createOrganization', errorCode, errorMessage },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update an organization\n */\nexport function useUpdateOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [updateOrganization, { data, loading, error }] = useUpdateOrganizationMutation();\n\n return {\n updateOrganization: async (id: string, input: UpdateOrganizationInput) => {\n logger.info('mutation.start', {\n name: 'updateOrganization',\n organizationId: redactId(id),\n });\n try {\n const result = await updateOrganization({ variables: { id, input } });\n emit('organization.updated', { organizationId: id, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'updateOrganization',\n organizationId: redactId(id),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.update_failed', {\n organizationId: id,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'updateOrganization',\n organizationId: redactId(id),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete an organization\n */\nexport function useDeleteOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [deleteOrganization, { data, loading, error }] = useDeleteOrganizationMutation();\n\n return {\n deleteOrganization: async (id: string) => {\n logger.info('mutation.start', {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n });\n try {\n const result = await deleteOrganization({ variables: { id } });\n emit('organization.deleted', { organizationId: id, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.delete_failed', {\n organizationId: id,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useOrganizations.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/hooks/useOrganizations.ts"],"sourcesContent":["import {\n useGetOrganizationsQuery,\n useGetMyOrganizationsQuery,\n useGetOrganizationQuery,\n useGetOrganizationBySlugQuery,\n useGetOrganizationsByTenantQuery,\n useCreateOrganizationMutation,\n useUpdateOrganizationMutation,\n useDeleteOrganizationMutation,\n} from '../../../../generated/global-operations';\nimport type {\n Organization,\n OrganizationFilterInput,\n CreateOrganizationInput,\n UpdateOrganizationInput,\n} from '../types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\nimport { executeRecaptcha } from '../../../shared/recaptcha';\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organizations with filtering and pagination\n *\n * Calls the platform-wide `allOrganizations` admin query. For the end-user\n * \"my orgs\" view use `useMyOrganizations`.\n */\nexport function useOrganizations(\n filter?: OrganizationFilterInput,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetOrganizationsQuery({\n variables: { filter, limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.allOrganizations?.items as Organization[]) || [],\n total: data?.allOrganizations?.total || 0,\n limit: data?.allOrganizations?.limit || limit,\n offset: data?.allOrganizations?.offset || offset,\n hasMore: data?.allOrganizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch organizations the current user is a member of (owned + added to).\n *\n * Backed by the `myOrganizations` query which uses @rbac(scopeType: \"self\") —\n * authenticated user only, no permission check, resolver filters by actorId.\n * Use this in product-app contexts where the user should see *their* orgs.\n *\n * Filter input is intentionally not supported — `myOrganizations` returns the\n * full membership list and any further filtering should be done client-side\n * (or via a future `myOrganizationsFiltered` query if the use-case warrants it).\n */\nexport function useMyOrganizations(\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMyOrganizationsQuery({\n variables: { limit, offset },\n skip: !enabled,\n // First mount: use cache if present, then revalidate from the network in\n // the background. Subsequent renders within the same session reuse the\n // cache. This keeps the list fresh when the user navigates back after\n // accepting an invite in another tab without paying the round-trip on\n // every re-render.\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.myOrganizations?.items as Organization[]) || [],\n total: data?.myOrganizations?.total || 0,\n limit: data?.myOrganizations?.limit || limit,\n offset: data?.myOrganizations?.offset || offset,\n hasMore: data?.myOrganizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single organization by ID\n */\nexport function useOrganization(id: string) {\n const { data, loading, error, refetch } = useGetOrganizationQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const organization = data?.organization as Organization | undefined;\n\n return {\n organization,\n loading: loading && !organization,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch an organization by slug\n */\nexport function useOrganizationBySlug(tenantId: string, slug: string) {\n const { data, loading, error, refetch } = useGetOrganizationBySlugQuery({\n variables: { tenantId, slug },\n skip: !tenantId || !slug,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organization: data?.organizationBySlug as Organization | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch organizations by tenant ID\n */\nexport function useOrganizationsByTenant(tenantId: string, limit: number = 20, offset: number = 0) {\n const { data, loading, error, refetch, fetchMore } = useGetOrganizationsByTenantQuery({\n variables: { tenantId, limit, offset },\n skip: !tenantId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n organizations: (data?.allOrganizations?.items as Organization[]) || [],\n total: data?.allOrganizations?.total || 0,\n limit: data?.allOrganizations?.limit || limit,\n offset: data?.allOrganizations?.offset || offset,\n hasMore: data?.allOrganizations?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new organization\n */\nexport function useCreateOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [createOrganization, { data, loading, error }] = useCreateOrganizationMutation();\n\n return {\n createOrganization: async (input: CreateOrganizationInput) => {\n logger.info('mutation.start', { name: 'createOrganization' });\n try {\n const recaptchaToken = await executeRecaptcha('organization_create');\n const result = await createOrganization({\n variables: {\n input: {\n ...input,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n },\n },\n });\n const organizationId = result.data?.createOrganization?.id;\n emit('organization.created', { organizationId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'createOrganization',\n organizationId: redactId(organizationId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.create_failed', {\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n { name: 'createOrganization', errorCode, errorMessage },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update an organization\n */\nexport function useUpdateOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [updateOrganization, { data, loading, error }] = useUpdateOrganizationMutation();\n\n return {\n updateOrganization: async (id: string, input: UpdateOrganizationInput) => {\n logger.info('mutation.start', {\n name: 'updateOrganization',\n organizationId: redactId(id),\n });\n try {\n const result = await updateOrganization({ variables: { id, input } });\n emit('organization.updated', { organizationId: id, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'updateOrganization',\n organizationId: redactId(id),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.update_failed', {\n organizationId: id,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'updateOrganization',\n organizationId: redactId(id),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete an organization\n */\nexport function useDeleteOrganization() {\n const { emit } = useOrganizationEventEmitter();\n const [deleteOrganization, { data, loading, error }] = useDeleteOrganizationMutation();\n\n return {\n deleteOrganization: async (id: string) => {\n logger.info('mutation.start', {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n });\n try {\n const result = await deleteOrganization({ variables: { id } });\n emit('organization.deleted', { organizationId: id, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.delete_failed', {\n organizationId: id,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'deleteOrganization',\n organizationId: redactId(id),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n data,\n loading,\n error,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building2, Eye, Mail, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n useDeleteOrganization,\n useMyOrganizations,\n useOrganizations,\n} from '../hooks/useOrganizations';\nimport { useMyInvites } from '../../members/hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\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';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls which scope of organizations the page renders:\n *\n * - `'my'` (default) calls `myOrganizations` (gateway @rbac(scopeType:\"self\")).\n * Use this in product-app shells (e.g. vibecontrols) where the user should\n * see only orgs they are a member of. Membership IS the access list, so no\n * client-side permission gate is needed.\n *\n * - `'admin'` calls the legacy `organizations` query (gateway @rbac(scopeType:\n * \"global\")). Restricted to actors with global `organization:read` (super\n * admins / service / custom global-admin roles). Use this in the admin app\n * where operators need the tenant-wide list. Once `allOrganizations` is\n * published through Hive Cloud + codegen, switch to `useAllOrganizations`.\n */\nexport interface OrganizationsListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function OrganizationsListPage({ mode = 'my' }: OrganizationsListPageProps = {}) {\n useOrganizationPageView('organizations.list');\n const tr = useTr();\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 CRUDL, while an explicit false value still gates create.\n const createDetails = useFeatureFlagDetails('organization.create');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [selectedOrganizationIds, setSelectedOrganizationIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // Show a count badge of pending invites alongside the Invitations link.\n // Skipped in 'admin' mode since admins land here via tenant-wide management\n // rather than personal invites.\n const { total: pendingInvitesCount } = useMyInvites(InviteStatus.Pending, 1, 0, mode === 'my');\n\n // Page size is large enough to cover realistic memberships / tenant-wide\n // org counts in one round-trip so the client-side search is exhaustive.\n // If a tenant ever exceeds this, wire in fetchMore/pagination instead of\n // silently truncating.\n const ORGANIZATIONS_PAGE_SIZE = 200;\n const myResult = useMyOrganizations(ORGANIZATIONS_PAGE_SIZE, 0, mode === 'my');\n const adminResult = useOrganizations(undefined, ORGANIZATIONS_PAGE_SIZE, 0, mode === 'admin');\n const {\n organizations: allOrganizations,\n loading,\n error,\n refetch,\n } = mode === 'admin' ? adminResult : myResult;\n\n const isSearchFiltered = debouncedSearchQuery.trim().length > 0;\n\n const organizations = useMemo(() => {\n const q = debouncedSearchQuery.trim().toLowerCase();\n if (!q) return allOrganizations;\n return allOrganizations.filter(\n (org) => org.name?.toLowerCase().includes(q) || org.slug?.toLowerCase().includes(q)\n );\n }, [allOrganizations, debouncedSearchQuery]);\n\n const { deleteOrganization, loading: deleteLoading } = useDeleteOrganization();\n\n useEffect(() => {\n setSelectedOrganizationIds((previousSelection) => {\n const visibleIds = new Set(organizations.map((organization) => organization.id));\n const nextSelection = new Set(\n [...previousSelection].filter((organizationId) => visibleIds.has(organizationId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [organizations]);\n\n const selectedOrganizations = useMemo(\n () => organizations.filter((organization) => selectedOrganizationIds.has(organization.id)),\n [organizations, selectedOrganizationIds]\n );\n\n const allVisibleSelected =\n organizations.length > 0 &&\n organizations.every((organization) => selectedOrganizationIds.has(organization.id));\n const partiallySelected =\n organizations.some((organization) => selectedOrganizationIds.has(organization.id)) &&\n !allVisibleSelected;\n\n const executeDelete = async (organizationIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n organizationIds.map(async (organizationId) => {\n const result = await deleteOrganization(organizationId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to delete organization');\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 setSelectedOrganizationIds(new Set());\n await refetch();\n const successMsg =\n organizationIds.length === 1\n ? tr('organizations.delete.successSingle', 'Organization deleted successfully')\n : `${organizationIds.length} ${tr('organizations.delete.successMany', 'organizations deleted successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (deleteError) {\n const errorMsg =\n deleteError instanceof Error\n ? deleteError.message\n : tr('organizations.delete.failed', 'Failed to delete organization');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (organizationId: string, organizationName: string) => {\n nativeImpact('medium');\n const title = tr('organizations.delete.title', 'Delete organization');\n const description = `${tr('organizations.delete.confirmPrefix', 'Delete')} ${organizationName}? ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('common.delete', 'Delete'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('common.delete', 'Delete'),\n variant: 'danger',\n onConfirm: () => {\n void executeDelete([organizationId]);\n },\n });\n } else if (nativeResult) {\n void executeDelete([organizationId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedOrganizations.length === 0) return;\n\n openConfirmDialog({\n title: tr('organizations.delete.bulkTitle', 'Delete selected organizations'),\n description: `${tr('organizations.delete.bulkPrefix', 'Delete')} ${selectedOrganizations.length} ${tr(\n selectedOrganizations.length === 1\n ? 'organizations.delete.bulkSuffixSingle'\n : 'organizations.delete.bulkSuffixMany',\n selectedOrganizations.length === 1 ? 'selected organization?' : 'selected organizations?'\n )} ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`,\n confirmText: `${tr('common.delete', 'Delete')} ${selectedOrganizations.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeDelete(selectedOrganizations.map((organization) => organization.id));\n },\n });\n };\n\n const toggleOrganizationSelection = (organizationId: string, checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(organizationId);\n } else {\n nextSelection.delete(organizationId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllOrganizations = (checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n organizations.forEach((organization) => {\n if (checked) {\n nextSelection.add(organization.id);\n } else {\n nextSelection.delete(organization.id);\n }\n });\n return nextSelection;\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 // No client-side list gate: this page calls `myOrganizations`\n // (@rbac(scopeType: \"self\")), so the data set IS the access list — if the\n // user is in zero orgs the page renders the empty state, not a deny. The\n // create/delete/edit buttons keep their own per-action `hasPermission`\n // checks below to hide actions the user can't perform on a given org.\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('organizations.list.loading', 'Loading organizations...')} />\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('organizations.list.title', 'Organizations')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.list.subtitle',\n 'Manage tenant organizations and the workspaces that belong to them.'\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n {mode === 'my' && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/invites`)}\n className=\"relative 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.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-[0.98]\"\n aria-label={tr('organizations.invitations.button', 'Invitations')}\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.invitations.button', 'Invitations')}\n </span>\n {pendingInvitesCount > 0 && (\n <span\n className=\"ml-1 inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-[var(--color-action-primary-bg)] px-1.5 py-0.5 text-xs font-semibold text-[var(--color-action-primary-text)]\"\n aria-label={`${pendingInvitesCount} pending`}\n >\n {pendingInvitesCount}\n </span>\n )}\n </button>\n )}\n\n {createEnabled && hasPermission(PERMISSIONS.ORG_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/new`)}\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 <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.create.button', 'Create Organization')}\n </span>\n </button>\n )}\n </div>\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('organizations.list.searchPlaceholder', 'Search organizations...')}\n label={tr('organizations.list.searchLabel', 'Search organizations')}\n inputId=\"organizations-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('organizations.list.refresh', 'Refresh organizations')}\n title={tr('organizations.list.refresh', 'Refresh organizations')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {organizations.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={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedOrganizationIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedOrganizationIds(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 {selectedOrganizationIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedOrganizations.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.deleteSelected', 'Delete Selected')}</span>\n {selectedOrganizations.length > 0 ? (\n <span>({selectedOrganizations.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {organizations.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 {isSearchFiltered && allOrganizations.length > 0 ? (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.noMatchesTitle', 'No matching organizations')}\n description={tr(\n 'organizations.list.noMatchesDescription',\n 'No organizations match your search. Try a different name or slug, or clear the search.'\n )}\n action={{\n label: tr('common.clearSearch', 'Clear search'),\n onClick: () => setSearchQuery(''),\n }}\n />\n ) : (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.emptyTitle', 'No organizations found')}\n description={tr(\n 'organizations.list.emptyDescription',\n 'Create your first organization to start managing workspaces, members, and invites.'\n )}\n action={\n createEnabled && hasPermission(PERMISSIONS.ORG_CREATE)\n ? {\n label: tr('organizations.create.button', 'Create Organization'),\n onClick: () => navigate(`${basePath}/new`),\n }\n : undefined\n }\n />\n )}\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {organizations.map((organization) => (\n <article\n key={organization.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={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) => toggleOrganizationSelection(organization.id, checked)}\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-12 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-base font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {organization.name}\n </h2>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n <dl className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\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={() => navigate(`${basePath}/${organization.id}`)}\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.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\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 <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\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('common.delete', 'Delete')}\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={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\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('organizations.list.column.organization', 'Organization')}\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('common.status', 'Status')}\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('organizations.list.column.workspaces', 'Workspaces')}\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('common.created', 'Created')}\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 {organizations.map((organization) => (\n <tr\n key={organization.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={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) =>\n toggleOrganizationSelection(organization.id, checked)\n }\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n <div className=\"min-w-0\">\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <OrganizationStatusBadge status={organization.status} />\n </td>\n <td className=\"px-6 py-4 align-top text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(organization.createdAt)}\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={() => navigate(`${basePath}/${organization.id}`)}\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 title={`${tr('common.view', 'View')} ${organization.name}`}\n aria-label={`${tr('common.view', 'View')} ${organization.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\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 title={`${tr('common.edit', 'Edit')} ${organization.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${organization.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\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 title={`${tr('common.delete', 'Delete')} ${organization.name}`}\n aria-label={`${tr('common.delete', 'Delete')} ${organization.name}`}\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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"}
1
+ {"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building2, Eye, Mail, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n useDeleteOrganization,\n useMyOrganizations,\n useOrganizations,\n} from '../hooks/useOrganizations';\nimport { useMyInvites } from '../../members/hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\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';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls which scope of organizations the page renders:\n *\n * - `'my'` (default) calls `myOrganizations` (gateway @rbac(scopeType:\"self\")).\n * Use this in product-app shells (e.g. vibecontrols) where the user should\n * see only orgs they are a member of. Membership IS the access list, so no\n * client-side permission gate is needed.\n *\n * - `'admin'` calls `allOrganizations` (gateway @rbac(scopeType: \"global\")).\n * Restricted to actors with global `organization:read` (super admins /\n * service / custom global-admin roles). Use this in the admin app where\n * operators need the tenant-wide list.\n */\nexport interface OrganizationsListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function OrganizationsListPage({ mode = 'my' }: OrganizationsListPageProps = {}) {\n useOrganizationPageView('organizations.list');\n const tr = useTr();\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 CRUDL, while an explicit false value still gates create.\n const createDetails = useFeatureFlagDetails('organization.create');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [selectedOrganizationIds, setSelectedOrganizationIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // Show a count badge of pending invites alongside the Invitations link.\n // Skipped in 'admin' mode since admins land here via tenant-wide management\n // rather than personal invites.\n const { total: pendingInvitesCount } = useMyInvites(InviteStatus.Pending, 1, 0, mode === 'my');\n\n // Page size is large enough to cover realistic memberships / tenant-wide\n // org counts in one round-trip so the client-side search is exhaustive.\n // If a tenant ever exceeds this, wire in fetchMore/pagination instead of\n // silently truncating.\n const ORGANIZATIONS_PAGE_SIZE = 200;\n const myResult = useMyOrganizations(ORGANIZATIONS_PAGE_SIZE, 0, mode === 'my');\n const adminResult = useOrganizations(undefined, ORGANIZATIONS_PAGE_SIZE, 0, mode === 'admin');\n const {\n organizations: allOrganizations,\n loading,\n error,\n refetch,\n } = mode === 'admin' ? adminResult : myResult;\n\n const isSearchFiltered = debouncedSearchQuery.trim().length > 0;\n\n const organizations = useMemo(() => {\n const q = debouncedSearchQuery.trim().toLowerCase();\n if (!q) return allOrganizations;\n return allOrganizations.filter(\n (org) => org.name?.toLowerCase().includes(q) || org.slug?.toLowerCase().includes(q)\n );\n }, [allOrganizations, debouncedSearchQuery]);\n\n const { deleteOrganization, loading: deleteLoading } = useDeleteOrganization();\n\n useEffect(() => {\n setSelectedOrganizationIds((previousSelection) => {\n const visibleIds = new Set(organizations.map((organization) => organization.id));\n const nextSelection = new Set(\n [...previousSelection].filter((organizationId) => visibleIds.has(organizationId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [organizations]);\n\n const selectedOrganizations = useMemo(\n () => organizations.filter((organization) => selectedOrganizationIds.has(organization.id)),\n [organizations, selectedOrganizationIds]\n );\n\n const allVisibleSelected =\n organizations.length > 0 &&\n organizations.every((organization) => selectedOrganizationIds.has(organization.id));\n const partiallySelected =\n organizations.some((organization) => selectedOrganizationIds.has(organization.id)) &&\n !allVisibleSelected;\n\n const executeDelete = async (organizationIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n organizationIds.map(async (organizationId) => {\n const result = await deleteOrganization(organizationId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to delete organization');\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 setSelectedOrganizationIds(new Set());\n await refetch();\n const successMsg =\n organizationIds.length === 1\n ? tr('organizations.delete.successSingle', 'Organization deleted successfully')\n : `${organizationIds.length} ${tr('organizations.delete.successMany', 'organizations deleted successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (deleteError) {\n const errorMsg =\n deleteError instanceof Error\n ? deleteError.message\n : tr('organizations.delete.failed', 'Failed to delete organization');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (organizationId: string, organizationName: string) => {\n nativeImpact('medium');\n const title = tr('organizations.delete.title', 'Delete organization');\n const description = `${tr('organizations.delete.confirmPrefix', 'Delete')} ${organizationName}? ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('common.delete', 'Delete'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('common.delete', 'Delete'),\n variant: 'danger',\n onConfirm: () => {\n void executeDelete([organizationId]);\n },\n });\n } else if (nativeResult) {\n void executeDelete([organizationId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedOrganizations.length === 0) return;\n\n openConfirmDialog({\n title: tr('organizations.delete.bulkTitle', 'Delete selected organizations'),\n description: `${tr('organizations.delete.bulkPrefix', 'Delete')} ${selectedOrganizations.length} ${tr(\n selectedOrganizations.length === 1\n ? 'organizations.delete.bulkSuffixSingle'\n : 'organizations.delete.bulkSuffixMany',\n selectedOrganizations.length === 1 ? 'selected organization?' : 'selected organizations?'\n )} ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`,\n confirmText: `${tr('common.delete', 'Delete')} ${selectedOrganizations.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeDelete(selectedOrganizations.map((organization) => organization.id));\n },\n });\n };\n\n const toggleOrganizationSelection = (organizationId: string, checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(organizationId);\n } else {\n nextSelection.delete(organizationId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllOrganizations = (checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n organizations.forEach((organization) => {\n if (checked) {\n nextSelection.add(organization.id);\n } else {\n nextSelection.delete(organization.id);\n }\n });\n return nextSelection;\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 // No client-side list gate: this page calls `myOrganizations`\n // (@rbac(scopeType: \"self\")), so the data set IS the access list — if the\n // user is in zero orgs the page renders the empty state, not a deny. The\n // create/delete/edit buttons keep their own per-action `hasPermission`\n // checks below to hide actions the user can't perform on a given org.\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('organizations.list.loading', 'Loading organizations...')} />\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('organizations.list.title', 'Organizations')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.list.subtitle',\n 'Manage tenant organizations and the workspaces that belong to them.'\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n {mode === 'my' && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/invites`)}\n className=\"relative 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.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-[0.98]\"\n aria-label={tr('organizations.invitations.button', 'Invitations')}\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.invitations.button', 'Invitations')}\n </span>\n {pendingInvitesCount > 0 && (\n <span\n className=\"ml-1 inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-[var(--color-action-primary-bg)] px-1.5 py-0.5 text-xs font-semibold text-[var(--color-action-primary-text)]\"\n aria-label={`${pendingInvitesCount} pending`}\n >\n {pendingInvitesCount}\n </span>\n )}\n </button>\n )}\n\n {createEnabled && hasPermission(PERMISSIONS.ORG_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/new`)}\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 <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.create.button', 'Create Organization')}\n </span>\n </button>\n )}\n </div>\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('organizations.list.searchPlaceholder', 'Search organizations...')}\n label={tr('organizations.list.searchLabel', 'Search organizations')}\n inputId=\"organizations-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('organizations.list.refresh', 'Refresh organizations')}\n title={tr('organizations.list.refresh', 'Refresh organizations')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {organizations.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={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedOrganizationIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedOrganizationIds(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 {selectedOrganizationIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedOrganizations.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.deleteSelected', 'Delete Selected')}</span>\n {selectedOrganizations.length > 0 ? (\n <span>({selectedOrganizations.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {organizations.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 {isSearchFiltered && allOrganizations.length > 0 ? (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.noMatchesTitle', 'No matching organizations')}\n description={tr(\n 'organizations.list.noMatchesDescription',\n 'No organizations match your search. Try a different name or slug, or clear the search.'\n )}\n action={{\n label: tr('common.clearSearch', 'Clear search'),\n onClick: () => setSearchQuery(''),\n }}\n />\n ) : (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.emptyTitle', 'No organizations found')}\n description={tr(\n 'organizations.list.emptyDescription',\n 'Create your first organization to start managing workspaces, members, and invites.'\n )}\n action={\n createEnabled && hasPermission(PERMISSIONS.ORG_CREATE)\n ? {\n label: tr('organizations.create.button', 'Create Organization'),\n onClick: () => navigate(`${basePath}/new`),\n }\n : undefined\n }\n />\n )}\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {organizations.map((organization) => (\n <article\n key={organization.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={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) => toggleOrganizationSelection(organization.id, checked)}\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-12 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-base font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {organization.name}\n </h2>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n <dl className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\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={() => navigate(`${basePath}/${organization.id}`)}\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.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\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 <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\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('common.delete', 'Delete')}\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={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\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('organizations.list.column.organization', 'Organization')}\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('common.status', 'Status')}\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('organizations.list.column.workspaces', 'Workspaces')}\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('common.created', 'Created')}\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 {organizations.map((organization) => (\n <tr\n key={organization.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={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) =>\n toggleOrganizationSelection(organization.id, checked)\n }\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n <div className=\"min-w-0\">\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <OrganizationStatusBadge status={organization.status} />\n </td>\n <td className=\"px-6 py-4 align-top text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(organization.createdAt)}\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={() => navigate(`${basePath}/${organization.id}`)}\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 title={`${tr('common.view', 'View')} ${organization.name}`}\n aria-label={`${tr('common.view', 'View')} ${organization.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\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 title={`${tr('common.edit', 'Edit')} ${organization.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${organization.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\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 title={`${tr('common.delete', 'Delete')} ${organization.name}`}\n aria-label={`${tr('common.delete', 'Delete')} ${organization.name}`}\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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"}