@burdenoff/microfe-organization 2026.1013.2 → 2026.1101.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/generated/global-operations.js +57 -58
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js +1 -2
- package/dist/generated/global-types.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js +2 -3
- package/dist/node_modules/sonner/dist/index.js.map +1 -1
- package/dist/node_modules/zod/v4/classic/errors.js +44 -0
- package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/parse.js +7 -0
- package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/schemas.js +652 -0
- package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/api.js +429 -0
- package/dist/node_modules/zod/v4/core/api.js.map +1 -0
- package/dist/node_modules/zod/v4/core/checks.js +143 -0
- package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
- package/dist/node_modules/zod/v4/core/core.js +84 -0
- package/dist/node_modules/zod/v4/core/core.js.map +1 -0
- package/dist/node_modules/zod/v4/core/doc.js +29 -0
- package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
- package/dist/node_modules/zod/v4/core/errors.js +90 -0
- package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +285 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/memoizer.js +188 -0
- package/dist/node_modules/zod/v4/core/memoizer.js.map +1 -0
- package/dist/node_modules/zod/v4/core/parse.js +159 -0
- package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/core/regexes.js +34 -0
- package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
- package/dist/node_modules/zod/v4/core/registries.js +41 -0
- package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
- package/dist/node_modules/zod/v4/core/schemas.js +979 -0
- package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js +330 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
- package/dist/node_modules/zod/v4/core/util.js +417 -0
- package/dist/node_modules/zod/v4/core/util.js.map +1 -0
- package/dist/node_modules/zod/v4/core/versions.js +9 -0
- package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
- package/dist/node_modules/zod/v4/locales/en.js +96 -0
- package/dist/node_modules/zod/v4/locales/en.js.map +1 -0
- package/dist/organization-management/AdminOrganizationRoot.js +13 -10
- package/dist/organization-management/AdminOrganizationRoot.js.map +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.js +53 -47
- package/dist/organization-management/AdminOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoot.js +13 -10
- package/dist/organization-management/UserOrganizationRoot.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +52 -46
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/constants/permissions.js +1 -2
- package/dist/organization-management/constants/permissions.js.map +1 -1
- package/dist/organization-management/hooks/useOrganizationEventEmitter.js +1 -2
- package/dist/organization-management/hooks/useOrganizationEventEmitter.js.map +1 -1
- package/dist/organization-management/hooks/useOrganizationPageView.js +1 -2
- package/dist/organization-management/hooks/useOrganizationPageView.js.map +1 -1
- package/dist/organization-management/modules/members/components/BulkInviteForm.js +3 -3
- package/dist/organization-management/modules/members/components/BulkInviteForm.js.map +1 -1
- package/dist/organization-management/modules/members/components/InviteMemberForm.js +1 -2
- package/dist/organization-management/modules/members/components/InviteMemberForm.js.map +1 -1
- package/dist/organization-management/modules/members/components/InviteStatusBadge.js +1 -2
- package/dist/organization-management/modules/members/components/InviteStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/members/components/MemberRoleBadge.js +1 -2
- package/dist/organization-management/modules/members/components/MemberRoleBadge.js.map +1 -1
- package/dist/organization-management/modules/members/components/MyInviteCard.js +5 -6
- package/dist/organization-management/modules/members/components/MyInviteCard.js.map +1 -1
- package/dist/organization-management/modules/members/components/PendingInvitesList.js +8 -9
- package/dist/organization-management/modules/members/components/PendingInvitesList.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.js +2 -3
- package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useMembers.js +1 -2
- package/dist/organization-management/modules/members/hooks/useMembers.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js +1 -2
- package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js +16 -17
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js +14 -15
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js +18 -19
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js +142 -143
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js +168 -169
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +13 -14
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -1
- package/dist/organization-management/modules/members/utils/bulkInviteCsv.js +4 -10
- package/dist/organization-management/modules/members/utils/bulkInviteCsv.js.map +1 -1
- package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js +1 -2
- package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js.map +1 -1
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js +14 -15
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
- package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js +1 -2
- package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/organizations/hooks/useOrganizations.js +2 -3
- package/dist/organization-management/modules/organizations/hooks/useOrganizations.js.map +1 -1
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js +22 -21
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +18 -19
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +29 -30
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js +14 -15
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js +33 -34
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/schemas/index.js +11 -12
- package/dist/organization-management/modules/organizations/schemas/index.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js +16 -24
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +5 -6
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +31 -32
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js +1 -2
- package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js +1 -2
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js +11 -12
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +18 -19
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +43 -44
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js +19 -20
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +240 -241
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/schemas/index.js +6 -7
- package/dist/organization-management/modules/workspaces/schemas/index.js.map +1 -1
- package/dist/organization-management/providers/OrgConsoleProvider.js +1 -2
- package/dist/organization-management/providers/OrgConsoleProvider.js.map +1 -1
- package/dist/organization-management/providers/OrgScopedPermissions.js +1 -2
- package/dist/organization-management/providers/OrgScopedPermissions.js.map +1 -1
- package/dist/organization-management/shared/components/dialog/ConfirmDialog.js +1 -2
- package/dist/organization-management/shared/components/dialog/ConfirmDialog.js.map +1 -1
- package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js +1 -2
- package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/ErrorState.js +1 -2
- package/dist/organization-management/shared/components/feedback/ErrorState.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/LoadingState.js +1 -2
- package/dist/organization-management/shared/components/feedback/LoadingState.js.map +1 -1
- package/dist/organization-management/shared/components/input/SearchInput.js +1 -2
- package/dist/organization-management/shared/components/input/SearchInput.js.map +1 -1
- package/dist/organization-management/shared/components/input/SelectionCheckbox.js +1 -2
- package/dist/organization-management/shared/components/input/SelectionCheckbox.js.map +1 -1
- package/dist/organization-management/shared/hooks/useDebouncedValue.js +1 -2
- package/dist/organization-management/shared/hooks/useDebouncedValue.js.map +1 -1
- package/dist/organization-management/shared/hooks/useTr.js +1 -2
- package/dist/organization-management/shared/hooks/useTr.js.map +1 -1
- package/dist/organization-management/shared/logger.js +1 -2
- package/dist/organization-management/shared/logger.js.map +1 -1
- package/dist/organization-management/shared/recaptcha.js +1 -2
- package/dist/organization-management/shared/recaptcha.js.map +1 -1
- package/dist/organization-management/shared/types/graphql.js +1 -2
- package/dist/organization-management/shared/types/graphql.js.map +1 -1
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +7 -6
- package/dist/node_modules/zod/v3/ZodError.js +0 -82
- package/dist/node_modules/zod/v3/ZodError.js.map +0 -1
- package/dist/node_modules/zod/v3/errors.js +0 -10
- package/dist/node_modules/zod/v3/errors.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/errorUtil.js +0 -9
- package/dist/node_modules/zod/v3/helpers/errorUtil.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/parseUtil.js +0 -93
- package/dist/node_modules/zod/v3/helpers/parseUtil.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/util.js +0 -76
- package/dist/node_modules/zod/v3/helpers/util.js.map +0 -1
- package/dist/node_modules/zod/v3/locales/en.js +0 -62
- package/dist/node_modules/zod/v3/locales/en.js.map +0 -1
- package/dist/node_modules/zod/v3/types.js +0 -2441
- package/dist/node_modules/zod/v3/types.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingInvitesList.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/PendingInvitesList.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { RotateCw, X, Loader2, AlertCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport type { OrganizationInvite } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface PendingInvitesListProps {\n organizationId: string;\n}\n\nexport function PendingInvitesList({ organizationId }: PendingInvitesListProps) {\n const tr = useTr();\n const { invites, loading, error, total } = useOrganizationInvites(\n organizationId,\n { status: InviteStatus.Pending },\n 50\n );\n const { cancelInvite, loading: cancelling } = useCancelInvite();\n const { resendInvite, loading: resending } = useResendInvite();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (loading && invites.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 size={20} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <p className=\"py-6 text-center text-sm text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n );\n }\n\n if (invites.length === 0) {\n return (\n <p className=\"text-sm text-[var(--color-text-muted)] text-center py-6\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </p>\n );\n }\n\n const handleCancel = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await cancelInvite(inviteId);\n toast.success(`${tr('members.invite.cancelSuccess', 'Invite cancelled for')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.cancelFailed', 'Failed to cancel invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const handleResend = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await resendInvite(inviteId);\n toast.success(`${tr('members.invite.resendSuccess', 'Invite re-sent to')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.resendFailed', 'Failed to resend invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const columns: ResponsiveTableColumn<OrganizationInvite>[] = [\n {\n key: 'email',\n header: tr('members.invite.emailLabel', 'Email Address'),\n priority: 'primary',\n cell: (invite) => <span className=\"text-[var(--color-text-primary)]\">{invite.email}</span>,\n },\n {\n key: 'role',\n header: tr('members.list.column.role', 'Role'),\n cell: (invite) => <MemberRoleBadge role={invite.role} />,\n },\n {\n key: 'status',\n header: tr('common.status', 'Status'),\n cell: (invite) => <InviteStatusBadge status={invite.status} />,\n },\n {\n key: 'expires',\n header: tr('members.invite.expires', 'Expires'),\n cell: (invite) => (\n <span className=\"text-[var(--color-text-muted)]\">\n {new Date(invite.expiresAt).toLocaleDateString()}\n </span>\n ),\n },\n ];\n\n return (\n <div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-secondary)] uppercase tracking-wider mb-3\">\n {tr('members.invite.pendingTitle', 'Pending Invites')} ({total})\n </h3>\n\n {actionError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-2.5\">\n <AlertCircle size={14} 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 )}\n\n <ResponsiveTable<OrganizationInvite>\n columns={columns}\n data={invites}\n rowKey={(invite) => invite.id}\n rowActions={(invite) => (\n <>\n <button\n type=\"button\"\n onClick={() => handleResend(invite.id, invite.email)}\n disabled={resending}\n className=\"p-1.5 rounded-md hover:bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] transition-colors\"\n title={tr('members.invite.resend', 'Resend invite')}\n aria-label={`${tr('members.invite.resendFor', 'Resend invite for')} ${invite.email}`}\n >\n <RotateCw size={14} />\n </button>\n <button\n type=\"button\"\n onClick={() => handleCancel(invite.id, invite.email)}\n disabled={cancelling}\n className=\"p-1.5 rounded-md text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)]\"\n title={tr('members.invite.cancel', 'Cancel invite')}\n aria-label={`${tr('members.invite.cancelFor', 'Cancel invite for')} ${invite.email}`}\n >\n <X size={14} />\n </button>\n </>\n )}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAeA,
|
|
1
|
+
{"version":3,"file":"PendingInvitesList.js","names":["_Fragment","PendingInvitesList","tr","useTr","useOrganizationInvites","organizationId","InviteStatus","cancelling","useCancelInvite","resending","useResendInvite","actionError","setActionError","useState","loading","invites","Loader2","error","handleCancel","cancelInvite","inviteId","toast","email","err","msg","handleResend","resendInvite","columns","invite","MemberRoleBadge","InviteStatusBadge","total","AlertCircle","ResponsiveTable","Fragment","RotateCw","X"],"sources":["../../../../../src/organization-management/modules/members/components/PendingInvitesList.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { RotateCw, X, Loader2, AlertCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport type { OrganizationInvite } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface PendingInvitesListProps {\n organizationId: string;\n}\n\nexport function PendingInvitesList({ organizationId }: PendingInvitesListProps) {\n const tr = useTr();\n const { invites, loading, error, total } = useOrganizationInvites(\n organizationId,\n { status: InviteStatus.Pending },\n 50\n );\n const { cancelInvite, loading: cancelling } = useCancelInvite();\n const { resendInvite, loading: resending } = useResendInvite();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (loading && invites.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 size={20} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <p className=\"py-6 text-center text-sm text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n );\n }\n\n if (invites.length === 0) {\n return (\n <p className=\"text-sm text-[var(--color-text-muted)] text-center py-6\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </p>\n );\n }\n\n const handleCancel = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await cancelInvite(inviteId);\n toast.success(`${tr('members.invite.cancelSuccess', 'Invite cancelled for')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.cancelFailed', 'Failed to cancel invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const handleResend = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await resendInvite(inviteId);\n toast.success(`${tr('members.invite.resendSuccess', 'Invite re-sent to')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.resendFailed', 'Failed to resend invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const columns: ResponsiveTableColumn<OrganizationInvite>[] = [\n {\n key: 'email',\n header: tr('members.invite.emailLabel', 'Email Address'),\n priority: 'primary',\n cell: (invite) => <span className=\"text-[var(--color-text-primary)]\">{invite.email}</span>,\n },\n {\n key: 'role',\n header: tr('members.list.column.role', 'Role'),\n cell: (invite) => <MemberRoleBadge role={invite.role} />,\n },\n {\n key: 'status',\n header: tr('common.status', 'Status'),\n cell: (invite) => <InviteStatusBadge status={invite.status} />,\n },\n {\n key: 'expires',\n header: tr('members.invite.expires', 'Expires'),\n cell: (invite) => (\n <span className=\"text-[var(--color-text-muted)]\">\n {new Date(invite.expiresAt).toLocaleDateString()}\n </span>\n ),\n },\n ];\n\n return (\n <div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-secondary)] uppercase tracking-wider mb-3\">\n {tr('members.invite.pendingTitle', 'Pending Invites')} ({total})\n </h3>\n\n {actionError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-2.5\">\n <AlertCircle size={14} 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 )}\n\n <ResponsiveTable<OrganizationInvite>\n columns={columns}\n data={invites}\n rowKey={(invite) => invite.id}\n rowActions={(invite) => (\n <>\n <button\n type=\"button\"\n onClick={() => handleResend(invite.id, invite.email)}\n disabled={resending}\n className=\"p-1.5 rounded-md hover:bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] transition-colors\"\n title={tr('members.invite.resend', 'Resend invite')}\n aria-label={`${tr('members.invite.resendFor', 'Resend invite for')} ${invite.email}`}\n >\n <RotateCw size={14} />\n </button>\n <button\n type=\"button\"\n onClick={() => handleCancel(invite.id, invite.email)}\n disabled={cancelling}\n className=\"p-1.5 rounded-md text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)]\"\n title={tr('members.invite.cancel', 'Cancel invite')}\n aria-label={`${tr('members.invite.cancelFor', 'Cancel invite for')} ${invite.email}`}\n >\n <X size={14} />\n </button>\n </>\n )}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { logger as d, redactId as f } from "../../../shared/logger.js";
|
|
|
4
4
|
import { executeRecaptcha as p } from "../../../shared/recaptcha.js";
|
|
5
5
|
import { gql as m } from "@apollo/client";
|
|
6
6
|
import { useMutation as h } from "@apollo/client/react";
|
|
7
|
-
|
|
7
|
+
// src/organization-management/modules/members/hooks/useInvites.ts
|
|
8
8
|
var g = m`
|
|
9
9
|
mutation AcceptOrganizationInviteWithCaptcha($inviteId: ID!, $recaptchaToken: String) {
|
|
10
10
|
acceptOrganizationInvite(inviteId: $inviteId, recaptchaToken: $recaptchaToken) {
|
|
@@ -124,7 +124,7 @@ function C() {
|
|
|
124
124
|
inviteId: f(e)
|
|
125
125
|
});
|
|
126
126
|
try {
|
|
127
|
-
let t = await p("invite_accept"), i = await r({ variables: {
|
|
127
|
+
let t = await /* @__PURE__ */ p("invite_accept"), i = await r({ variables: {
|
|
128
128
|
inviteId: e,
|
|
129
129
|
...t ? { recaptchaToken: t } : {}
|
|
130
130
|
} });
|
|
@@ -262,7 +262,6 @@ function E() {
|
|
|
262
262
|
error: i
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
|
-
//#endregion
|
|
266
265
|
export { C as useAcceptInvite, T as useCancelInvite, S as useInviteMember, b as useMyInvites, x as useOrganizationInvite, y as useOrganizationInvites, w as useRejectInvite, E as useResendInvite };
|
|
267
266
|
|
|
268
267
|
//# sourceMappingURL=useInvites.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInvites.js","names":[],"sources":["../../../../../src/organization-management/modules/members/hooks/useInvites.ts"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n useInviteOrganizationMemberMutation,\n useRejectOrganizationInviteMutation,\n useCancelOrganizationInviteMutation,\n useResendOrganizationInviteMutation,\n useGetOrganizationInvitesQuery,\n useGetMyOrganizationInvitesQuery,\n useGetMyOrganizationInviteQuery,\n GetOrganizationInvitesDocument,\n GetMyOrganizationInvitesDocument,\n GetMembersDocument,\n} from '../../../../generated/global-operations';\nimport type {\n InviteStatus,\n OrganizationInvite,\n OrganizationMember,\n} from '../../../../generated/global-types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\nimport { executeRecaptcha } from '../../../shared/recaptcha';\n\nconst ACCEPT_ORGANIZATION_INVITE_WITH_CAPTCHA = gql`\n mutation AcceptOrganizationInviteWithCaptcha($inviteId: ID!, $recaptchaToken: String) {\n acceptOrganizationInvite(inviteId: $inviteId, recaptchaToken: $recaptchaToken) {\n id\n userId\n organizationId\n role\n joinedAt\n }\n }\n`;\n\ninterface AcceptOrganizationInviteData {\n acceptOrganizationInvite: OrganizationMember;\n}\n\ninterface AcceptOrganizationInviteVariables {\n inviteId: string;\n recaptchaToken?: string;\n}\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organization invites (admin view)\n */\nexport function useOrganizationInvites(\n organizationId: string,\n filter?: { status?: InviteStatus },\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch } = useGetOrganizationInvitesQuery({\n variables: { organizationId, filter, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invites: (data?.organizationInvites?.items ?? []) as OrganizationInvite[],\n total: data?.organizationInvites?.total ?? 0,\n hasMore: data?.organizationInvites?.hasMore ?? false,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch invites for the current user (invitee view).\n *\n * `enabled` defaults to `true` for backward compat. Callers in admin /\n * service contexts (e.g. OrganizationsListPage in `mode=\"admin\"`) should\n * pass `false` to skip the network round-trip and avoid surfacing\n * authorization errors for actors that don't hold the self-scope.\n */\nexport function useMyInvites(\n status?: InviteStatus,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch } = useGetMyOrganizationInvitesQuery({\n variables: { status, limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invites: (data?.myOrganizationInvites?.items ?? []) as OrganizationInvite[],\n total: data?.myOrganizationInvites?.total ?? 0,\n hasMore: data?.myOrganizationInvites?.hasMore ?? false,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch a SINGLE invite addressed to the current user, by id.\n * Backs the per-invite acceptance page reached from the invite email link.\n * `enabled` defaults to true; pass false to defer until the user is\n * authenticated (the query is self-scoped and 401s for anonymous callers).\n */\nexport function useOrganizationInvite(inviteId: string, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetMyOrganizationInviteQuery({\n variables: { id: inviteId },\n skip: !enabled || !inviteId,\n fetchPolicy: 'network-only',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invite: (data?.myOrganizationInvite ?? null) as OrganizationInvite | null,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to invite a member by email\n */\nexport function useInviteMember() {\n const { emit } = useOrganizationEventEmitter();\n const [inviteMember, { loading, error }] = useInviteOrganizationMemberMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n inviteMember: async (input: { organizationId: string; email: string; message?: string }) => {\n logger.info('mutation.start', {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n });\n try {\n const result = await inviteMember({ variables: { input } });\n const inviteId = result.data?.inviteOrganizationMember?.id;\n emit('organization.invite.created', {\n organizationId: input.organizationId,\n inviteId,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.create_failed', {\n organizationId: input.organizationId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to accept an organization invite\n */\nexport function useAcceptInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [acceptInvite, { loading, error }] = useMutation<\n AcceptOrganizationInviteData,\n AcceptOrganizationInviteVariables\n >(ACCEPT_ORGANIZATION_INVITE_WITH_CAPTCHA, {\n refetchQueries: [GetMyOrganizationInvitesDocument, GetMembersDocument],\n });\n\n return {\n acceptInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const recaptchaToken = await executeRecaptcha('invite_accept');\n const result = await acceptInvite({\n variables: { inviteId, ...(recaptchaToken ? { recaptchaToken } : {}) },\n });\n emit('organization.invite.accepted', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.accept_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to reject an organization invite\n */\nexport function useRejectInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [rejectInvite, { loading, error }] = useRejectOrganizationInviteMutation({\n refetchQueries: [GetMyOrganizationInvitesDocument],\n });\n\n return {\n rejectInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await rejectInvite({ variables: { inviteId } });\n emit('organization.invite.rejected', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.reject_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to cancel an organization invite\n */\nexport function useCancelInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [cancelInvite, { loading, error }] = useCancelOrganizationInviteMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n cancelInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await cancelInvite({ variables: { inviteId } });\n emit('organization.invite.cancelled', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.cancel_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to resend an organization invite\n */\nexport function useResendInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [resendInvite, { loading, error }] = useResendOrganizationInviteMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n resendInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await resendInvite({ variables: { inviteId } });\n emit('organization.invite.resent', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.resend_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useInvites.js","names":["apolloError","ACCEPT_ORGANIZATION_INVITE_WITH_CAPTCHA","extractErrorCode","error","extractErrorMessage","useOrganizationInvites","useGetOrganizationInvitesQuery","organizationId","data","useMyInvites","useGetMyOrganizationInvitesQuery","enabled","useOrganizationInvite","useGetMyOrganizationInviteQuery","inviteId","useInviteMember","useOrganizationEventEmitter","inviteMember","useInviteOrganizationMemberMutation","GetOrganizationInvitesDocument","redactId","input","result","emit","logger","mutationError","errorCode","errorMessage","useAcceptInvite","acceptInvite","useMutation","GetMyOrganizationInvitesDocument","GetMembersDocument","recaptchaToken","useRejectInvite","rejectInvite","useRejectOrganizationInviteMutation","useCancelInvite","cancelInvite","useCancelOrganizationInviteMutation","useResendInvite","resendInvite","useResendOrganizationInviteMutation"],"sources":["../../../../../src/organization-management/modules/members/hooks/useInvites.ts"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n useInviteOrganizationMemberMutation,\n useRejectOrganizationInviteMutation,\n useCancelOrganizationInviteMutation,\n useResendOrganizationInviteMutation,\n useGetOrganizationInvitesQuery,\n useGetMyOrganizationInvitesQuery,\n useGetMyOrganizationInviteQuery,\n GetOrganizationInvitesDocument,\n GetMyOrganizationInvitesDocument,\n GetMembersDocument,\n} from '../../../../generated/global-operations';\nimport type {\n InviteStatus,\n OrganizationInvite,\n OrganizationMember,\n} from '../../../../generated/global-types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\nimport { executeRecaptcha } from '../../../shared/recaptcha';\n\nconst ACCEPT_ORGANIZATION_INVITE_WITH_CAPTCHA = gql`\n mutation AcceptOrganizationInviteWithCaptcha($inviteId: ID!, $recaptchaToken: String) {\n acceptOrganizationInvite(inviteId: $inviteId, recaptchaToken: $recaptchaToken) {\n id\n userId\n organizationId\n role\n joinedAt\n }\n }\n`;\n\ninterface AcceptOrganizationInviteData {\n acceptOrganizationInvite: OrganizationMember;\n}\n\ninterface AcceptOrganizationInviteVariables {\n inviteId: string;\n recaptchaToken?: string;\n}\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organization invites (admin view)\n */\nexport function useOrganizationInvites(\n organizationId: string,\n filter?: { status?: InviteStatus },\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch } = useGetOrganizationInvitesQuery({\n variables: { organizationId, filter, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invites: (data?.organizationInvites?.items ?? []) as OrganizationInvite[],\n total: data?.organizationInvites?.total ?? 0,\n hasMore: data?.organizationInvites?.hasMore ?? false,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch invites for the current user (invitee view).\n *\n * `enabled` defaults to `true` for backward compat. Callers in admin /\n * service contexts (e.g. OrganizationsListPage in `mode=\"admin\"`) should\n * pass `false` to skip the network round-trip and avoid surfacing\n * authorization errors for actors that don't hold the self-scope.\n */\nexport function useMyInvites(\n status?: InviteStatus,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch } = useGetMyOrganizationInvitesQuery({\n variables: { status, limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invites: (data?.myOrganizationInvites?.items ?? []) as OrganizationInvite[],\n total: data?.myOrganizationInvites?.total ?? 0,\n hasMore: data?.myOrganizationInvites?.hasMore ?? false,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch a SINGLE invite addressed to the current user, by id.\n * Backs the per-invite acceptance page reached from the invite email link.\n * `enabled` defaults to true; pass false to defer until the user is\n * authenticated (the query is self-scoped and 401s for anonymous callers).\n */\nexport function useOrganizationInvite(inviteId: string, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetMyOrganizationInviteQuery({\n variables: { id: inviteId },\n skip: !enabled || !inviteId,\n fetchPolicy: 'network-only',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n invite: (data?.myOrganizationInvite ?? null) as OrganizationInvite | null,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to invite a member by email\n */\nexport function useInviteMember() {\n const { emit } = useOrganizationEventEmitter();\n const [inviteMember, { loading, error }] = useInviteOrganizationMemberMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n inviteMember: async (input: { organizationId: string; email: string; message?: string }) => {\n logger.info('mutation.start', {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n });\n try {\n const result = await inviteMember({ variables: { input } });\n const inviteId = result.data?.inviteOrganizationMember?.id;\n emit('organization.invite.created', {\n organizationId: input.organizationId,\n inviteId,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.create_failed', {\n organizationId: input.organizationId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'inviteMember',\n organizationId: redactId(input.organizationId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to accept an organization invite\n */\nexport function useAcceptInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [acceptInvite, { loading, error }] = useMutation<\n AcceptOrganizationInviteData,\n AcceptOrganizationInviteVariables\n >(ACCEPT_ORGANIZATION_INVITE_WITH_CAPTCHA, {\n refetchQueries: [GetMyOrganizationInvitesDocument, GetMembersDocument],\n });\n\n return {\n acceptInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const recaptchaToken = await executeRecaptcha('invite_accept');\n const result = await acceptInvite({\n variables: { inviteId, ...(recaptchaToken ? { recaptchaToken } : {}) },\n });\n emit('organization.invite.accepted', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.accept_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'acceptInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to reject an organization invite\n */\nexport function useRejectInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [rejectInvite, { loading, error }] = useRejectOrganizationInviteMutation({\n refetchQueries: [GetMyOrganizationInvitesDocument],\n });\n\n return {\n rejectInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await rejectInvite({ variables: { inviteId } });\n emit('organization.invite.rejected', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.reject_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'rejectInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to cancel an organization invite\n */\nexport function useCancelInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [cancelInvite, { loading, error }] = useCancelOrganizationInviteMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n cancelInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await cancelInvite({ variables: { inviteId } });\n emit('organization.invite.cancelled', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.cancel_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'cancelInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to resend an organization invite\n */\nexport function useResendInvite() {\n const { emit } = useOrganizationEventEmitter();\n const [resendInvite, { loading, error }] = useResendOrganizationInviteMutation({\n refetchQueries: [GetOrganizationInvitesDocument],\n });\n\n return {\n resendInvite: async (inviteId: string) => {\n logger.info('mutation.start', {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n });\n try {\n const result = await resendInvite({ variables: { inviteId } });\n emit('organization.invite.resent', { inviteId, source: 'microfe-organization' });\n logger.info('mutation.success', {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('organization.invite.resend_failed', {\n inviteId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'resendInvite',\n inviteId: redactId(inviteId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GetMembersDocument as e, useGetMemberQuery as t, useGetMembersQuery as n, useRemoveOrganizationMemberMutation as r } from "../../../../generated/global-operations.js";
|
|
2
2
|
import { useOrganizationEventEmitter as i } from "../../../hooks/useOrganizationEventEmitter.js";
|
|
3
3
|
import { logger as a, redactId as o } from "../../../shared/logger.js";
|
|
4
|
-
|
|
4
|
+
// src/organization-management/modules/members/hooks/useMembers.ts
|
|
5
5
|
function s(e) {
|
|
6
6
|
return e?.graphQLErrors?.[0]?.extensions?.code;
|
|
7
7
|
}
|
|
@@ -89,7 +89,6 @@ function d() {
|
|
|
89
89
|
error: u
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
//#endregion
|
|
93
92
|
export { u as useMember, l as useMembers, d as useRemoveMember };
|
|
94
93
|
|
|
95
94
|
//# sourceMappingURL=useMembers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMembers.js","names":[],"sources":["../../../../../src/organization-management/modules/members/hooks/useMembers.ts"],"sourcesContent":["import {\n useGetMembersQuery,\n useGetMemberQuery,\n useGetMembersByContextQuery,\n useGetMemberByEmailQuery,\n useAddOrganizationMemberMutation,\n useUpdateOrganizationMemberMutation,\n useRemoveOrganizationMemberMutation,\n GetMembersDocument,\n} from '../../../../generated/global-operations';\nimport type { Member, AddMemberInput, UpdateMemberInput } from '../types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organization members with pagination\n * Updated to use offset-based pagination (limit/offset) instead of cursor-based\n */\nexport function useMembers(\n organizationId: string,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMembersQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId || !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.organizationMembers?.items as Member[]) || [],\n total: data?.organizationMembers?.total || 0,\n limit: data?.organizationMembers?.limit || limit,\n offset: data?.organizationMembers?.offset || offset,\n hasMore: data?.organizationMembers?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single member by filtering the members list\n * Note: Backend doesn't have single member query, we filter client-side\n */\nexport function useMember(organizationId: string, userId: string) {\n const { data, loading, error, refetch } = useGetMemberQuery({\n variables: { organizationId },\n skip: !organizationId || !userId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n // Find the member matching the userId from the list\n const member = data?.organizationMembers?.items?.find(\n (m: { userId: string }) => m.userId === userId\n ) as Member | undefined;\n\n return {\n member,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch members by context (organization)\n * Uses the same query as useMembers but with different name for backward compatibility\n */\nexport function useMembersByContext(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMembersByContextQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.organizationMembers?.items as Member[]) || [],\n total: data?.organizationMembers?.total || 0,\n hasMore: data?.organizationMembers?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a member by email (client-side filtering)\n * Note: Backend doesn't have email-based query, we get all and filter\n */\nexport function useMemberByEmail(organizationId: string, email: string) {\n const { loading, error } = useGetMemberByEmailQuery({\n variables: { organizationId },\n skip: !organizationId || !email,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n // Email is not currently exposed by the backend membership payload.\n // Return undefined instead of a misleading first item until a dedicated field/query exists.\n return {\n member: undefined as Member | undefined,\n loading,\n error,\n };\n}\n\n/**\n * Hook to add a new organization member\n */\nexport function useAddMember() {\n const { emit } = useOrganizationEventEmitter();\n const [addMember, { loading, error }] = useAddOrganizationMemberMutation({\n refetchQueries: [GetMembersDocument],\n });\n\n return {\n addMember: async (input: AddMemberInput) => {\n logger.info('mutation.start', {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n });\n try {\n const result = await addMember({ variables: { input } });\n emit('member.added', {\n organizationId: input.organizationId,\n memberId: input.userId,\n role: input.role ?? undefined,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n memberId: redactId(input.userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.add_failed', {\n organizationId: input.organizationId,\n memberId: input.userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n memberId: redactId(input.userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to update an organization member\n */\nexport function useUpdateMember() {\n const { emit } = useOrganizationEventEmitter();\n const [updateMember, { loading, error }] = useUpdateOrganizationMemberMutation();\n\n return {\n updateMember: async (organizationId: string, userId: string, input: UpdateMemberInput) => {\n logger.info('mutation.start', {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n try {\n const result = await updateMember({ variables: { organizationId, userId, input } });\n emit('member.role_changed', {\n organizationId,\n memberId: userId,\n role: input.role ?? undefined,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.role_change_failed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to remove an organization member\n */\nexport function useRemoveMember() {\n const { emit } = useOrganizationEventEmitter();\n const [removeMember, { loading, error }] = useRemoveOrganizationMemberMutation({\n refetchQueries: [GetMembersDocument],\n });\n\n return {\n removeMember: async (organizationId: string, userId: string) => {\n logger.info('mutation.start', {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n try {\n const result = await removeMember({ variables: { organizationId, userId } });\n emit('member.removed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.remove_failed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n"],"mappings":";;;;AA4BA,SAAS,EAAiB,GAAoC;AAE5D,QADoB,GACA,gBAAgB,IAAI,YAAY;;AAGtD,SAAS,EAAoB,GAAwB;AAGnD,QAFI,aAAiB,QAAc,EAAM,UACrB,GACA,WAAW;;AAOjC,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB,IAAmB,IACnB;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAmB;EACtE,WAAW;GAAE;GAAgB;GAAO;GAAQ;EAC5C,MAAM,CAAC,KAAkB,CAAC;EAC1B,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,SAAU,GAAM,qBAAqB,SAAsB,EAAE;EAC7D,OAAO,GAAM,qBAAqB,SAAS;EAC3C,OAAO,GAAM,qBAAqB,SAAS;EAC3C,QAAQ,GAAM,qBAAqB,UAAU;EAC7C,SAAS,GAAM,qBAAqB,WAAW;EAC/C;EACA;EACA;EACA;EACD;;AAOH,SAAgB,EAAU,GAAwB,GAAgB;CAChE,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAkB;EAC1D,WAAW,EAAE,mBAAgB;EAC7B,MAAM,CAAC,KAAkB,CAAC;EAC1B,aAAa;EACb,iBAAiB;EAClB,CAAC;AAOF,QAAO;EACL,QALa,GAAM,qBAAqB,OAAO,MAC9C,MAA0B,EAAE,WAAW,EACzC;EAIC;EACA;EACA;EACD;;AAyKH,SAAgB,IAAkB;CAChC,IAAM,EAAE,YAAS,GAA6B,EACxC,CAAC,GAAc,EAAE,YAAS,cAAW,EAAoC,EAC7E,gBAAgB,CAAC,EAAmB,EACrC,CAAC;AAEF,QAAO;EACL,cAAc,OAAO,GAAwB,MAAmB;AAC9D,KAAO,KAAK,kBAAkB;IAC5B,MAAM;IACN,gBAAgB,EAAS,EAAe;IACxC,UAAU,EAAS,EAAO;IAC3B,CAAC;AACF,OAAI;IACF,IAAM,IAAS,MAAM,EAAa,EAAE,WAAW;KAAE;KAAgB;KAAQ,EAAE,CAAC;AAW5E,WAVA,EAAK,kBAAkB;KACrB;KACA,UAAU;KACV,QAAQ;KACT,CAAC,EACF,EAAO,KAAK,oBAAoB;KAC9B,MAAM;KACN,gBAAgB,EAAS,EAAe;KACxC,UAAU,EAAS,EAAO;KAC3B,CAAC,EACK;YACA,GAAe;IACtB,IAAM,IAAY,EAAiB,EAAc,EAC3C,IAAe,EAAoB,EAAc;AAmBvD,UAlBA,EAAK,wBAAwB;KAC3B;KACA,UAAU;KACV,QAAQ;KACR;KACA;KACD,CAAC,EACF,EAAO,MACL,mBACA;KACE,MAAM;KACN,gBAAgB,EAAS,EAAe;KACxC,UAAU,EAAS,EAAO;KAC1B;KACA;KACD,EACD,EACD,EACK;;;EAGV;EACA;EACD"}
|
|
1
|
+
{"version":3,"file":"useMembers.js","names":["apolloError","extractErrorCode","error","extractErrorMessage","useMembers","useGetMembersQuery","organizationId","enabled","data","limit","offset","useMember","useGetMemberQuery","userId","m","useRemoveMember","useOrganizationEventEmitter","removeMember","useRemoveOrganizationMemberMutation","GetMembersDocument","redactId","result","emit","logger","mutationError","errorCode","errorMessage"],"sources":["../../../../../src/organization-management/modules/members/hooks/useMembers.ts"],"sourcesContent":["import {\n useGetMembersQuery,\n useGetMemberQuery,\n useGetMembersByContextQuery,\n useGetMemberByEmailQuery,\n useAddOrganizationMemberMutation,\n useUpdateOrganizationMemberMutation,\n useRemoveOrganizationMemberMutation,\n GetMembersDocument,\n} from '../../../../generated/global-operations';\nimport type { Member, AddMemberInput, UpdateMemberInput } from '../types';\nimport { useOrganizationEventEmitter } from '../../../hooks/useOrganizationEventEmitter';\nimport { logger, redactId } from '../../../shared/logger';\n\ninterface GraphqlExtensions {\n code?: string;\n}\n\ninterface GraphqlError {\n message?: string;\n extensions?: GraphqlExtensions;\n}\n\ninterface ApolloLikeError {\n message?: string;\n graphQLErrors?: ReadonlyArray<GraphqlError>;\n}\n\nfunction extractErrorCode(error: unknown): string | undefined {\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.graphQLErrors?.[0]?.extensions?.code;\n}\n\nfunction extractErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n const apolloError = error as ApolloLikeError | undefined;\n return apolloError?.message ?? 'Unknown error';\n}\n\n/**\n * Hook to fetch organization members with pagination\n * Updated to use offset-based pagination (limit/offset) instead of cursor-based\n */\nexport function useMembers(\n organizationId: string,\n limit: number = 20,\n offset: number = 0,\n enabled: boolean = true\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMembersQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId || !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.organizationMembers?.items as Member[]) || [],\n total: data?.organizationMembers?.total || 0,\n limit: data?.organizationMembers?.limit || limit,\n offset: data?.organizationMembers?.offset || offset,\n hasMore: data?.organizationMembers?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single member by filtering the members list\n * Note: Backend doesn't have single member query, we filter client-side\n */\nexport function useMember(organizationId: string, userId: string) {\n const { data, loading, error, refetch } = useGetMemberQuery({\n variables: { organizationId },\n skip: !organizationId || !userId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n // Find the member matching the userId from the list\n const member = data?.organizationMembers?.items?.find(\n (m: { userId: string }) => m.userId === userId\n ) as Member | undefined;\n\n return {\n member,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch members by context (organization)\n * Uses the same query as useMembers but with different name for backward compatibility\n */\nexport function useMembersByContext(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch, fetchMore } = useGetMembersByContextQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.organizationMembers?.items as Member[]) || [],\n total: data?.organizationMembers?.total || 0,\n hasMore: data?.organizationMembers?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a member by email (client-side filtering)\n * Note: Backend doesn't have email-based query, we get all and filter\n */\nexport function useMemberByEmail(organizationId: string, email: string) {\n const { loading, error } = useGetMemberByEmailQuery({\n variables: { organizationId },\n skip: !organizationId || !email,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n // Email is not currently exposed by the backend membership payload.\n // Return undefined instead of a misleading first item until a dedicated field/query exists.\n return {\n member: undefined as Member | undefined,\n loading,\n error,\n };\n}\n\n/**\n * Hook to add a new organization member\n */\nexport function useAddMember() {\n const { emit } = useOrganizationEventEmitter();\n const [addMember, { loading, error }] = useAddOrganizationMemberMutation({\n refetchQueries: [GetMembersDocument],\n });\n\n return {\n addMember: async (input: AddMemberInput) => {\n logger.info('mutation.start', {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n });\n try {\n const result = await addMember({ variables: { input } });\n emit('member.added', {\n organizationId: input.organizationId,\n memberId: input.userId,\n role: input.role ?? undefined,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n memberId: redactId(input.userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.add_failed', {\n organizationId: input.organizationId,\n memberId: input.userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'addMember',\n organizationId: redactId(input.organizationId),\n memberId: redactId(input.userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to update an organization member\n */\nexport function useUpdateMember() {\n const { emit } = useOrganizationEventEmitter();\n const [updateMember, { loading, error }] = useUpdateOrganizationMemberMutation();\n\n return {\n updateMember: async (organizationId: string, userId: string, input: UpdateMemberInput) => {\n logger.info('mutation.start', {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n try {\n const result = await updateMember({ variables: { organizationId, userId, input } });\n emit('member.role_changed', {\n organizationId,\n memberId: userId,\n role: input.role ?? undefined,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.role_change_failed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'updateMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n\n/**\n * Hook to remove an organization member\n */\nexport function useRemoveMember() {\n const { emit } = useOrganizationEventEmitter();\n const [removeMember, { loading, error }] = useRemoveOrganizationMemberMutation({\n refetchQueries: [GetMembersDocument],\n });\n\n return {\n removeMember: async (organizationId: string, userId: string) => {\n logger.info('mutation.start', {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n try {\n const result = await removeMember({ variables: { organizationId, userId } });\n emit('member.removed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n });\n logger.info('mutation.success', {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n });\n return result;\n } catch (mutationError) {\n const errorCode = extractErrorCode(mutationError);\n const errorMessage = extractErrorMessage(mutationError);\n emit('member.remove_failed', {\n organizationId,\n memberId: userId,\n source: 'microfe-organization',\n errorCode,\n errorMessage,\n });\n logger.error(\n 'mutation.failed',\n {\n name: 'removeMember',\n organizationId: redactId(organizationId),\n memberId: redactId(userId),\n errorCode,\n errorMessage,\n },\n mutationError\n );\n throw mutationError;\n }\n },\n loading,\n error,\n };\n}\n"],"mappings":";;;;AA4BA,SAASC,EAAiB,GAAoC;CAE5D,OAAOD,GAAa,gBAAgB,EAAE,EAAE,YAAY;AACtD;AAEA,SAASG,EAAoB,GAAwB;CAGnD,OAFID,aAAiB,QAAcA,EAAM,UAElCF,GAAa,WAAW;AACjC;AAMA,SAAgBI,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB,IAAmB,IACnB;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAcC,EAAmB;EACtE,WAAW;GAAE;GAAgB;GAAO;EAAO;EAC3C,MAAM,CAACC,KAAkB,CAACC;EAC1B,aAAa;EACb,iBAAiB;CACnB,CAAC;CAED,OAAO;EACL,SAAUC,GAAM,qBAAqB,SAAsB,CAAC;EAC5D,OAAOA,GAAM,qBAAqB,SAAS;EAC3C,OAAOA,GAAM,qBAAqB,SAASC;EAC3C,QAAQD,GAAM,qBAAqB,UAAUE;EAC7C,SAASF,GAAM,qBAAqB,WAAW;EAC/C;EACA;EACA;EACA;CACF;AACF;AAMA,SAAgBG,EAAU,GAAwB,GAAgB;CAChE,IAAM,EAAE,SAAM,YAAS,UAAO,eAAYC,EAAkB;EAC1D,WAAW,EAAE,kBAAe;EAC5B,MAAM,CAACN,KAAkB,CAACO;EAC1B,aAAa;EACb,iBAAiB;CACnB,CAAC;CAOD,OAAO;EACL,QALaL,GAAM,qBAAqB,OAAO,MAC9C,MAA0BM,EAAE,WAAWD,CAC1C;EAIE;EACA;EACA;CACF;AACF;AAwKA,SAAgBE,IAAkB;CAChC,IAAM,EAAE,YAASC,EAA4B,GACvC,CAACC,GAAc,EAAE,YAAS,cAAWC,EAAoC,EAC7E,gBAAgB,CAACC,CAAkB,EACrC,CAAC;CAED,OAAO;EACL,cAAc,OAAO,GAAwB,MAAmB;GAC9D,EAAO,KAAK,kBAAkB;IAC5B,MAAM;IACN,gBAAgBC,EAASd,CAAc;IACvC,UAAUc,EAASP,CAAM;GAC3B,CAAC;GACD,IAAI;IACF,IAAMQ,IAAS,MAAMJ,EAAa,EAAE,WAAW;KAAE;KAAgB;IAAO,EAAE,CAAC;IAW3E,OAVAK,EAAK,kBAAkB;KACrB;KACA,UAAUT;KACV,QAAQ;IACV,CAAC,GACDU,EAAO,KAAK,oBAAoB;KAC9B,MAAM;KACN,gBAAgBH,EAASd,CAAc;KACvC,UAAUc,EAASP,CAAM;IAC3B,CAAC,GACMQ;GACT,SAASG,GAAe;IACtB,IAAMC,IAAYxB,EAAiBuB,CAAa,GAC1CE,IAAevB,EAAoBqB,CAAa;IAmBtD,MAlBAF,EAAK,wBAAwB;KAC3B;KACA,UAAUT;KACV,QAAQ;KACR;KACA;IACF,CAAC,GACDU,EAAO,MACL,mBACA;KACE,MAAM;KACN,gBAAgBH,EAASd,CAAc;KACvC,UAAUc,EAASP,CAAM;KACzB;KACA;IACF,GACAW,CACF,GACMA;GACR;EACF;EACA;EACA;CACF;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
|
|
2
2
|
import { useCallback as t, useEffect as n, useMemo as r, useRef as i, useState as a } from "react";
|
|
3
3
|
import { graphqlFetch as o } from "@burdenoff/fe-libs/shared/graphql";
|
|
4
|
-
|
|
4
|
+
// src/organization-management/modules/members/hooks/useWorkspaceInvites.ts
|
|
5
5
|
var s = "\n query GetMyPendingWorkspaceInvites {\n myPendingInvitations {\n id\n email\n status\n roleIds\n workspaceId\n invitedById\n expiresAt\n createdAt\n projectId\n invitedUserId\n updatedAt\n deletedAt\n context {\n type\n metadata\n }\n }\n }\n", c = "\n mutation AcceptWorkspaceInvite($id: ID!) {\n acceptInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n", l = "\n mutation RejectWorkspaceInvite($id: ID!) {\n rejectInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n";
|
|
6
6
|
function u(e, t) {
|
|
7
7
|
return e instanceof Error ? e : Error(t);
|
|
@@ -112,7 +112,6 @@ function m() {
|
|
|
112
112
|
error: s
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
|
-
//#endregion
|
|
116
115
|
export { p as useAcceptWorkspaceInvite, f as useMyWorkspaceInvites, m as useRejectWorkspaceInvite };
|
|
117
116
|
|
|
118
117
|
//# sourceMappingURL=useWorkspaceInvites.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWorkspaceInvites.js","names":[],"sources":["../../../../../src/organization-management/modules/members/hooks/useWorkspaceInvites.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { Invitation } from '../../../../generated/wspace-types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\n\nconst GET_MY_PENDING_WORKSPACE_INVITES = `\n query GetMyPendingWorkspaceInvites {\n myPendingInvitations {\n id\n email\n status\n roleIds\n workspaceId\n invitedById\n expiresAt\n createdAt\n projectId\n invitedUserId\n updatedAt\n deletedAt\n context {\n type\n metadata\n }\n }\n }\n`;\n\nconst ACCEPT_WORKSPACE_INVITE = `\n mutation AcceptWorkspaceInvite($id: ID!) {\n acceptInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\nconst REJECT_WORKSPACE_INVITE = `\n mutation RejectWorkspaceInvite($id: ID!) {\n rejectInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\ninterface WorkspaceInvitesResponse {\n myPendingInvitations: Invitation[];\n}\n\ninterface WorkspaceInviteMutationResponse {\n acceptInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n rejectInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n}\n\nfunction toError(error: unknown, fallback: string) {\n return error instanceof Error ? error : new Error(fallback);\n}\n\nfunction isAbortLikeError(error: unknown) {\n if (!(error instanceof Error)) {\n return false;\n }\n\n return error.name === 'AbortError' || error.message.toLowerCase().includes('signal is aborted');\n}\n\nexport function useMyWorkspaceInvites(email?: string, enabled: boolean = true) {\n const { authToken } = useOrgConsole();\n const shouldFetch = enabled && Boolean(email);\n const [invites, setInvites] = useState<Invitation[]>([]);\n const [loading, setLoading] = useState(shouldFetch);\n const [error, setError] = useState<Error | null>(null);\n const requestIdRef = useRef(0);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const loadInvites = useCallback(\n async (signal?: AbortSignal) => {\n if (!enabled || !email) {\n setInvites([]);\n setError(null);\n setLoading(false);\n return [] as Invitation[];\n }\n\n const requestId = ++requestIdRef.current;\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInvitesResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: GET_MY_PENDING_WORKSPACE_INVITES,\n signal,\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n if (signal?.aborted) {\n return [] as Invitation[];\n }\n\n throw new Error(result.errors[0]?.message || 'Failed to fetch workspace invites');\n }\n\n const nextInvites = result.data?.myPendingInvitations ?? [];\n if (requestIdRef.current === requestId) {\n setInvites(nextInvites);\n }\n return nextInvites;\n } catch (fetchError) {\n if (signal?.aborted || isAbortLikeError(fetchError)) {\n return [] as Invitation[];\n }\n\n const normalizedError = toError(fetchError, 'Failed to fetch workspace invites');\n if (requestIdRef.current === requestId) {\n setError(normalizedError);\n setInvites([]);\n }\n throw normalizedError;\n } finally {\n if (requestIdRef.current === requestId) {\n setLoading(false);\n }\n }\n },\n [authToken, email, enabled]\n );\n\n useEffect(() => {\n const abortController = new AbortController();\n abortControllerRef.current?.abort();\n abortControllerRef.current = abortController;\n\n void loadInvites(abortController.signal).catch(() => undefined);\n\n return () => {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n abortController.abort();\n };\n }, [loadInvites]);\n\n const isLoading =\n loading ||\n (shouldFetch && invites.length === 0 && error === null && requestIdRef.current === 0);\n\n return useMemo(\n () => ({\n invites,\n total: invites.length,\n loading: isLoading,\n error,\n refetch: loadInvites,\n }),\n [error, invites, isLoading, loadInvites]\n );\n}\n\nexport function useAcceptWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const acceptInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: ACCEPT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to accept workspace invite');\n }\n\n return result.data?.acceptInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to accept workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { acceptInvite, loading, error };\n}\n\nexport function useRejectWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rejectInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: REJECT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to reject workspace invite');\n }\n\n return result.data?.rejectInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to reject workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { rejectInvite, loading, error };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useWorkspaceInvites.js","names":["GET_MY_PENDING_WORKSPACE_INVITES","ACCEPT_WORKSPACE_INVITE","REJECT_WORKSPACE_INVITE","toError","error","fallback","isAbortLikeError","useMyWorkspaceInvites","useOrgConsole","shouldFetch","enabled","email","invites","setInvites","useState","loading","setLoading","setError","requestIdRef","useRef","abortControllerRef","loadInvites","useCallback","requestId","result","graphqlFetch","authToken","signal","nextInvites","fetchError","normalizedError","abortController","isLoading","useMemo","useAcceptWorkspaceInvite","inviteId","mutationError","useRejectWorkspaceInvite"],"sources":["../../../../../src/organization-management/modules/members/hooks/useWorkspaceInvites.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { Invitation } from '../../../../generated/wspace-types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\n\nconst GET_MY_PENDING_WORKSPACE_INVITES = `\n query GetMyPendingWorkspaceInvites {\n myPendingInvitations {\n id\n email\n status\n roleIds\n workspaceId\n invitedById\n expiresAt\n createdAt\n projectId\n invitedUserId\n updatedAt\n deletedAt\n context {\n type\n metadata\n }\n }\n }\n`;\n\nconst ACCEPT_WORKSPACE_INVITE = `\n mutation AcceptWorkspaceInvite($id: ID!) {\n acceptInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\nconst REJECT_WORKSPACE_INVITE = `\n mutation RejectWorkspaceInvite($id: ID!) {\n rejectInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\ninterface WorkspaceInvitesResponse {\n myPendingInvitations: Invitation[];\n}\n\ninterface WorkspaceInviteMutationResponse {\n acceptInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n rejectInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n}\n\nfunction toError(error: unknown, fallback: string) {\n return error instanceof Error ? error : new Error(fallback);\n}\n\nfunction isAbortLikeError(error: unknown) {\n if (!(error instanceof Error)) {\n return false;\n }\n\n return error.name === 'AbortError' || error.message.toLowerCase().includes('signal is aborted');\n}\n\nexport function useMyWorkspaceInvites(email?: string, enabled: boolean = true) {\n const { authToken } = useOrgConsole();\n const shouldFetch = enabled && Boolean(email);\n const [invites, setInvites] = useState<Invitation[]>([]);\n const [loading, setLoading] = useState(shouldFetch);\n const [error, setError] = useState<Error | null>(null);\n const requestIdRef = useRef(0);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const loadInvites = useCallback(\n async (signal?: AbortSignal) => {\n if (!enabled || !email) {\n setInvites([]);\n setError(null);\n setLoading(false);\n return [] as Invitation[];\n }\n\n const requestId = ++requestIdRef.current;\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInvitesResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: GET_MY_PENDING_WORKSPACE_INVITES,\n signal,\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n if (signal?.aborted) {\n return [] as Invitation[];\n }\n\n throw new Error(result.errors[0]?.message || 'Failed to fetch workspace invites');\n }\n\n const nextInvites = result.data?.myPendingInvitations ?? [];\n if (requestIdRef.current === requestId) {\n setInvites(nextInvites);\n }\n return nextInvites;\n } catch (fetchError) {\n if (signal?.aborted || isAbortLikeError(fetchError)) {\n return [] as Invitation[];\n }\n\n const normalizedError = toError(fetchError, 'Failed to fetch workspace invites');\n if (requestIdRef.current === requestId) {\n setError(normalizedError);\n setInvites([]);\n }\n throw normalizedError;\n } finally {\n if (requestIdRef.current === requestId) {\n setLoading(false);\n }\n }\n },\n [authToken, email, enabled]\n );\n\n useEffect(() => {\n const abortController = new AbortController();\n abortControllerRef.current?.abort();\n abortControllerRef.current = abortController;\n\n void loadInvites(abortController.signal).catch(() => undefined);\n\n return () => {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n abortController.abort();\n };\n }, [loadInvites]);\n\n const isLoading =\n loading ||\n (shouldFetch && invites.length === 0 && error === null && requestIdRef.current === 0);\n\n return useMemo(\n () => ({\n invites,\n total: invites.length,\n loading: isLoading,\n error,\n refetch: loadInvites,\n }),\n [error, invites, isLoading, loadInvites]\n );\n}\n\nexport function useAcceptWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const acceptInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: ACCEPT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to accept workspace invite');\n }\n\n return result.data?.acceptInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to accept workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { acceptInvite, loading, error };\n}\n\nexport function useRejectWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rejectInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: REJECT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to reject workspace invite');\n }\n\n return result.data?.rejectInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to reject workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { rejectInvite, loading, error };\n}\n"],"mappings":"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"}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { toast as e } from "../../../../node_modules/sonner/dist/index.js";
|
|
2
|
+
import { useOrgConsole as t } from "../../../providers/OrgConsoleProvider.js";
|
|
3
3
|
import { useInviteMember as n } from "../hooks/useInvites.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
4
|
+
import { InviteMemberForm as r } from "../components/InviteMemberForm.js";
|
|
5
|
+
import { BulkInviteForm as i } from "../components/BulkInviteForm.js";
|
|
6
|
+
import { PendingInvitesList as a } from "../components/PendingInvitesList.js";
|
|
7
|
+
import { LoadingState as o } from "../../../shared/components/feedback/LoadingState.js";
|
|
8
|
+
import { PERMISSIONS as s } from "../../../constants/permissions.js";
|
|
9
|
+
import { useTr as c } from "../../../shared/hooks/useTr.js";
|
|
10
10
|
import { useState as l } from "react";
|
|
11
11
|
import { ArrowLeft as u, UserPlus as d } from "lucide-react";
|
|
12
12
|
import { GlassCard as f, PagePurpose as p, ShowMore as m } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
import { usePermissions as h } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
14
14
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
15
15
|
import { useParams as v } from "react-router";
|
|
16
|
-
|
|
16
|
+
// src/organization-management/modules/members/pages/InviteMemberPage.tsx
|
|
17
17
|
function y() {
|
|
18
|
-
let y =
|
|
19
|
-
await T(
|
|
18
|
+
let y = c(), { organizationId: b } = v(), { navigate: x, basePath: S = "/organizations" } = t(), { hasPermission: C, isLoading: w } = h(), { inviteMember: T, loading: E, error: D } = n(), [O, k] = l("single"), A = async (t) => {
|
|
19
|
+
await T(t), e.success(`${y("members.invite.success", "Invitation sent to")} ${t.email}`);
|
|
20
20
|
}, j = () => {
|
|
21
21
|
x(`${S}/${b}/members`);
|
|
22
22
|
};
|
|
23
23
|
return w ? /* @__PURE__ */ g("div", {
|
|
24
24
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
25
|
-
children: /* @__PURE__ */ g(
|
|
26
|
-
}) : C(
|
|
25
|
+
children: /* @__PURE__ */ g(o, { message: y("common.loadingPermissions", "Loading permissions...") })
|
|
26
|
+
}) : C(s.MEMBER_INVITE) ? /* @__PURE__ */ g("div", {
|
|
27
27
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
28
28
|
children: /* @__PURE__ */ _("div", {
|
|
29
29
|
className: "mx-auto max-w-3xl space-y-8",
|
|
@@ -90,12 +90,12 @@ function y() {
|
|
|
90
90
|
children: D.message.includes("CONFLICT") ? y("members.invite.error.conflict", "An invite already exists for this email address.") : D.message.includes("already a member") ? y("members.invite.error.alreadyMember", "This user is already a member of the organization.") : D.message
|
|
91
91
|
})
|
|
92
92
|
}),
|
|
93
|
-
O === "single" ? /* @__PURE__ */ g(
|
|
93
|
+
O === "single" ? /* @__PURE__ */ g(r, {
|
|
94
94
|
organizationId: b,
|
|
95
95
|
onSubmit: A,
|
|
96
96
|
onCancel: j,
|
|
97
97
|
loading: E
|
|
98
|
-
}) : /* @__PURE__ */ g(
|
|
98
|
+
}) : /* @__PURE__ */ g(i, {
|
|
99
99
|
organizationId: b,
|
|
100
100
|
inviteMember: T,
|
|
101
101
|
onCancel: j,
|
|
@@ -108,7 +108,7 @@ function y() {
|
|
|
108
108
|
className: "border-border-seam p-6",
|
|
109
109
|
children: /* @__PURE__ */ g(m, {
|
|
110
110
|
collapsedHeight: 260,
|
|
111
|
-
children: /* @__PURE__ */ g(
|
|
111
|
+
children: /* @__PURE__ */ g(a, { organizationId: b })
|
|
112
112
|
})
|
|
113
113
|
})
|
|
114
114
|
]
|
|
@@ -121,7 +121,6 @@ function y() {
|
|
|
121
121
|
})
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
//#endregion
|
|
125
124
|
export { y as InviteMemberPage };
|
|
126
125
|
|
|
127
126
|
//# sourceMappingURL=InviteMemberPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { GlassCard, PagePurpose, ShowMore } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useInviteMember } from '../hooks/useInvites';\nimport { InviteMemberForm } from '../components/InviteMemberForm';\nimport { BulkInviteForm } from '../components/BulkInviteForm';\nimport { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ntype InviteMode = 'single' | 'bulk';\n\n/**\n * Invite Member Page\n * Email-based invite form with pending invites list below\n */\nexport function InviteMemberPage() {\n const tr = useTr();\n const { organizationId } = useParams<{ organizationId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { inviteMember, loading, error } = useInviteMember();\n const [mode, setMode] = useState<InviteMode>('single');\n\n const handleSubmit = async (data: {\n organizationId: string;\n email: string;\n message?: string;\n }) => {\n await inviteMember(data);\n toast.success(`${tr('members.invite.success', 'Invitation sent to')} ${data.email}`);\n };\n\n const handleCancel = () => {\n navigate(`${basePath}/${organizationId}/members`);\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_INVITE)) {\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.invite.noPermission', \"You don't have permission to invite members.\")}\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=\"mx-auto max-w-3xl space-y-8\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg p-2 transition-colors hover:bg-[var(--color-bg-subtle)]\"\n aria-label={tr('members.invite.backToMembers', 'Back to members')}\n >\n <ArrowLeft size={20} className=\"text-[var(--color-text-secondary)]\" />\n </button>\n <div className=\"flex items-center gap-2\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invite.title', 'Invite Member')}\n </h1>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-2xl\">\n {tr(\n 'members.invite.purpose',\n 'Send someone an invitation to join this organization. Invite one person by email, or upload a CSV to invite a whole team at once — invitees accept from their own account and instantly gain access to the organization.'\n )}\n </PagePurpose>\n\n {/* Mode toggle */}\n <div\n className=\"inline-flex rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-1\"\n role=\"tablist\"\n aria-label={tr('members.invite.modeToggle', 'Invite mode')}\n >\n {(['single', 'bulk'] as const).map((m) => {\n const isActive = mode === m;\n const label =\n m === 'single'\n ? tr('members.invite.modeSingle', 'Single')\n : tr('members.invite.modeBulk', 'Bulk (CSV)');\n return (\n <button\n key={m}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setMode(m)}\n className={`min-h-9 rounded-md px-4 py-1.5 text-sm font-medium transition-colors ${\n isActive\n ? 'bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]'\n : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n >\n {label}\n </button>\n );\n })}\n </div>\n\n {/* Invite Form Card */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {mode === 'single'\n ? tr('members.invite.send', 'Send Invite')\n : tr('members.bulkInvite.title', 'Bulk Invite from CSV')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-muted)] mb-6\">\n {mode === 'single'\n ? tr(\n 'members.invite.description',\n 'Enter the email address of the person you want to invite. They will receive an email notification and can accept or decline from their account.'\n )\n : tr(\n 'members.bulkInvite.pageDescription',\n 'Upload a CSV to invite many people at once. Download the template below to see the expected format.'\n )}\n </p>\n\n {mode === 'single' && error && (\n <div className=\"mb-4 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error.message.includes('CONFLICT')\n ? tr(\n 'members.invite.error.conflict',\n 'An invite already exists for this email address.'\n )\n : error.message.includes('already a member')\n ? tr(\n 'members.invite.error.alreadyMember',\n 'This user is already a member of the organization.'\n )\n : error.message}\n </p>\n </div>\n )}\n\n {mode === 'single' ? (\n <InviteMemberForm\n organizationId={organizationId!}\n onSubmit={handleSubmit}\n onCancel={handleCancel}\n loading={loading}\n />\n ) : (\n <BulkInviteForm\n organizationId={organizationId!}\n inviteMember={inviteMember}\n onCancel={handleCancel}\n loading={loading}\n />\n )}\n </GlassCard>\n\n {/* Pending Invites */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6\">\n <ShowMore collapsedHeight={260}>\n <PendingInvitesList organizationId={organizationId!} />\n </ShowMore>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InviteMemberPage.js","names":["InviteMemberPage","tr","useTr","useParams","useOrgConsole","permissionsLoading","usePermissions","useInviteMember","mode","setMode","useState","handleSubmit","inviteMember","data","toast","handleCancel","basePath","organizationId","LoadingState","hasPermission","PERMISSIONS","ArrowLeft","UserPlus","PagePurpose","isActive","m","label","GlassCard","error","InviteMemberForm","BulkInviteForm","ShowMore","PendingInvitesList"],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { GlassCard, PagePurpose, ShowMore } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useInviteMember } from '../hooks/useInvites';\nimport { InviteMemberForm } from '../components/InviteMemberForm';\nimport { BulkInviteForm } from '../components/BulkInviteForm';\nimport { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ntype InviteMode = 'single' | 'bulk';\n\n/**\n * Invite Member Page\n * Email-based invite form with pending invites list below\n */\nexport function InviteMemberPage() {\n const tr = useTr();\n const { organizationId } = useParams<{ organizationId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { inviteMember, loading, error } = useInviteMember();\n const [mode, setMode] = useState<InviteMode>('single');\n\n const handleSubmit = async (data: {\n organizationId: string;\n email: string;\n message?: string;\n }) => {\n await inviteMember(data);\n toast.success(`${tr('members.invite.success', 'Invitation sent to')} ${data.email}`);\n };\n\n const handleCancel = () => {\n navigate(`${basePath}/${organizationId}/members`);\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_INVITE)) {\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.invite.noPermission', \"You don't have permission to invite members.\")}\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=\"mx-auto max-w-3xl space-y-8\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg p-2 transition-colors hover:bg-[var(--color-bg-subtle)]\"\n aria-label={tr('members.invite.backToMembers', 'Back to members')}\n >\n <ArrowLeft size={20} className=\"text-[var(--color-text-secondary)]\" />\n </button>\n <div className=\"flex items-center gap-2\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invite.title', 'Invite Member')}\n </h1>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-2xl\">\n {tr(\n 'members.invite.purpose',\n 'Send someone an invitation to join this organization. Invite one person by email, or upload a CSV to invite a whole team at once — invitees accept from their own account and instantly gain access to the organization.'\n )}\n </PagePurpose>\n\n {/* Mode toggle */}\n <div\n className=\"inline-flex rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-1\"\n role=\"tablist\"\n aria-label={tr('members.invite.modeToggle', 'Invite mode')}\n >\n {(['single', 'bulk'] as const).map((m) => {\n const isActive = mode === m;\n const label =\n m === 'single'\n ? tr('members.invite.modeSingle', 'Single')\n : tr('members.invite.modeBulk', 'Bulk (CSV)');\n return (\n <button\n key={m}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setMode(m)}\n className={`min-h-9 rounded-md px-4 py-1.5 text-sm font-medium transition-colors ${\n isActive\n ? 'bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]'\n : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n >\n {label}\n </button>\n );\n })}\n </div>\n\n {/* Invite Form Card */}\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {mode === 'single'\n ? tr('members.invite.send', 'Send Invite')\n : tr('members.bulkInvite.title', 'Bulk Invite from CSV')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-muted)] mb-6\">\n {mode === 'single'\n ? tr(\n 'members.invite.description',\n 'Enter the email address of the person you want to invite. They will receive an email notification and can accept or decline from their account.'\n )\n : tr(\n 'members.bulkInvite.pageDescription',\n 'Upload a CSV to invite many people at once. Download the template below to see the expected format.'\n )}\n </p>\n\n {mode === 'single' && error && (\n <div className=\"mb-4 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error.message.includes('CONFLICT')\n ? tr(\n 'members.invite.error.conflict',\n 'An invite already exists for this email address.'\n )\n : error.message.includes('already a member')\n ? tr(\n 'members.invite.error.alreadyMember',\n 'This user is already a member of the organization.'\n )\n : error.message}\n </p>\n </div>\n )}\n\n {mode === 'single' ? (\n <InviteMemberForm\n organizationId={organizationId!}\n onSubmit={handleSubmit}\n onCancel={handleCancel}\n loading={loading}\n />\n ) : (\n <BulkInviteForm\n organizationId={organizationId!}\n inviteMember={inviteMember}\n onCancel={handleCancel}\n loading={loading}\n />\n )}\n </GlassCard>\n\n {/* Pending Invites */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6\">\n <ShowMore collapsedHeight={260}>\n <PendingInvitesList organizationId={organizationId!} />\n </ShowMore>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
|
|
2
2
|
import { useMyOrganizations as t } from "../../organizations/hooks/useOrganizations.js";
|
|
3
|
-
import {
|
|
3
|
+
import { useOrgsWithPermission as n } from "../../organizations/hooks/useOrgsWithPermission.js";
|
|
4
4
|
import { LoadingState as r } from "../../../shared/components/feedback/LoadingState.js";
|
|
5
5
|
import { ErrorState as i } from "../../../shared/components/feedback/ErrorState.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { useTr as a } from "../../../shared/hooks/useTr.js";
|
|
7
|
+
import { useOrganizationPageView as o } from "../../../hooks/useOrganizationPageView.js";
|
|
8
8
|
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
9
9
|
import { Building as u, ChevronRight as d, UserPlus as f } from "lucide-react";
|
|
10
10
|
import { GlassCard as p, IllustratedEmptyState as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
12
|
-
|
|
12
|
+
// src/organization-management/modules/members/pages/InvitePeoplePage.tsx
|
|
13
13
|
function v() {
|
|
14
|
-
|
|
15
|
-
let v =
|
|
14
|
+
o("organizations.invite-people");
|
|
15
|
+
let v = a(), { navigate: y, basePath: b = "/organizations" } = e(), { organizations: x, loading: S, error: C } = t(100, 0), w = c(() => x.map((e) => e.id), [x]), { allowedIds: T, loading: E, isError: D } = n(w, "member", "create"), O = c(() => x.filter((e) => T.has(e.id)), [x, T]), [k, A] = l(!1);
|
|
16
16
|
return s(() => {
|
|
17
|
-
|
|
17
|
+
k || S || E || O.length === 1 && (A(!0), y(`${b}/${O[0].id}/members/invite`));
|
|
18
18
|
}, [
|
|
19
|
-
|
|
19
|
+
k,
|
|
20
20
|
S,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
E,
|
|
22
|
+
O,
|
|
23
23
|
y,
|
|
24
24
|
b
|
|
25
|
-
]), S ||
|
|
25
|
+
]), S || E ? /* @__PURE__ */ g("div", {
|
|
26
26
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
27
27
|
children: /* @__PURE__ */ g(r, { message: v("common.loading", "Loading...") })
|
|
28
|
-
}) : C ||
|
|
28
|
+
}) : C || D ? /* @__PURE__ */ g("div", {
|
|
29
29
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
30
30
|
children: /* @__PURE__ */ g(i, { message: C?.message || v("members.invitePeople.error", "Failed to load organizations for invite.") })
|
|
31
31
|
}) : /* @__PURE__ */ g("div", {
|
|
@@ -52,7 +52,7 @@ function v() {
|
|
|
52
52
|
children: v("members.invitePeople.purpose", "This is the quickest way to bring a new person on board — choose which organization to invite them into, then send the invite. They will get an invitation they can accept to join and start collaborating.")
|
|
53
53
|
})
|
|
54
54
|
] })]
|
|
55
|
-
}),
|
|
55
|
+
}), O.length === 0 ? /* @__PURE__ */ g(p, {
|
|
56
56
|
treatment: "glass",
|
|
57
57
|
glow: !0,
|
|
58
58
|
className: "border-border-seam p-6",
|
|
@@ -63,7 +63,7 @@ function v() {
|
|
|
63
63
|
})
|
|
64
64
|
}) : /* @__PURE__ */ g("ul", {
|
|
65
65
|
className: "divide-y divide-[var(--color-border-seam)] overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1",
|
|
66
|
-
children:
|
|
66
|
+
children: O.map((e) => /* @__PURE__ */ g("li", { children: /* @__PURE__ */ _("button", {
|
|
67
67
|
type: "button",
|
|
68
68
|
onClick: () => y(`${b}/${e.id}/members/invite`),
|
|
69
69
|
className: "flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-[var(--color-bg-subtle)]",
|
|
@@ -95,7 +95,6 @@ function v() {
|
|
|
95
95
|
})
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
|
-
//#endregion
|
|
99
98
|
export { v as InvitePeoplePage };
|
|
100
99
|
|
|
101
100
|
//# sourceMappingURL=InvitePeoplePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvitePeoplePage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InvitePeoplePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building, ChevronRight, UserPlus } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMyOrganizations } from '../../organizations/hooks/useOrganizations';\nimport { useOrgsWithPermission } from '../../organizations/hooks/useOrgsWithPermission';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\n/**\n * InvitePeoplePage\n *\n * Globally-reachable entry point (sidebar nav item) for inviting users.\n * Lists the orgs the current user can invite into, then deep-links into\n * each org's existing `members/invite` page so the invite flow itself\n * remains a single source of truth.\n *\n * If the user has exactly one org with invite permission, redirect\n * straight to that org's invite page — saves a click for the common case.\n */\nexport function InvitePeoplePage() {\n useOrganizationPageView('organizations.invite-people');\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { organizations, loading, error } = useMyOrganizations(100, 0);\n\n const orgIds = useMemo(() => organizations.map((o) => o.id), [organizations]);\n const {\n allowedIds,\n loading: permLoading,\n isError: permError,\n } = useOrgsWithPermission(orgIds, 'member', 'create');\n\n const invitable = useMemo(\n () => organizations.filter((o) => allowedIds.has(o.id)),\n [organizations, allowedIds]\n );\n\n const [autoRedirected, setAutoRedirected] = useState(false);\n useEffect(() => {\n if (autoRedirected) return;\n if (loading || permLoading) return;\n if (invitable.length === 1) {\n setAutoRedirected(true);\n navigate(`${basePath}/${invitable[0].id}/members/invite`);\n }\n }, [autoRedirected, loading, permLoading, invitable, navigate, basePath]);\n\n if (loading || permLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loading', 'Loading...')} />\n </div>\n );\n }\n\n if (error || permError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <ErrorState\n message={\n error?.message ||\n tr('members.invitePeople.error', 'Failed to load organizations for invite.')\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 max-w-3xl space-y-6\">\n <div className=\"flex items-center gap-3\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" aria-hidden=\"true\" />\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invitePeople.title', 'Invite People')}\n </h1>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'members.invitePeople.subtitle',\n 'Pick an organization to send an invite into. You can also invite from any organization or workspace detail page.'\n )}\n </p>\n <PagePurpose className=\"mt-2 max-w-2xl\">\n {tr(\n 'members.invitePeople.purpose',\n 'This is the quickest way to bring a new person on board — choose which organization to invite them into, then send the invite. They will get an invitation they can accept to join and start collaborating.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n {invitable.length === 0 ? (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-6\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tr('members.invitePeople.emptyTitle', 'No organizations where you can invite')}\n description={tr(\n 'members.invitePeople.emptyDescription',\n \"You don't currently have permission to invite members into any organization. Ask an organization admin to grant you invite access.\"\n )}\n />\n </GlassCard>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)] overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1\">\n {invitable.map((org) => (\n <li key={org.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${org.id}/members/invite`)}\n className=\"flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Building size={20} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-[var(--color-text-primary)]\">\n {org.name}\n </p>\n <p className=\"truncate font-mono text-xs text-[var(--color-text-secondary)]\">\n /{org.slug}\n </p>\n </div>\n </div>\n <ChevronRight\n size={18}\n className=\"shrink-0 text-[var(--color-text-secondary)]\"\n aria-hidden=\"true\"\n />\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,
|
|
1
|
+
{"version":3,"file":"InvitePeoplePage.js","names":["InvitePeoplePage","tr","useTr","useOrgConsole","useMyOrganizations","orgIds","useMemo","organizations","o","permLoading","permError","useOrgsWithPermission","invitable","allowedIds","autoRedirected","setAutoRedirected","useState","useEffect","loading","navigate","basePath","LoadingState","error","ErrorState","UserPlus","PagePurpose","GlassCard","IllustratedEmptyState","org","Building","ChevronRight"],"sources":["../../../../../src/organization-management/modules/members/pages/InvitePeoplePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building, ChevronRight, UserPlus } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMyOrganizations } from '../../organizations/hooks/useOrganizations';\nimport { useOrgsWithPermission } from '../../organizations/hooks/useOrgsWithPermission';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\n/**\n * InvitePeoplePage\n *\n * Globally-reachable entry point (sidebar nav item) for inviting users.\n * Lists the orgs the current user can invite into, then deep-links into\n * each org's existing `members/invite` page so the invite flow itself\n * remains a single source of truth.\n *\n * If the user has exactly one org with invite permission, redirect\n * straight to that org's invite page — saves a click for the common case.\n */\nexport function InvitePeoplePage() {\n useOrganizationPageView('organizations.invite-people');\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { organizations, loading, error } = useMyOrganizations(100, 0);\n\n const orgIds = useMemo(() => organizations.map((o) => o.id), [organizations]);\n const {\n allowedIds,\n loading: permLoading,\n isError: permError,\n } = useOrgsWithPermission(orgIds, 'member', 'create');\n\n const invitable = useMemo(\n () => organizations.filter((o) => allowedIds.has(o.id)),\n [organizations, allowedIds]\n );\n\n const [autoRedirected, setAutoRedirected] = useState(false);\n useEffect(() => {\n if (autoRedirected) return;\n if (loading || permLoading) return;\n if (invitable.length === 1) {\n setAutoRedirected(true);\n navigate(`${basePath}/${invitable[0].id}/members/invite`);\n }\n }, [autoRedirected, loading, permLoading, invitable, navigate, basePath]);\n\n if (loading || permLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loading', 'Loading...')} />\n </div>\n );\n }\n\n if (error || permError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <ErrorState\n message={\n error?.message ||\n tr('members.invitePeople.error', 'Failed to load organizations for invite.')\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 max-w-3xl space-y-6\">\n <div className=\"flex items-center gap-3\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" aria-hidden=\"true\" />\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invitePeople.title', 'Invite People')}\n </h1>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'members.invitePeople.subtitle',\n 'Pick an organization to send an invite into. You can also invite from any organization or workspace detail page.'\n )}\n </p>\n <PagePurpose className=\"mt-2 max-w-2xl\">\n {tr(\n 'members.invitePeople.purpose',\n 'This is the quickest way to bring a new person on board — choose which organization to invite them into, then send the invite. They will get an invitation they can accept to join and start collaborating.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n {invitable.length === 0 ? (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-6\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tr('members.invitePeople.emptyTitle', 'No organizations where you can invite')}\n description={tr(\n 'members.invitePeople.emptyDescription',\n \"You don't currently have permission to invite members into any organization. Ask an organization admin to grant you invite access.\"\n )}\n />\n </GlassCard>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)] overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1\">\n {invitable.map((org) => (\n <li key={org.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${org.id}/members/invite`)}\n className=\"flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Building size={20} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-[var(--color-text-primary)]\">\n {org.name}\n </p>\n <p className=\"truncate font-mono text-xs text-[var(--color-text-secondary)]\">\n /{org.slug}\n </p>\n </div>\n </div>\n <ChevronRight\n size={18}\n className=\"shrink-0 text-[var(--color-text-secondary)]\"\n aria-hidden=\"true\"\n />\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|