@burdenoff/microfe-organization 2026.731.1 → 2026.828.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/node_modules/sonner/dist/index.js +74 -67
- package/dist/node_modules/sonner/dist/index.js.map +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.js +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useParams } from 'react-router
|
|
1
|
+
{"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { buildActorFallback, useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Member Detail Page\n * Simplified version showing available member fields\n */\nexport function MemberDetailPage() {\n const tr = useTr();\n const { organizationId, userId } = useParams<{ organizationId: string; userId: string }>();\n const { navigate, basePath = '/organizations', apiGatewayUrl, authToken } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { member, loading, error } = useMember(organizationId!, userId!);\n\n const memberActors = useMemo<ActorInput[]>(\n () => (userId ? [{ actorId: userId, actorType: 'user' }] : []),\n [userId]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n const memberProfile =\n userId && member\n ? (profileMap[member.userId] ??\n buildActorFallback({ actorId: member.userId, actorType: 'user' }))\n : null;\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.detail.noPermission', \"You don't have permission to view member details.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.detail.loading', 'Loading member...')} />\n </div>\n );\n }\n\n if (error || !member) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('members.detail.notFound', 'Member not found')}\n action={{\n label: tr('common.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/${organizationId}/members`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-200\">\n {/* Header */}\n <div className=\"mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members`)}\n className=\"inline-flex items-center gap-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-all duration-200 mb-4\"\n >\n <ArrowLeft size={20} />\n {tr('members.detail.backToMembers', 'Back to Members')}\n </button>\n\n <EmphasisPanel className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-full bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Shield size={32} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div>\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {memberProfile?.displayName ?? tr('members.detail.titlePrefix', 'Member')}\n </h1>\n {memberProfile?.email ? (\n <p className=\"text-[var(--color-text-secondary)] mt-1 break-all\">\n {memberProfile.email}\n </p>\n ) : null}\n <p className=\"text-[var(--color-text-secondary)] mt-1\">{member.role}</p>\n <PagePurpose className=\"mt-3 max-w-2xl\">\n {tr(\n 'members.detail.purpose',\n \"This page shows one person's membership in the organization — their role, when they joined, and who invited them. Use it to confirm a member has the right level of access.\"\n )}\n </PagePurpose>\n <details className=\"mt-2 text-xs text-[var(--color-text-secondary)]\">\n <summary className=\"cursor-pointer\">\n {tr('members.detail.showIds', 'Show technical IDs')}\n </summary>\n <dl className=\"mt-1 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1\">\n <dt>{tr('members.detail.userId', 'User ID')}</dt>\n <dd className=\"break-all font-mono\">{member.userId}</dd>\n <dt>{tr('members.list.memberId', 'Member ID')}</dt>\n <dd className=\"break-all font-mono\">{member.id}</dd>\n </dl>\n </details>\n </div>\n </EmphasisPanel>\n </div>\n\n {/* Details */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.detail.info', 'Member Information')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </p>\n <div className=\"mt-1\">\n <MemberRoleBadge role={member.role} />\n </div>\n </div>\n\n {member.invitedBy && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.invitedBy', 'Invited By')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.invitedBy}</p>\n </div>\n )}\n\n {member.joinedAt && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.joinedAt', 'Joined At')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {new Date(member.joinedAt).toLocaleString()}\n </p>\n </div>\n )}\n </div>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ import { GlassCard as b, IllustratedEmptyState as te, PagePurpose as ne } from "
|
|
|
16
16
|
import { useFeatureFlagDetails as re } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
17
17
|
import { usePermissions as ie } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
18
18
|
import { Fragment as ae, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
19
|
-
import { useParams as C } from "react-router
|
|
19
|
+
import { useParams as C } from "react-router";
|
|
20
20
|
import { ActorIdentity as w, buildActorFallback as oe, useActorProfiles as se } from "@burdenoff/fe-libs/shared";
|
|
21
21
|
//#region src/organization-management/modules/members/pages/MembersListPage.tsx
|
|
22
22
|
function ce() {
|
|
@@ -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 { GlassCard, IllustratedEmptyState, PagePurpose } 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 { 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 <PagePurpose className=\"max-w-2xl\">\n {tr(\n 'members.list.purpose',\n 'Members are the people who belong to this organization and inherit access to its workspaces. Use this page to see who has access, check their roles, invite new people, or remove anyone who should no longer be here.'\n )}\n </PagePurpose>\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=\"glass\" glow 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\n treatment=\"glass\"\n glow={members.length === 0}\n className=\"border-border-seam p-6\"\n >\n <IllustratedEmptyState\n illustration=\"empty-people\"\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 <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 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:bg-[var(--color-action-primary-bgHover)]\"\n >\n {tr('members.invite.button', 'Invite Member')}\n </button>\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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"}
|
|
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';\nimport { toast } from 'sonner';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } 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 { 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 <PagePurpose className=\"max-w-2xl\">\n {tr(\n 'members.list.purpose',\n 'Members are the people who belong to this organization and inherit access to its workspaces. Use this page to see who has access, check their roles, invite new people, or remove anyone who should no longer be here.'\n )}\n </PagePurpose>\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=\"glass\" glow 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\n treatment=\"glass\"\n glow={members.length === 0}\n className=\"border-border-seam p-6\"\n >\n <IllustratedEmptyState\n illustration=\"empty-people\"\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 <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 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:bg-[var(--color-action-primary-bgHover)]\"\n >\n {tr('members.invite.button', 'Invite Member')}\n </button>\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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"}
|
|
@@ -12,7 +12,7 @@ import { AlertCircle as te, BriefcaseBusiness as ne, Loader2 as g, Mail as _ } f
|
|
|
12
12
|
import { Badge as v, GlassCard as y, IllustratedEmptyState as b, PagePurpose as re } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
import { usePermissions as x } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
14
14
|
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
15
|
-
import { NavLink as w } from "react-router
|
|
15
|
+
import { NavLink as w } from "react-router";
|
|
16
16
|
import { ActorIdentity as ie, useActorProfiles as T } from "@burdenoff/fe-libs/shared";
|
|
17
17
|
//#region src/organization-management/modules/members/pages/MyInvitesPage.tsx
|
|
18
18
|
function E(e) {
|
|
@@ -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 } 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, IllustratedEmptyState, PagePurpose } 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 <PagePurpose className=\"mb-4 max-w-3xl\">\n {tr(\n 'members.myInvites.purpose',\n 'This is your personal inbox for invitations — when someone invites you to a workspace or organization, accept it here to gain access, or decline if it was not meant for you. Switch tabs to review each type of invite.'\n )}\n </PagePurpose>\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 <GlassCard treatment=\"glass\" glow className=\"rounded-xl\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr('members.myInvites.workspaceEmptyTitle', 'No pending workspace invites')}\n description={tr(\n 'members.myInvites.workspaceEmptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n />\n </GlassCard>\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 <GlassCard treatment=\"glass\" glow className=\"rounded-xl\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr('members.myInvites.emptyTitle', 'No pending invites')}\n description={tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n />\n </GlassCard>\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';\nimport { Mail, Loader2, AlertCircle, BriefcaseBusiness } 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, IllustratedEmptyState, PagePurpose } 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 <PagePurpose className=\"mb-4 max-w-3xl\">\n {tr(\n 'members.myInvites.purpose',\n 'This is your personal inbox for invitations — when someone invites you to a workspace or organization, accept it here to gain access, or decline if it was not meant for you. Switch tabs to review each type of invite.'\n )}\n </PagePurpose>\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 <GlassCard treatment=\"glass\" glow className=\"rounded-xl\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr('members.myInvites.workspaceEmptyTitle', 'No pending workspace invites')}\n description={tr(\n 'members.myInvites.workspaceEmptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n />\n </GlassCard>\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 <GlassCard treatment=\"glass\" glow className=\"rounded-xl\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr('members.myInvites.emptyTitle', 'No pending invites')}\n description={tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n />\n </GlassCard>\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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"}
|
package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js
CHANGED
|
@@ -7,7 +7,7 @@ import { useState as s } from "react";
|
|
|
7
7
|
import { AlertTriangle as c, Ban as l, Building2 as u, CheckCircle as d, Clock as f, Home as p, Loader2 as m, Mail as h, XCircle as g } from "lucide-react";
|
|
8
8
|
import { Badge as _, EmphasisPanel as v, GlassCard as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
9
9
|
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
10
|
-
import { useParams as C } from "react-router
|
|
10
|
+
import { useParams as C } from "react-router";
|
|
11
11
|
//#region src/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.tsx
|
|
12
12
|
function w(e) {
|
|
13
13
|
if (!e) return null;
|
package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrganizationInviteAcceptancePage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Mail,\n Loader2,\n CheckCircle,\n XCircle,\n Clock,\n AlertTriangle,\n Ban,\n Home,\n Building2,\n} from 'lucide-react';\nimport { Badge, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrganizationInvite, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\n\n/**\n * OrganizationInviteAcceptancePage\n *\n * Per-invite acceptance page reached from the organization invite email link\n * (`/organizations/invite/:inviteId`). Mirrors the workspace invite-acceptance\n * flow: loads the single invite by id, renders the appropriate state machine\n * (PENDING → accept/reject, ACCEPTED/REJECTED/EXPIRED/CANCELLED), and gates the\n * unauthenticated case behind a sign-in CTA that returns the user here.\n *\n * The previous behaviour dropped the recipient on `/organizations/invites`\n * (the workspace-invites list tab) with no invite context, which rendered as\n * an empty page. The email now deep-links here with the invite id.\n */\nfunction formatDate(value?: string | null): string | null {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n return date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction PageShell({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-10 sm:px-6\">\n <div className=\"mx-auto max-w-2xl\">{children}</div>\n </div>\n );\n}\n\ninterface StatusViewProps {\n icon: typeof CheckCircle;\n iconClass: string;\n title: string;\n description: string;\n email?: string;\n primaryLabel: string;\n onPrimary: () => void;\n}\n\nfunction StatusView({\n icon: Icon,\n iconClass,\n title,\n description,\n email,\n primaryLabel,\n onPrimary,\n}: StatusViewProps) {\n return (\n <PageShell>\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center\">\n <div\n className={`mx-auto mb-4 flex size-14 items-center justify-center rounded-2xl ${iconClass}`}\n >\n <Icon size={28} />\n </div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">{title}</h1>\n <p className=\"mt-2 text-[var(--color-text-secondary)]\">{description}</p>\n {email ? (\n <p className=\"mt-1 break-all text-sm text-[var(--color-text-muted)]\">{email}</p>\n ) : null}\n <button\n type=\"button\"\n onClick={onPrimary}\n className=\"mt-6 inline-flex items-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n <Home size={16} />\n {primaryLabel}\n </button>\n </GlassCard>\n </PageShell>\n );\n}\n\nexport function OrganizationInviteAcceptancePage() {\n useOrganizationPageView('organizations.invite-acceptance');\n const tr = useTr();\n const { inviteId = '' } = useParams<{ inviteId: string }>();\n const { basePath = '/organizations', navigate, currentUser, authToken } = useOrgConsole();\n\n const isAuthenticated = Boolean(authToken || currentUser?.email);\n const { invite, loading, error, refetch } = useOrganizationInvite(inviteId, isAuthenticated);\n const { acceptInvite } = useAcceptInvite();\n const { rejectInvite } = useRejectInvite();\n\n const [processing, setProcessing] = useState<'accept' | 'reject' | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const goToInvitesList = () => navigate(`${basePath}/invites/organizations`);\n\n const handleAccept = async () => {\n if (!inviteId) return;\n try {\n setProcessing('accept');\n setActionError(null);\n await acceptInvite(inviteId);\n navigate(invite?.organizationId ? `${basePath}/${invite.organizationId}` : basePath);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.inviteAcceptance.acceptFailed', 'Failed to accept invitation')\n );\n } finally {\n setProcessing(null);\n }\n };\n\n const handleReject = async () => {\n if (!inviteId) return;\n try {\n setProcessing('reject');\n setActionError(null);\n await rejectInvite(inviteId);\n goToInvitesList();\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.inviteAcceptance.rejectFailed', 'Failed to reject invitation')\n );\n } finally {\n setProcessing(null);\n }\n };\n\n // Unauthenticated — invitees must sign in with the invited email first.\n if (!isAuthenticated) {\n const returnTo = encodeURIComponent(`${basePath}/invite/${inviteId}`);\n return (\n <PageShell>\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8\">\n <div className=\"mb-4 flex items-center gap-3\">\n <div className=\"rounded-xl bg-[var(--color-bg-subtle)] p-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.inviteAcceptance.signInRequired', 'Sign in to view your invitation')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'members.inviteAcceptance.signInDescription',\n 'Organization invitations require an authenticated account before you can review or accept them.'\n )}\n </p>\n </div>\n </div>\n <p className=\"mb-6 text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.inviteAcceptance.signInHint',\n 'Use the same email address that received the invitation, then come back here to accept or decline it.'\n )}\n </p>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <a\n href={`/auth/login?returnTo=${returnTo}`}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n {tr('members.inviteAcceptance.signInButton', 'Sign In')}\n </a>\n <button\n type=\"button\"\n onClick={() => navigate(basePath)}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Home size={16} />\n {tr('members.inviteAcceptance.backToOrganizations', 'Back to Organizations')}\n </button>\n </div>\n </GlassCard>\n </PageShell>\n );\n }\n\n if (loading) {\n return (\n <PageShell>\n <div className=\"flex min-h-[300px] items-center justify-center rounded-2xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={28} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </PageShell>\n );\n }\n\n if (error) {\n return (\n <StatusView\n icon={AlertTriangle}\n iconClass=\"bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]\"\n title={tr('members.inviteAcceptance.errorTitle', 'Error Loading Invitation')}\n description={error.message}\n primaryLabel={tr('members.inviteAcceptance.tryAgain', 'Try Again')}\n onPrimary={() => {\n void refetch();\n }}\n />\n );\n }\n\n if (!invite) {\n return (\n <StatusView\n icon={AlertTriangle}\n iconClass=\"bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)]\"\n title={tr('members.inviteAcceptance.notFoundTitle', 'Invitation Not Found')}\n description={tr(\n 'members.inviteAcceptance.notFoundDescription',\n 'This invitation link is invalid, has been removed, or was addressed to a different account.'\n )}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n const isExpiredByDate = invite.expiresAt && new Date(invite.expiresAt) < new Date();\n const effectiveStatus =\n invite.status === InviteStatus.Pending && isExpiredByDate\n ? InviteStatus.Expired\n : invite.status;\n\n if (effectiveStatus === InviteStatus.Accepted) {\n return (\n <StatusView\n icon={CheckCircle}\n iconClass=\"bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)]\"\n title={tr('members.inviteAcceptance.alreadyAcceptedTitle', 'Invitation Already Accepted')}\n description={tr(\n 'members.inviteAcceptance.alreadyAcceptedDescription',\n 'You have already accepted this invitation.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganization', 'Go to Organization')}\n onPrimary={() => navigate(`${basePath}/${invite.organizationId}`)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Rejected) {\n return (\n <StatusView\n icon={XCircle}\n iconClass=\"bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)]\"\n title={tr('members.inviteAcceptance.rejectedTitle', 'Invitation Declined')}\n description={tr(\n 'members.inviteAcceptance.rejectedDescription',\n 'This invitation was previously declined.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Expired) {\n return (\n <StatusView\n icon={Clock}\n iconClass=\"bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)]\"\n title={tr('members.inviteAcceptance.expiredTitle', 'Invitation Expired')}\n description={tr(\n 'members.inviteAcceptance.expiredDescription',\n 'This invitation has expired. Please contact an organization admin for a new one.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Cancelled) {\n return (\n <StatusView\n icon={Ban}\n iconClass=\"bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]\"\n title={tr('members.inviteAcceptance.cancelledTitle', 'Invitation Cancelled')}\n description={tr(\n 'members.inviteAcceptance.cancelledDescription',\n 'This invitation was cancelled by the sender and is no longer valid.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n // PENDING — show the acceptance UI.\n const organizationName = invite.organization?.name ?? invite.organizationId;\n const expiresLabel = formatDate(invite.expiresAt);\n const busy = processing !== null;\n\n return (\n <PageShell>\n <EmphasisPanel className=\"p-8\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"rounded-xl bg-[var(--color-bg-subtle)] p-3\">\n <Building2 size={24} className=\"text-[var(--color-text-primary)]\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.inviteAcceptance.pageTitle', 'Organization Invitation')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr('members.inviteAcceptance.pageSubtitle', \"You've been invited to join\")}{' '}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {organizationName}\n </span>\n </p>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n 'members.inviteAcceptance.purpose',\n 'Review the details of this invitation below. Accept it to join the organization and gain access to its workspaces, or decline if it was not meant for you.'\n )}\n </PagePurpose>\n\n <dl className=\"space-y-4 rounded-xl bg-[var(--color-bg-subtle)] p-5\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.emailLabel', 'Email Address')}\n </dt>\n <dd className=\"mt-1 break-all text-[var(--color-text-primary)]\">{invite.email}</dd>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.roleLabel', 'Role')}\n </dt>\n <dd>\n <Badge variant=\"secondary\">{invite.role}</Badge>\n </dd>\n </div>\n {expiresLabel ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.expiresLabel', 'Expires')}\n </dt>\n <dd className=\"mt-1 flex items-center gap-2 text-[var(--color-text-primary)]\">\n <Clock size={16} className=\"text-[var(--color-text-muted)]\" />\n {expiresLabel}\n </dd>\n </div>\n ) : null}\n {invite.message ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.messageLabel', 'Message')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-secondary)]\">{invite.message}</dd>\n </div>\n ) : null}\n </dl>\n\n {actionError ? (\n <div className=\"mt-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertTriangle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{actionError}</p>\n </div>\n ) : null}\n\n <div className=\"mt-6 flex flex-col gap-3 border-t border-[var(--color-border-seam)] pt-6 sm:flex-row\">\n <button\n type=\"button\"\n onClick={() => void handleAccept()}\n disabled={busy}\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-[var(--color-status-success-bg)] px-6 py-3 font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {processing === 'accept' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <CheckCircle size={16} />\n )}\n {tr('members.inviteAcceptance.acceptButton', 'Accept Invitation')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleReject()}\n disabled={busy}\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-3 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 {processing === 'reject' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <XCircle size={16} />\n )}\n {tr('members.inviteAcceptance.declineButton', 'Decline')}\n </button>\n </div>\n </EmphasisPanel>\n </PageShell>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OrganizationInviteAcceptancePage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router';\nimport {\n Mail,\n Loader2,\n CheckCircle,\n XCircle,\n Clock,\n AlertTriangle,\n Ban,\n Home,\n Building2,\n} from 'lucide-react';\nimport { Badge, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrganizationInvite, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\n\n/**\n * OrganizationInviteAcceptancePage\n *\n * Per-invite acceptance page reached from the organization invite email link\n * (`/organizations/invite/:inviteId`). Mirrors the workspace invite-acceptance\n * flow: loads the single invite by id, renders the appropriate state machine\n * (PENDING → accept/reject, ACCEPTED/REJECTED/EXPIRED/CANCELLED), and gates the\n * unauthenticated case behind a sign-in CTA that returns the user here.\n *\n * The previous behaviour dropped the recipient on `/organizations/invites`\n * (the workspace-invites list tab) with no invite context, which rendered as\n * an empty page. The email now deep-links here with the invite id.\n */\nfunction formatDate(value?: string | null): string | null {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n return date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction PageShell({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-10 sm:px-6\">\n <div className=\"mx-auto max-w-2xl\">{children}</div>\n </div>\n );\n}\n\ninterface StatusViewProps {\n icon: typeof CheckCircle;\n iconClass: string;\n title: string;\n description: string;\n email?: string;\n primaryLabel: string;\n onPrimary: () => void;\n}\n\nfunction StatusView({\n icon: Icon,\n iconClass,\n title,\n description,\n email,\n primaryLabel,\n onPrimary,\n}: StatusViewProps) {\n return (\n <PageShell>\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center\">\n <div\n className={`mx-auto mb-4 flex size-14 items-center justify-center rounded-2xl ${iconClass}`}\n >\n <Icon size={28} />\n </div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">{title}</h1>\n <p className=\"mt-2 text-[var(--color-text-secondary)]\">{description}</p>\n {email ? (\n <p className=\"mt-1 break-all text-sm text-[var(--color-text-muted)]\">{email}</p>\n ) : null}\n <button\n type=\"button\"\n onClick={onPrimary}\n className=\"mt-6 inline-flex items-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n <Home size={16} />\n {primaryLabel}\n </button>\n </GlassCard>\n </PageShell>\n );\n}\n\nexport function OrganizationInviteAcceptancePage() {\n useOrganizationPageView('organizations.invite-acceptance');\n const tr = useTr();\n const { inviteId = '' } = useParams<{ inviteId: string }>();\n const { basePath = '/organizations', navigate, currentUser, authToken } = useOrgConsole();\n\n const isAuthenticated = Boolean(authToken || currentUser?.email);\n const { invite, loading, error, refetch } = useOrganizationInvite(inviteId, isAuthenticated);\n const { acceptInvite } = useAcceptInvite();\n const { rejectInvite } = useRejectInvite();\n\n const [processing, setProcessing] = useState<'accept' | 'reject' | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const goToInvitesList = () => navigate(`${basePath}/invites/organizations`);\n\n const handleAccept = async () => {\n if (!inviteId) return;\n try {\n setProcessing('accept');\n setActionError(null);\n await acceptInvite(inviteId);\n navigate(invite?.organizationId ? `${basePath}/${invite.organizationId}` : basePath);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.inviteAcceptance.acceptFailed', 'Failed to accept invitation')\n );\n } finally {\n setProcessing(null);\n }\n };\n\n const handleReject = async () => {\n if (!inviteId) return;\n try {\n setProcessing('reject');\n setActionError(null);\n await rejectInvite(inviteId);\n goToInvitesList();\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.inviteAcceptance.rejectFailed', 'Failed to reject invitation')\n );\n } finally {\n setProcessing(null);\n }\n };\n\n // Unauthenticated — invitees must sign in with the invited email first.\n if (!isAuthenticated) {\n const returnTo = encodeURIComponent(`${basePath}/invite/${inviteId}`);\n return (\n <PageShell>\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8\">\n <div className=\"mb-4 flex items-center gap-3\">\n <div className=\"rounded-xl bg-[var(--color-bg-subtle)] p-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.inviteAcceptance.signInRequired', 'Sign in to view your invitation')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'members.inviteAcceptance.signInDescription',\n 'Organization invitations require an authenticated account before you can review or accept them.'\n )}\n </p>\n </div>\n </div>\n <p className=\"mb-6 text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.inviteAcceptance.signInHint',\n 'Use the same email address that received the invitation, then come back here to accept or decline it.'\n )}\n </p>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <a\n href={`/auth/login?returnTo=${returnTo}`}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n {tr('members.inviteAcceptance.signInButton', 'Sign In')}\n </a>\n <button\n type=\"button\"\n onClick={() => navigate(basePath)}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Home size={16} />\n {tr('members.inviteAcceptance.backToOrganizations', 'Back to Organizations')}\n </button>\n </div>\n </GlassCard>\n </PageShell>\n );\n }\n\n if (loading) {\n return (\n <PageShell>\n <div className=\"flex min-h-[300px] items-center justify-center rounded-2xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={28} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </PageShell>\n );\n }\n\n if (error) {\n return (\n <StatusView\n icon={AlertTriangle}\n iconClass=\"bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]\"\n title={tr('members.inviteAcceptance.errorTitle', 'Error Loading Invitation')}\n description={error.message}\n primaryLabel={tr('members.inviteAcceptance.tryAgain', 'Try Again')}\n onPrimary={() => {\n void refetch();\n }}\n />\n );\n }\n\n if (!invite) {\n return (\n <StatusView\n icon={AlertTriangle}\n iconClass=\"bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)]\"\n title={tr('members.inviteAcceptance.notFoundTitle', 'Invitation Not Found')}\n description={tr(\n 'members.inviteAcceptance.notFoundDescription',\n 'This invitation link is invalid, has been removed, or was addressed to a different account.'\n )}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n const isExpiredByDate = invite.expiresAt && new Date(invite.expiresAt) < new Date();\n const effectiveStatus =\n invite.status === InviteStatus.Pending && isExpiredByDate\n ? InviteStatus.Expired\n : invite.status;\n\n if (effectiveStatus === InviteStatus.Accepted) {\n return (\n <StatusView\n icon={CheckCircle}\n iconClass=\"bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)]\"\n title={tr('members.inviteAcceptance.alreadyAcceptedTitle', 'Invitation Already Accepted')}\n description={tr(\n 'members.inviteAcceptance.alreadyAcceptedDescription',\n 'You have already accepted this invitation.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganization', 'Go to Organization')}\n onPrimary={() => navigate(`${basePath}/${invite.organizationId}`)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Rejected) {\n return (\n <StatusView\n icon={XCircle}\n iconClass=\"bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)]\"\n title={tr('members.inviteAcceptance.rejectedTitle', 'Invitation Declined')}\n description={tr(\n 'members.inviteAcceptance.rejectedDescription',\n 'This invitation was previously declined.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Expired) {\n return (\n <StatusView\n icon={Clock}\n iconClass=\"bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)]\"\n title={tr('members.inviteAcceptance.expiredTitle', 'Invitation Expired')}\n description={tr(\n 'members.inviteAcceptance.expiredDescription',\n 'This invitation has expired. Please contact an organization admin for a new one.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n if (effectiveStatus === InviteStatus.Cancelled) {\n return (\n <StatusView\n icon={Ban}\n iconClass=\"bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]\"\n title={tr('members.inviteAcceptance.cancelledTitle', 'Invitation Cancelled')}\n description={tr(\n 'members.inviteAcceptance.cancelledDescription',\n 'This invitation was cancelled by the sender and is no longer valid.'\n )}\n email={invite.email}\n primaryLabel={tr('members.inviteAcceptance.goToOrganizations', 'Go to Organizations')}\n onPrimary={() => navigate(basePath)}\n />\n );\n }\n\n // PENDING — show the acceptance UI.\n const organizationName = invite.organization?.name ?? invite.organizationId;\n const expiresLabel = formatDate(invite.expiresAt);\n const busy = processing !== null;\n\n return (\n <PageShell>\n <EmphasisPanel className=\"p-8\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"rounded-xl bg-[var(--color-bg-subtle)] p-3\">\n <Building2 size={24} className=\"text-[var(--color-text-primary)]\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.inviteAcceptance.pageTitle', 'Organization Invitation')}\n </h1>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr('members.inviteAcceptance.pageSubtitle', \"You've been invited to join\")}{' '}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {organizationName}\n </span>\n </p>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n 'members.inviteAcceptance.purpose',\n 'Review the details of this invitation below. Accept it to join the organization and gain access to its workspaces, or decline if it was not meant for you.'\n )}\n </PagePurpose>\n\n <dl className=\"space-y-4 rounded-xl bg-[var(--color-bg-subtle)] p-5\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.emailLabel', 'Email Address')}\n </dt>\n <dd className=\"mt-1 break-all text-[var(--color-text-primary)]\">{invite.email}</dd>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.roleLabel', 'Role')}\n </dt>\n <dd>\n <Badge variant=\"secondary\">{invite.role}</Badge>\n </dd>\n </div>\n {expiresLabel ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.expiresLabel', 'Expires')}\n </dt>\n <dd className=\"mt-1 flex items-center gap-2 text-[var(--color-text-primary)]\">\n <Clock size={16} className=\"text-[var(--color-text-muted)]\" />\n {expiresLabel}\n </dd>\n </div>\n ) : null}\n {invite.message ? (\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-muted)]\">\n {tr('members.inviteAcceptance.messageLabel', 'Message')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-secondary)]\">{invite.message}</dd>\n </div>\n ) : null}\n </dl>\n\n {actionError ? (\n <div className=\"mt-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertTriangle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{actionError}</p>\n </div>\n ) : null}\n\n <div className=\"mt-6 flex flex-col gap-3 border-t border-[var(--color-border-seam)] pt-6 sm:flex-row\">\n <button\n type=\"button\"\n onClick={() => void handleAccept()}\n disabled={busy}\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-[var(--color-status-success-bg)] px-6 py-3 font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {processing === 'accept' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <CheckCircle size={16} />\n )}\n {tr('members.inviteAcceptance.acceptButton', 'Accept Invitation')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleReject()}\n disabled={busy}\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-3 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 {processing === 'reject' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <XCircle size={16} />\n )}\n {tr('members.inviteAcceptance.declineButton', 'Decline')}\n </button>\n </div>\n </EmphasisPanel>\n </PageShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ import { ArrowLeft as f, Briefcase as p, Building as m, Edit as h, UserPlus as g
|
|
|
13
13
|
import { CTAOverflowMenu as v, EmphasisPanel as y, GlassCard as b, IllustratedEmptyState as x, NextSteps as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
import { usePermissions as w } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
15
15
|
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
16
|
-
import { useParams as D } from "react-router
|
|
16
|
+
import { useParams as D } from "react-router";
|
|
17
17
|
//#region src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx
|
|
18
18
|
function O(e) {
|
|
19
19
|
return new Date(e).toLocaleDateString("en-US", {
|