@burdenoff/microfe-organization 2026.624.2 → 2026.625.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.
- package/dist/organization-management/modules/members/components/BulkInviteForm.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js +58 -52
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js +41 -39
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js +70 -68
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js +232 -203
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js +38 -37
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +134 -132
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +38 -36
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +163 -166
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js +48 -46
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js +257 -229
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +43 -50
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +39 -37
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +146 -145
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js +46 -44
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +115 -72
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Eye, Mail, RefreshCw, Shield, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n ActorIdentity,\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n} from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter((member) => {\n const profile = profileMap[member.userId];\n if (profile?.displayName.toLowerCase().includes(normalizedQuery)) return true;\n if (profile?.email?.toLowerCase().includes(normalizedQuery)) return true;\n return (\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n });\n }, [debouncedSearchQuery, members, profileMap]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-200\">\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\"\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)]\"\n aria-label={tr('members.list.refresh', 'Refresh members')}\n title={tr('members.list.refresh', 'Refresh members')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {filteredMembers.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedMemberIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedMemberIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedMemberIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={handleBulkRemove}\n disabled={selectedMembers.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.removeSelected', 'Remove Selected')}</span>\n {selectedMembers.length > 0 ? <span>({selectedMembers.length})</span> : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {filteredMembers.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Shield size={40} aria-hidden=\"true\" />}\n title={tr('members.list.emptyTitle', 'No members found')}\n description={tr(\n 'members.list.emptyDescription',\n 'Invite someone to this organization to grant them access and assign their role.'\n )}\n action={\n inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? {\n label: tr('members.invite.button', 'Invite Member'),\n onClick: () => navigate(`${basePath}/${organizationId}/members/invite`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"break-all text-base font-semibold text-[var(--color-text-primary)]\">\n {profileMap[member.userId]?.displayName ??\n `User ${member.userId.slice(0, 8)}…`}\n </h2>\n <MemberRoleBadge role={member.role} />\n </div>\n {profileMap[member.userId]?.email ? (\n <p className=\"mt-0.5 break-all text-xs text-[var(--color-text-secondary)]\">\n {profileMap[member.userId]?.email}\n </p>\n ) : null}\n <details className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.list.showIds', 'Show IDs')}\n </summary>\n <p className=\"mt-1 break-all font-mono\">\n {tr('members.detail.userId', 'User ID')}: {member.userId}\n </p>\n <p className=\"mt-0.5 break-all font-mono\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </details>\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-primary)]\">\n {formatDate(member.joinedAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('members.remove.confirm', 'Remove')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.user', 'User')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"cell\"\n />\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
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 { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n ActorIdentity,\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n} from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter((member) => {\n const profile = profileMap[member.userId];\n if (profile?.displayName.toLowerCase().includes(normalizedQuery)) return true;\n if (profile?.email?.toLowerCase().includes(normalizedQuery)) return true;\n return (\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n });\n }, [debouncedSearchQuery, members, profileMap]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-200\">\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\"\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 {members.length > 0 ? (\n <div className=\"grid grid-cols-2 gap-4\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">{tr('members.list.title', 'Members')}</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {members.length}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('members.list.column.role', 'Role')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {new Set(members.map((m) => m.role)).size}\n </p>\n </GlassCard>\n </div>\n ) : null}\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-4\">\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)]\"\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 </GlassCard>\n\n {filteredMembers.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6\">\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 </GlassCard>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <GlassCard key={member.id} treatment=\"insight\" className=\"border-border-seam p-4\">\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 </GlassCard>\n ))}\n </div>\n\n <GlassCard\n treatment=\"plain\"\n className=\"hidden overflow-hidden border-border-seam p-0 md:block\"\n >\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-seam)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <ActorIdentity\n profile={\n profileMap[member.userId] ??\n buildActorFallback({\n actorId: member.userId,\n actorType: 'user',\n })\n }\n variant=\"cell\"\n />\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </GlassCard>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,13 +4,13 @@ import { InviteStatus as i } from "../../../../generated/global-types.js";
|
|
|
4
4
|
import { useTr as a } from "../../../shared/hooks/useTr.js";
|
|
5
5
|
import { LoadingState as o } from "../../../shared/components/feedback/LoadingState.js";
|
|
6
6
|
import { useOrganizationPageView as s } from "../../../hooks/useOrganizationPageView.js";
|
|
7
|
-
import { useAcceptWorkspaceInvite as ee, useMyWorkspaceInvites as
|
|
8
|
-
import { MyInviteCard as
|
|
9
|
-
import { getProjectDisplayNames as
|
|
10
|
-
import { useMemo as
|
|
11
|
-
import { AlertCircle as
|
|
12
|
-
import { Badge as v } from "@burdenoff/fe-libs/ui";
|
|
13
|
-
import { usePermissions as
|
|
7
|
+
import { useAcceptWorkspaceInvite as ee, useMyWorkspaceInvites as c, useRejectWorkspaceInvite as te } from "../hooks/useWorkspaceInvites.js";
|
|
8
|
+
import { MyInviteCard as ne } from "../components/MyInviteCard.js";
|
|
9
|
+
import { getProjectDisplayNames as l, getRoleDisplayNames as u, getWorkspaceDisplayName as re } from "../utils/workspaceInvitationDisplay.js";
|
|
10
|
+
import { useMemo as d, useState as f } from "react";
|
|
11
|
+
import { AlertCircle as p, BriefcaseBusiness as m, Building2 as h, Loader2 as g, Mail as _ } from "lucide-react";
|
|
12
|
+
import { Badge as v, GlassCard as ie } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { usePermissions as ae } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
14
14
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
15
15
|
import { NavLink as x } from "react-router-dom";
|
|
16
16
|
import { ActorIdentity as S, useActorProfiles as C } from "@burdenoff/fe-libs/shared";
|
|
@@ -47,36 +47,36 @@ function E({ activeTab: e, basePath: t, tr: n }) {
|
|
|
47
47
|
}
|
|
48
48
|
function D({ activeTab: x = "workspace" }) {
|
|
49
49
|
s(x === "workspace" ? "organizations.my-workspace-invites" : "organizations.my-organization-invites");
|
|
50
|
-
let T = a(), { isLoading: D } =
|
|
50
|
+
let T = a(), { isLoading: D } = ae(), { basePath: O = "/organizations", currentUser: k, authToken: A, apiGatewayUrl: oe } = e(), j = x === "organization", M = x === "workspace", { invites: N, loading: P, error: F, total: I } = n(i.Pending, 20, 0, j), { invites: L, loading: R, error: z, total: B, refetch: V } = c(k?.email, M), { acceptInvite: H, loading: U } = t(), { rejectInvite: W, loading: G } = r(), { acceptInvite: K, loading: se } = ee(), { rejectInvite: ce, loading: le } = te(), [q, J] = f(null), Y = q?.tab === x ? q.message : null, [X, Z] = f(null), ue = C(d(() => L.flatMap((e) => e.invitedById ? [{
|
|
51
51
|
actorId: e.invitedById,
|
|
52
52
|
actorType: "user"
|
|
53
|
-
}] : []), [
|
|
53
|
+
}] : []), [L]), {
|
|
54
54
|
apiGatewayUrl: oe,
|
|
55
|
-
authToken:
|
|
56
|
-
}), Q =
|
|
55
|
+
authToken: A
|
|
56
|
+
}), Q = d(() => [...L].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()), [L]), $ = x === "workspace" ? B : I, de = async (e) => {
|
|
57
57
|
try {
|
|
58
|
-
J(null), await
|
|
58
|
+
J(null), await H(e);
|
|
59
59
|
} catch (e) {
|
|
60
60
|
J({
|
|
61
61
|
tab: "organization",
|
|
62
62
|
message: e instanceof Error ? e.message : T("members.myInvites.acceptFailed", "Failed to accept invite")
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
},
|
|
65
|
+
}, fe = async (e) => {
|
|
66
66
|
try {
|
|
67
|
-
J(null), await
|
|
67
|
+
J(null), await W(e);
|
|
68
68
|
} catch (e) {
|
|
69
69
|
J({
|
|
70
70
|
tab: "organization",
|
|
71
71
|
message: e instanceof Error ? e.message : T("members.myInvites.rejectFailed", "Failed to reject invite")
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
},
|
|
74
|
+
}, pe = async (e) => {
|
|
75
75
|
try {
|
|
76
76
|
Z({
|
|
77
77
|
inviteId: e,
|
|
78
78
|
action: "accept"
|
|
79
|
-
}), J(null), await
|
|
79
|
+
}), J(null), await K(e), await V().catch(() => void 0);
|
|
80
80
|
} catch (e) {
|
|
81
81
|
J({
|
|
82
82
|
tab: "workspace",
|
|
@@ -85,12 +85,12 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
85
85
|
} finally {
|
|
86
86
|
Z((t) => t?.inviteId === e && t.action === "accept" ? null : t);
|
|
87
87
|
}
|
|
88
|
-
},
|
|
88
|
+
}, me = async (e) => {
|
|
89
89
|
try {
|
|
90
90
|
Z({
|
|
91
91
|
inviteId: e,
|
|
92
92
|
action: "reject"
|
|
93
|
-
}), J(null), await
|
|
93
|
+
}), J(null), await ce(e), await V().catch(() => void 0);
|
|
94
94
|
} catch (e) {
|
|
95
95
|
J({
|
|
96
96
|
tab: "workspace",
|
|
@@ -103,7 +103,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
103
103
|
return D ? /* @__PURE__ */ y("div", {
|
|
104
104
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
105
105
|
children: /* @__PURE__ */ y(o, { message: T("common.loadingPermissions", "Loading permissions...") })
|
|
106
|
-
}) :
|
|
106
|
+
}) : M && !k?.email ? /* @__PURE__ */ y("div", {
|
|
107
107
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
108
108
|
children: /* @__PURE__ */ b("div", {
|
|
109
109
|
className: "max-w-4xl mx-auto",
|
|
@@ -132,7 +132,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
132
132
|
})
|
|
133
133
|
]
|
|
134
134
|
})
|
|
135
|
-
}) :
|
|
135
|
+
}) : j && P && N.length === 0 || M && R && L.length === 0 ? /* @__PURE__ */ y("div", {
|
|
136
136
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
137
137
|
children: /* @__PURE__ */ b("div", {
|
|
138
138
|
className: "max-w-4xl mx-auto",
|
|
@@ -161,7 +161,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
161
161
|
})
|
|
162
162
|
]
|
|
163
163
|
})
|
|
164
|
-
}) :
|
|
164
|
+
}) : j && F ? /* @__PURE__ */ y("div", {
|
|
165
165
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
166
166
|
children: /* @__PURE__ */ b("div", {
|
|
167
167
|
className: "max-w-4xl mx-auto",
|
|
@@ -188,13 +188,13 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
188
188
|
children: [
|
|
189
189
|
T("members.myInvites.loadFailed", "Failed to load invites"),
|
|
190
190
|
": ",
|
|
191
|
-
|
|
191
|
+
F.message
|
|
192
192
|
]
|
|
193
193
|
})
|
|
194
194
|
})
|
|
195
195
|
]
|
|
196
196
|
})
|
|
197
|
-
}) :
|
|
197
|
+
}) : M && z ? /* @__PURE__ */ y("div", {
|
|
198
198
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
199
199
|
children: /* @__PURE__ */ b("div", {
|
|
200
200
|
className: "max-w-4xl mx-auto",
|
|
@@ -222,7 +222,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
222
222
|
T("members.myInvites.workspaceLoadFailed", "Failed to load workspace invites"),
|
|
223
223
|
":",
|
|
224
224
|
" ",
|
|
225
|
-
|
|
225
|
+
z.message
|
|
226
226
|
]
|
|
227
227
|
})
|
|
228
228
|
})
|
|
@@ -257,7 +257,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
257
257
|
}),
|
|
258
258
|
Y && /* @__PURE__ */ b("div", {
|
|
259
259
|
className: "mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3",
|
|
260
|
-
children: [/* @__PURE__ */ y(
|
|
260
|
+
children: [/* @__PURE__ */ y(p, {
|
|
261
261
|
size: 16,
|
|
262
262
|
className: "shrink-0 text-[var(--color-status-error-text)]"
|
|
263
263
|
}), /* @__PURE__ */ y("p", {
|
|
@@ -265,7 +265,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
265
265
|
children: Y
|
|
266
266
|
})]
|
|
267
267
|
}),
|
|
268
|
-
|
|
268
|
+
M ? Q.length === 0 ? /* @__PURE__ */ b("div", {
|
|
269
269
|
className: "text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]",
|
|
270
270
|
children: [
|
|
271
271
|
/* @__PURE__ */ y(m, {
|
|
@@ -284,9 +284,10 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
284
284
|
}) : /* @__PURE__ */ y("div", {
|
|
285
285
|
className: "space-y-3",
|
|
286
286
|
children: Q.map((e) => {
|
|
287
|
-
let t =
|
|
288
|
-
return /* @__PURE__ */ y(
|
|
289
|
-
|
|
287
|
+
let t = re(e), n = u(e), r = l(e), i = e.invitedById ? ue[e.invitedById] : void 0, a = i?.displayName || i?.email || T("members.myInvites.invitedByFallback", "Someone"), o = X?.inviteId === e.id && X?.action === "accept" && se, s = X?.inviteId === e.id && X?.action === "reject" && le;
|
|
288
|
+
return /* @__PURE__ */ y(ie, {
|
|
289
|
+
treatment: "insight",
|
|
290
|
+
className: "border-border-seam p-5",
|
|
290
291
|
children: /* @__PURE__ */ b("div", {
|
|
291
292
|
className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between",
|
|
292
293
|
children: [/* @__PURE__ */ b("div", {
|
|
@@ -363,7 +364,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
363
364
|
className: "flex flex-wrap items-center gap-2 shrink-0",
|
|
364
365
|
children: [/* @__PURE__ */ b("button", {
|
|
365
366
|
type: "button",
|
|
366
|
-
onClick: () => void
|
|
367
|
+
onClick: () => void pe(e.id),
|
|
367
368
|
disabled: o || s,
|
|
368
369
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-status-success-bg)] px-4 py-2 text-sm font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50",
|
|
369
370
|
children: [o ? /* @__PURE__ */ y(g, {
|
|
@@ -372,7 +373,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
372
373
|
}) : null, T("members.myInvites.accept", "Accept")]
|
|
373
374
|
}), /* @__PURE__ */ b("button", {
|
|
374
375
|
type: "button",
|
|
375
|
-
onClick: () => void
|
|
376
|
+
onClick: () => void me(e.id),
|
|
376
377
|
disabled: o || s,
|
|
377
378
|
className: "inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-border-default)] bg-transparent px-4 py-2 text-sm font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:border-[var(--color-status-error-border)] hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)] disabled:cursor-not-allowed disabled:opacity-50",
|
|
378
379
|
children: [s ? /* @__PURE__ */ y(g, {
|
|
@@ -384,7 +385,7 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
384
385
|
})
|
|
385
386
|
}, e.id);
|
|
386
387
|
})
|
|
387
|
-
}) :
|
|
388
|
+
}) : N.length === 0 ? /* @__PURE__ */ b("div", {
|
|
388
389
|
className: "text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]",
|
|
389
390
|
children: [
|
|
390
391
|
/* @__PURE__ */ y(h, {
|
|
@@ -402,12 +403,12 @@ function D({ activeTab: x = "workspace" }) {
|
|
|
402
403
|
]
|
|
403
404
|
}) : /* @__PURE__ */ y("div", {
|
|
404
405
|
className: "space-y-3",
|
|
405
|
-
children:
|
|
406
|
+
children: N.map((e) => /* @__PURE__ */ y(ne, {
|
|
406
407
|
invite: e,
|
|
407
|
-
onAccept:
|
|
408
|
-
onReject:
|
|
409
|
-
accepting:
|
|
410
|
-
rejecting:
|
|
408
|
+
onAccept: de,
|
|
409
|
+
onReject: fe,
|
|
410
|
+
accepting: U,
|
|
411
|
+
rejecting: G
|
|
411
412
|
}, e.id))
|
|
412
413
|
})
|
|
413
414
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyInvitesPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MyInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { NavLink } from 'react-router-dom';\nimport { Mail, Loader2, AlertCircle, BriefcaseBusiness, Building2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { useMyInvites, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport {\n useAcceptWorkspaceInvite,\n useMyWorkspaceInvites,\n useRejectWorkspaceInvite,\n} from '../hooks/useWorkspaceInvites';\nimport { MyInviteCard } from '../components/MyInviteCard';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { InviteStatus as OrganizationInviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/workspaceInvitationDisplay';\n\n/**\n * My Invites Page\n * Shows pending invites for the current user with accept/reject actions\n */\ntype MyInvitesTab = 'workspace' | 'organization';\n\ninterface MyInvitesPageProps {\n activeTab?: MyInvitesTab;\n}\n\ninterface TabActionError {\n tab: MyInvitesTab;\n message: string;\n}\n\ninterface InvitesTabNavProps {\n activeTab: MyInvitesTab;\n basePath: string;\n tr: ReturnType<typeof useTr>;\n}\n\nfunction formatDate(value?: string | null) {\n if (!value) return null;\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction joinPath(base: string, path: string): string {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n}\n\nfunction InvitesTabNav({ activeTab, basePath, tr }: InvitesTabNavProps) {\n const getTabClassName = (isActive: boolean) =>\n [\n 'border-b-2 px-4 py-2 text-sm font-medium transition-colors',\n isActive\n ? 'border-[var(--color-border-focus)] text-[var(--color-text-primary)]'\n : 'border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]',\n ].join(' ');\n\n return (\n <div className=\"mb-6 flex gap-2 border-b border-[var(--color-border-default)]\">\n <NavLink\n to={joinPath(basePath, '/invites')}\n end\n className={getTabClassName(activeTab === 'workspace')}\n >\n {tr('members.myInvites.workspaceTab', 'Workspace Invites')}\n </NavLink>\n <NavLink\n to={joinPath(basePath, '/invites/organizations')}\n className={getTabClassName(activeTab === 'organization')}\n >\n {tr('members.myInvites.organizationTab', 'Organization Invites')}\n </NavLink>\n </div>\n );\n}\n\nexport function MyInvitesPage({ activeTab = 'workspace' }: MyInvitesPageProps) {\n useOrganizationPageView(\n activeTab === 'workspace'\n ? 'organizations.my-workspace-invites'\n : 'organizations.my-organization-invites'\n );\n const tr = useTr();\n const { isLoading: permissionsLoading } = usePermissions();\n const { basePath = '/organizations', currentUser, authToken, apiGatewayUrl } = useOrgConsole();\n const orgInvitesEnabled = activeTab === 'organization';\n const workspaceInvitesEnabled = activeTab === 'workspace';\n const { invites, loading, error, total } = useMyInvites(\n OrganizationInviteStatus.Pending,\n 20,\n 0,\n orgInvitesEnabled\n );\n const {\n invites: workspaceInvites,\n loading: workspaceLoading,\n error: workspaceError,\n total: workspaceTotal,\n refetch: refetchWorkspaceInvites,\n } = useMyWorkspaceInvites(currentUser?.email, workspaceInvitesEnabled);\n const { acceptInvite, loading: accepting } = useAcceptInvite();\n const { rejectInvite, loading: rejecting } = useRejectInvite();\n const { acceptInvite: acceptWorkspaceInvite, loading: acceptingWorkspace } =\n useAcceptWorkspaceInvite();\n const { rejectInvite: rejectWorkspaceInvite, loading: rejectingWorkspace } =\n useRejectWorkspaceInvite();\n const [actionError, setActionError] = useState<TabActionError | null>(null);\n\n const visibleActionError = actionError?.tab === activeTab ? actionError.message : null;\n const [processingWorkspaceAction, setProcessingWorkspaceAction] = useState<{\n inviteId: string;\n action: 'accept' | 'reject';\n } | null>(null);\n\n const workspaceInviteActors = useMemo<ActorInput[]>(\n () =>\n workspaceInvites.flatMap((invite) =>\n invite.invitedById ? [{ actorId: invite.invitedById, actorType: 'user' as const }] : []\n ),\n [workspaceInvites]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(workspaceInviteActors, {\n apiGatewayUrl,\n authToken,\n });\n\n const sortedWorkspaceInvites = useMemo(\n () =>\n [...workspaceInvites].sort(\n (left: (typeof workspaceInvites)[number], right: (typeof workspaceInvites)[number]) =>\n new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [workspaceInvites]\n );\n\n const totalCount = activeTab === 'workspace' ? workspaceTotal : total;\n\n const handleAccept = async (inviteId: string) => {\n try {\n setActionError(null);\n await acceptInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.acceptFailed', 'Failed to accept invite'),\n });\n }\n };\n\n const handleReject = async (inviteId: string) => {\n try {\n setActionError(null);\n await rejectInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.rejectFailed', 'Failed to reject invite'),\n });\n }\n };\n\n const handleAcceptWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'accept' });\n setActionError(null);\n await acceptWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceAcceptFailed', 'Failed to accept workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'accept' ? null : current\n );\n }\n };\n\n const handleRejectWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'reject' });\n setActionError(null);\n await rejectWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceRejectFailed', 'Failed to reject workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'reject' ? null : current\n );\n }\n };\n\n // Own invites are always accessible — no permission denial needed.\n // We still wait for permissions to load to ensure consistent UX.\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 (workspaceInvitesEnabled && !currentUser?.email) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 text-center\">\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr('members.myInvites.signInWorkspace', 'Sign in to view your workspace invites.')}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && loading && invites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceLoading && workspaceInvites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.workspaceLoadFailed', 'Failed to load workspace invites')}:{' '}\n {workspaceError.message}\n </p>\n </div>\n </div>\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\">\n <div className=\"max-w-4xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n {totalCount > 0 && (\n <span className=\"px-2.5 py-0.5 rounded-full text-xs font-medium bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n {visibleActionError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertCircle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{visibleActionError}</p>\n </div>\n )}\n\n {workspaceInvitesEnabled ? (\n sortedWorkspaceInvites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <BriefcaseBusiness\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.workspaceEmptyTitle', 'No pending workspace invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.workspaceEmptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {sortedWorkspaceInvites.map((invite) => {\n const workspaceName = getWorkspaceDisplayName(invite);\n const roleNames = getRoleDisplayNames(invite);\n const projectNames = getProjectDisplayNames(invite);\n const inviterProfile = invite.invitedById\n ? inviterProfiles[invite.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n tr('members.myInvites.invitedByFallback', 'Someone');\n const isProcessingWorkspaceAccept =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'accept' &&\n acceptingWorkspace;\n const isProcessingWorkspaceReject =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'reject' &&\n rejectingWorkspace;\n\n return (\n <div\n key={invite.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5\"\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-[var(--color-bg-subtle)] p-2\">\n <BriefcaseBusiness\n size={20}\n className=\"text-[var(--color-text-secondary)]\"\n />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] break-words\">\n {workspaceName}\n </h3>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)] break-all\">\n {invite.email}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\">{invite.status}</Badge>\n {roleNames.map((roleName) => (\n <Badge key={`${invite.id}-${roleName}`} variant=\"outline\">\n {roleName}\n </Badge>\n ))}\n </div>\n\n <dl className=\"grid gap-3 text-sm text-[var(--color-text-secondary)] sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n inviterLabel\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{formatDate(invite.createdAt) ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{formatDate(invite.expiresAt) ?? '—'}</dd>\n </div>\n {projectNames.length > 0 ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.projectsLabel', 'Projects')}\n </dt>\n <dd className=\"mt-1 flex flex-wrap gap-2\">\n {projectNames.map((projectName) => (\n <Badge key={`${invite.id}-${projectName}`} variant=\"outline\">\n {projectName}\n </Badge>\n ))}\n </dd>\n </div>\n ) : null}\n </dl>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => void handleAcceptWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-status-success-bg)] px-4 py-2 text-sm font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceAccept ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleRejectWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-border-default)] bg-transparent px-4 py-2 text-sm font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:border-[var(--color-status-error-border)] hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceReject ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )\n ) : invites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <Building2\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {invites.map((invite) => (\n <MyInviteCard\n key={invite.id}\n invite={invite}\n onAccept={handleAccept}\n onReject={handleReject}\n accepting={accepting}\n rejecting={rejecting}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MyInvitesPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MyInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { NavLink } from 'react-router-dom';\nimport { Mail, Loader2, AlertCircle, BriefcaseBusiness, Building2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { useMyInvites, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport {\n useAcceptWorkspaceInvite,\n useMyWorkspaceInvites,\n useRejectWorkspaceInvite,\n} from '../hooks/useWorkspaceInvites';\nimport { MyInviteCard } from '../components/MyInviteCard';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { InviteStatus as OrganizationInviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/workspaceInvitationDisplay';\n\n/**\n * My Invites Page\n * Shows pending invites for the current user with accept/reject actions\n */\ntype MyInvitesTab = 'workspace' | 'organization';\n\ninterface MyInvitesPageProps {\n activeTab?: MyInvitesTab;\n}\n\ninterface TabActionError {\n tab: MyInvitesTab;\n message: string;\n}\n\ninterface InvitesTabNavProps {\n activeTab: MyInvitesTab;\n basePath: string;\n tr: ReturnType<typeof useTr>;\n}\n\nfunction formatDate(value?: string | null) {\n if (!value) return null;\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction joinPath(base: string, path: string): string {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n}\n\nfunction InvitesTabNav({ activeTab, basePath, tr }: InvitesTabNavProps) {\n const getTabClassName = (isActive: boolean) =>\n [\n 'border-b-2 px-4 py-2 text-sm font-medium transition-colors',\n isActive\n ? 'border-[var(--color-border-focus)] text-[var(--color-text-primary)]'\n : 'border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]',\n ].join(' ');\n\n return (\n <div className=\"mb-6 flex gap-2 border-b border-[var(--color-border-default)]\">\n <NavLink\n to={joinPath(basePath, '/invites')}\n end\n className={getTabClassName(activeTab === 'workspace')}\n >\n {tr('members.myInvites.workspaceTab', 'Workspace Invites')}\n </NavLink>\n <NavLink\n to={joinPath(basePath, '/invites/organizations')}\n className={getTabClassName(activeTab === 'organization')}\n >\n {tr('members.myInvites.organizationTab', 'Organization Invites')}\n </NavLink>\n </div>\n );\n}\n\nexport function MyInvitesPage({ activeTab = 'workspace' }: MyInvitesPageProps) {\n useOrganizationPageView(\n activeTab === 'workspace'\n ? 'organizations.my-workspace-invites'\n : 'organizations.my-organization-invites'\n );\n const tr = useTr();\n const { isLoading: permissionsLoading } = usePermissions();\n const { basePath = '/organizations', currentUser, authToken, apiGatewayUrl } = useOrgConsole();\n const orgInvitesEnabled = activeTab === 'organization';\n const workspaceInvitesEnabled = activeTab === 'workspace';\n const { invites, loading, error, total } = useMyInvites(\n OrganizationInviteStatus.Pending,\n 20,\n 0,\n orgInvitesEnabled\n );\n const {\n invites: workspaceInvites,\n loading: workspaceLoading,\n error: workspaceError,\n total: workspaceTotal,\n refetch: refetchWorkspaceInvites,\n } = useMyWorkspaceInvites(currentUser?.email, workspaceInvitesEnabled);\n const { acceptInvite, loading: accepting } = useAcceptInvite();\n const { rejectInvite, loading: rejecting } = useRejectInvite();\n const { acceptInvite: acceptWorkspaceInvite, loading: acceptingWorkspace } =\n useAcceptWorkspaceInvite();\n const { rejectInvite: rejectWorkspaceInvite, loading: rejectingWorkspace } =\n useRejectWorkspaceInvite();\n const [actionError, setActionError] = useState<TabActionError | null>(null);\n\n const visibleActionError = actionError?.tab === activeTab ? actionError.message : null;\n const [processingWorkspaceAction, setProcessingWorkspaceAction] = useState<{\n inviteId: string;\n action: 'accept' | 'reject';\n } | null>(null);\n\n const workspaceInviteActors = useMemo<ActorInput[]>(\n () =>\n workspaceInvites.flatMap((invite) =>\n invite.invitedById ? [{ actorId: invite.invitedById, actorType: 'user' as const }] : []\n ),\n [workspaceInvites]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(workspaceInviteActors, {\n apiGatewayUrl,\n authToken,\n });\n\n const sortedWorkspaceInvites = useMemo(\n () =>\n [...workspaceInvites].sort(\n (left: (typeof workspaceInvites)[number], right: (typeof workspaceInvites)[number]) =>\n new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [workspaceInvites]\n );\n\n const totalCount = activeTab === 'workspace' ? workspaceTotal : total;\n\n const handleAccept = async (inviteId: string) => {\n try {\n setActionError(null);\n await acceptInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.acceptFailed', 'Failed to accept invite'),\n });\n }\n };\n\n const handleReject = async (inviteId: string) => {\n try {\n setActionError(null);\n await rejectInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.rejectFailed', 'Failed to reject invite'),\n });\n }\n };\n\n const handleAcceptWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'accept' });\n setActionError(null);\n await acceptWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceAcceptFailed', 'Failed to accept workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'accept' ? null : current\n );\n }\n };\n\n const handleRejectWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'reject' });\n setActionError(null);\n await rejectWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceRejectFailed', 'Failed to reject workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'reject' ? null : current\n );\n }\n };\n\n // Own invites are always accessible — no permission denial needed.\n // We still wait for permissions to load to ensure consistent UX.\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 (workspaceInvitesEnabled && !currentUser?.email) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 text-center\">\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr('members.myInvites.signInWorkspace', 'Sign in to view your workspace invites.')}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && loading && invites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceLoading && workspaceInvites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.workspaceLoadFailed', 'Failed to load workspace invites')}:{' '}\n {workspaceError.message}\n </p>\n </div>\n </div>\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\">\n <div className=\"max-w-4xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n {totalCount > 0 && (\n <span className=\"px-2.5 py-0.5 rounded-full text-xs font-medium bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n {visibleActionError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertCircle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{visibleActionError}</p>\n </div>\n )}\n\n {workspaceInvitesEnabled ? (\n sortedWorkspaceInvites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <BriefcaseBusiness\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.workspaceEmptyTitle', 'No pending workspace invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.workspaceEmptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {sortedWorkspaceInvites.map((invite) => {\n const workspaceName = getWorkspaceDisplayName(invite);\n const roleNames = getRoleDisplayNames(invite);\n const projectNames = getProjectDisplayNames(invite);\n const inviterProfile = invite.invitedById\n ? inviterProfiles[invite.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n tr('members.myInvites.invitedByFallback', 'Someone');\n const isProcessingWorkspaceAccept =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'accept' &&\n acceptingWorkspace;\n const isProcessingWorkspaceReject =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'reject' &&\n rejectingWorkspace;\n\n return (\n <GlassCard key={invite.id} treatment=\"insight\" className=\"border-border-seam p-5\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-[var(--color-bg-subtle)] p-2\">\n <BriefcaseBusiness\n size={20}\n className=\"text-[var(--color-text-secondary)]\"\n />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] break-words\">\n {workspaceName}\n </h3>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)] break-all\">\n {invite.email}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\">{invite.status}</Badge>\n {roleNames.map((roleName) => (\n <Badge key={`${invite.id}-${roleName}`} variant=\"outline\">\n {roleName}\n </Badge>\n ))}\n </div>\n\n <dl className=\"grid gap-3 text-sm text-[var(--color-text-secondary)] sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n inviterLabel\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{formatDate(invite.createdAt) ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{formatDate(invite.expiresAt) ?? '—'}</dd>\n </div>\n {projectNames.length > 0 ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.projectsLabel', 'Projects')}\n </dt>\n <dd className=\"mt-1 flex flex-wrap gap-2\">\n {projectNames.map((projectName) => (\n <Badge key={`${invite.id}-${projectName}`} variant=\"outline\">\n {projectName}\n </Badge>\n ))}\n </dd>\n </div>\n ) : null}\n </dl>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => void handleAcceptWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-status-success-bg)] px-4 py-2 text-sm font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceAccept ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleRejectWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-border-default)] bg-transparent px-4 py-2 text-sm font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:border-[var(--color-status-error-border)] hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceReject ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )\n ) : invites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <Building2\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {invites.map((invite) => (\n <MyInviteCard\n key={invite.id}\n invite={invite}\n onAccept={handleAccept}\n onReject={handleReject}\n accepting={accepting}\n rejecting={rejecting}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|