@burdenoff/microfe-organization 2026.519.1 → 2026.521.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.d.ts +41 -0
- package/dist/generated/global-operations.d.ts.map +1 -1
- package/dist/generated/global-operations.js +39 -30
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js +3 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.d.ts.map +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.js +62 -45
- package/dist/organization-management/AdminOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.d.ts.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +60 -43
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/modules/members/components/PendingInvitesList.d.ts.map +1 -1
- package/dist/organization-management/modules/members/components/PendingInvitesList.js +70 -67
- package/dist/organization-management/modules/members/components/PendingInvitesList.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.d.ts +7 -2
- package/dist/organization-management/modules/members/hooks/useInvites.d.ts.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.js +9 -8
- package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.d.ts.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js +48 -47
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js +8 -8
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.d.ts +26 -0
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.d.ts.map +1 -0
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js +63 -0
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js.map +1 -0
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.d.ts.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +5 -5
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.d.ts.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js +226 -202
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.d.ts.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +152 -147
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.d.ts.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +39 -46
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.d.ts.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +3 -3
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/dist/organization-management/providers/OrgScopedPermissions.d.ts +22 -0
- package/dist/organization-management/providers/OrgScopedPermissions.d.ts.map +1 -0
- package/dist/organization-management/providers/OrgScopedPermissions.js +14 -0
- package/dist/organization-management/providers/OrgScopedPermissions.js.map +1 -0
- package/package.json +6 -4
|
@@ -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 { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } 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) => {\n try {\n setActionError(null);\n await cancelInvite(inviteId);\n } catch (err) {\n
|
|
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 { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } 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 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 <div className=\"overflow-x-auto rounded-lg border border-[var(--color-border-default)]\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-[var(--color-bg-subtle)] border-b border-[var(--color-border-default)]\">\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.emailLabel', 'Email Address')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.expires', 'Expires')}\n </th>\n <th className=\"text-right px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {invites.map((invite) => (\n <tr\n key={invite.id}\n className=\"border-b border-[var(--color-border-default)] last:border-b-0 hover:bg-[var(--color-bg-subtle)] transition-colors\"\n >\n <td className=\"px-4 py-3 text-[var(--color-text-primary)]\">{invite.email}</td>\n <td className=\"px-4 py-3\">\n <MemberRoleBadge role={invite.role} />\n </td>\n <td className=\"px-4 py-3\">\n <InviteStatusBadge status={invite.status} />\n </td>\n <td className=\"px-4 py-3 text-[var(--color-text-muted)]\">\n {new Date(invite.expiresAt).toLocaleDateString()}\n </td>\n <td className=\"px-4 py-3 text-right\">\n <div className=\"flex items-center justify-end gap-1\">\n <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 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 </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -17,9 +17,14 @@ export declare function useOrganizationInvites(organizationId: string, filter?:
|
|
|
17
17
|
refetch: (variables?: any) => Promise<import('@apollo/client').ApolloQueryResult<Types.GetOrganizationInvitesQuery>>;
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
|
-
* Hook to fetch invites for the current user (invitee view)
|
|
20
|
+
* Hook to fetch invites for the current user (invitee view).
|
|
21
|
+
*
|
|
22
|
+
* `enabled` defaults to `true` for backward compat. Callers in admin /
|
|
23
|
+
* service contexts (e.g. OrganizationsListPage in `mode="admin"`) should
|
|
24
|
+
* pass `false` to skip the network round-trip and avoid surfacing
|
|
25
|
+
* authorization errors for actors that don't hold the self-scope.
|
|
21
26
|
*/
|
|
22
|
-
export declare function useMyInvites(status?: InviteStatus, limit?: number, offset?: number): {
|
|
27
|
+
export declare function useMyInvites(status?: InviteStatus, limit?: number, offset?: number, enabled?: boolean): {
|
|
23
28
|
invites: OrganizationInvite[];
|
|
24
29
|
total: any;
|
|
25
30
|
hasMore: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInvites.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/members/hooks/useInvites.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,oCAAoC,CAAC;AAiB5C,UAAU,4BAA4B;IACpC,wBAAwB,EAAE,kBAAkB,CAAC;CAC9C;AAgCD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,YAAY,CAAA;CAAE,EAClC,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU;aAUqC,kBAAkB,EAAE;;;;;;EAO5E;AAED
|
|
1
|
+
{"version":3,"file":"useInvites.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/members/hooks/useInvites.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,oCAAoC,CAAC;AAiB5C,UAAU,4BAA4B;IACpC,wBAAwB,EAAE,kBAAkB,CAAC;CAC9C;AAgCD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,YAAY,CAAA;CAAE,EAClC,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU;aAUqC,kBAAkB,EAAE;;;;;;EAO5E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU,EAClB,OAAO,GAAE,OAAc;aAUkC,kBAAkB,EAAE;;;;;;EAO9E;AAED;;GAEG;AACH,wBAAgB,eAAe;0BAOC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;;;EA4C1F;AAED;;GAEG;AACH,wBAAgB,eAAe;6BAUI,MAAM;;;EAyCxC;AAED;;GAEG;AACH,wBAAgB,eAAe;6BAOI,MAAM;;;EAsCxC;AAED;;GAEG;AACH,wBAAgB,eAAe;6BAOI,MAAM;;;EAsCxC;AAED;;GAEG;AACH,wBAAgB,eAAe;6BAOI,MAAM;;;EAsCxC"}
|
|
@@ -43,23 +43,24 @@ function v(e, t, n = 20, r = 0) {
|
|
|
43
43
|
refetch: c
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
function y(e, t = 20, n = 0) {
|
|
47
|
-
let { data:
|
|
46
|
+
function y(e, t = 20, n = 0, r = !0) {
|
|
47
|
+
let { data: a, loading: o, error: s, refetch: c } = i({
|
|
48
48
|
variables: {
|
|
49
49
|
status: e,
|
|
50
50
|
limit: t,
|
|
51
51
|
offset: n
|
|
52
52
|
},
|
|
53
|
+
skip: !r,
|
|
53
54
|
fetchPolicy: "cache-first",
|
|
54
55
|
nextFetchPolicy: "cache-first"
|
|
55
56
|
});
|
|
56
57
|
return {
|
|
57
|
-
invites:
|
|
58
|
-
total:
|
|
59
|
-
hasMore:
|
|
60
|
-
loading:
|
|
61
|
-
error:
|
|
62
|
-
refetch:
|
|
58
|
+
invites: a?.myOrganizationInvites?.items ?? [],
|
|
59
|
+
total: a?.myOrganizationInvites?.total ?? 0,
|
|
60
|
+
hasMore: a?.myOrganizationInvites?.hasMore ?? !1,
|
|
61
|
+
loading: o,
|
|
62
|
+
error: s,
|
|
63
|
+
refetch: c
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
function b() {
|
|
@@ -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 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 */\nexport function useMyInvites(status?: InviteStatus, limit: number = 20, offset: number = 0) {\n const { data, loading, error, refetch } = useGetMyOrganizationInvitesQuery({\n variables: { status, limit, offset },\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 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":[],"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 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 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberPage.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"InviteMemberPage.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"names":[],"mappings":"AAYA;;;GAGG;AACH,wBAAgB,gBAAgB,4CAuG/B"}
|
|
@@ -1,91 +1,92 @@
|
|
|
1
1
|
import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
2
|
+
import { toast as t } from "../../../../node_modules/sonner/dist/index.js";
|
|
3
|
+
import { useInviteMember as n } from "../hooks/useInvites.js";
|
|
4
|
+
import { useTr as r } from "../../../shared/hooks/useTr.js";
|
|
5
|
+
import { LoadingState as i } from "../../../shared/components/feedback/LoadingState.js";
|
|
6
|
+
import { PERMISSIONS as a } from "../../../constants/permissions.js";
|
|
7
|
+
import { InviteMemberForm as o } from "../components/InviteMemberForm.js";
|
|
8
|
+
import { PendingInvitesList as s } from "../components/PendingInvitesList.js";
|
|
9
|
+
import { ArrowLeft as c, UserPlus as l } from "lucide-react";
|
|
10
|
+
import { usePermissions as u } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
11
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
12
|
+
import { useParams as p } from "react-router-dom";
|
|
12
13
|
//#region src/organization-management/modules/members/pages/InviteMemberPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let
|
|
15
|
-
await
|
|
16
|
-
},
|
|
17
|
-
|
|
14
|
+
function m() {
|
|
15
|
+
let m = r(), { organizationId: h } = p(), { navigate: g, basePath: _ = "/organizations" } = e(), { hasPermission: v, isLoading: y } = u(), { inviteMember: b, loading: x, error: S } = n(), C = async (e) => {
|
|
16
|
+
await b(e), t.success(`${m("members.invite.success", "Invitation sent to")} ${e.email}`);
|
|
17
|
+
}, w = () => {
|
|
18
|
+
g(`${_}/${h}/members`);
|
|
18
19
|
};
|
|
19
|
-
return
|
|
20
|
+
return y ? /* @__PURE__ */ d("div", {
|
|
20
21
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
21
|
-
children: /* @__PURE__ */
|
|
22
|
-
}) :
|
|
22
|
+
children: /* @__PURE__ */ d(i, { message: m("common.loadingPermissions", "Loading permissions...") })
|
|
23
|
+
}) : v(a.MEMBER_INVITE) ? /* @__PURE__ */ d("div", {
|
|
23
24
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
24
|
-
children: /* @__PURE__ */
|
|
25
|
+
children: /* @__PURE__ */ f("div", {
|
|
25
26
|
className: "mx-auto max-w-3xl space-y-8",
|
|
26
27
|
children: [
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ f("div", {
|
|
28
29
|
className: "flex items-center gap-3",
|
|
29
|
-
children: [/* @__PURE__ */
|
|
30
|
-
onClick:
|
|
30
|
+
children: [/* @__PURE__ */ d("button", {
|
|
31
|
+
onClick: w,
|
|
31
32
|
className: "rounded-lg p-2 transition-colors hover:bg-[var(--color-bg-subtle)]",
|
|
32
|
-
"aria-label":
|
|
33
|
-
children: /* @__PURE__ */
|
|
33
|
+
"aria-label": m("members.invite.backToMembers", "Back to members"),
|
|
34
|
+
children: /* @__PURE__ */ d(c, {
|
|
34
35
|
size: 20,
|
|
35
36
|
className: "text-[var(--color-text-secondary)]"
|
|
36
37
|
})
|
|
37
|
-
}), /* @__PURE__ */
|
|
38
|
+
}), /* @__PURE__ */ f("div", {
|
|
38
39
|
className: "flex items-center gap-2",
|
|
39
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ d(l, {
|
|
40
41
|
size: 24,
|
|
41
42
|
className: "text-[var(--color-text-primary)]"
|
|
42
|
-
}), /* @__PURE__ */
|
|
43
|
+
}), /* @__PURE__ */ d("h1", {
|
|
43
44
|
className: "text-2xl font-bold text-[var(--color-text-primary)]",
|
|
44
|
-
children:
|
|
45
|
+
children: m("members.invite.title", "Invite Member")
|
|
45
46
|
})]
|
|
46
47
|
})]
|
|
47
48
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ f("div", {
|
|
49
50
|
className: "bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6",
|
|
50
51
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ d("h2", {
|
|
52
53
|
className: "text-lg font-semibold text-[var(--color-text-primary)] mb-4",
|
|
53
|
-
children:
|
|
54
|
+
children: m("members.invite.send", "Send Invite")
|
|
54
55
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ d("p", {
|
|
56
57
|
className: "text-sm text-[var(--color-text-muted)] mb-6",
|
|
57
|
-
children:
|
|
58
|
+
children: m("members.invite.description", "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.")
|
|
58
59
|
}),
|
|
59
|
-
|
|
60
|
+
S && /* @__PURE__ */ d("div", {
|
|
60
61
|
className: "mb-4 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3",
|
|
61
|
-
children: /* @__PURE__ */
|
|
62
|
+
children: /* @__PURE__ */ d("p", {
|
|
62
63
|
className: "text-sm text-[var(--color-status-error-text)]",
|
|
63
|
-
children:
|
|
64
|
+
children: S.message.includes("CONFLICT") ? m("members.invite.error.conflict", "An invite already exists for this email address.") : S.message.includes("already a member") ? m("members.invite.error.alreadyMember", "This user is already a member of the organization.") : S.message
|
|
64
65
|
})
|
|
65
66
|
}),
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
organizationId:
|
|
68
|
-
onSubmit:
|
|
69
|
-
onCancel:
|
|
70
|
-
loading:
|
|
67
|
+
/* @__PURE__ */ d(o, {
|
|
68
|
+
organizationId: h,
|
|
69
|
+
onSubmit: C,
|
|
70
|
+
onCancel: w,
|
|
71
|
+
loading: x
|
|
71
72
|
})
|
|
72
73
|
]
|
|
73
74
|
}),
|
|
74
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ d("div", {
|
|
75
76
|
className: "bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6",
|
|
76
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: /* @__PURE__ */ d(s, { organizationId: h })
|
|
77
78
|
})
|
|
78
79
|
]
|
|
79
80
|
})
|
|
80
|
-
}) : /* @__PURE__ */
|
|
81
|
+
}) : /* @__PURE__ */ d("div", {
|
|
81
82
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
82
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ d("div", {
|
|
83
84
|
className: "p-8 text-center text-[var(--color-text-secondary)]",
|
|
84
|
-
children:
|
|
85
|
+
children: m("members.invite.noPermission", "You don't have permission to invite members.")
|
|
85
86
|
})
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
//#endregion
|
|
89
|
-
export {
|
|
90
|
+
export { m as InviteMemberPage };
|
|
90
91
|
|
|
91
92
|
//# sourceMappingURL=InviteMemberPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\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 { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\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\n const handleSubmit = async (data: {\n organizationId: string;\n email: string;\n message?: string;\n }) => {\n await inviteMember(data);\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 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 {/* Invite Form Card */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.invite.send', 'Send Invite')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-muted)] mb-6\">\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 </p>\n\n {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 <InviteMemberForm\n organizationId={organizationId!}\n onSubmit={handleSubmit}\n onCancel={handleCancel}\n loading={loading}\n />\n </div>\n\n {/* Pending Invites */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <PendingInvitesList organizationId={organizationId!} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"InviteMemberPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\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 { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\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\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 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 {/* Invite Form Card */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.invite.send', 'Send Invite')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-muted)] mb-6\">\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 </p>\n\n {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 <InviteMemberForm\n organizationId={organizationId!}\n onSubmit={handleSubmit}\n onCancel={handleCancel}\n loading={loading}\n />\n </div>\n\n {/* Pending Invites */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <PendingInvitesList organizationId={organizationId!} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,SAAgB,IAAmB;CACjC,IAAM,IAAK,GAAO,EACZ,EAAE,sBAAmB,GAAuC,EAC5D,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,iBAAc,YAAS,aAAU,GAAiB,EAEpD,IAAe,OAAO,MAItB;AAEJ,EADA,MAAM,EAAa,EAAK,EACxB,EAAM,QAAQ,GAAG,EAAG,0BAA0B,qBAAqB,CAAC,GAAG,EAAK,QAAQ;IAGhF,UAAqB;AACzB,IAAS,GAAG,EAAS,GAAG,EAAe,UAAU;;AAqBnD,QAlBI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIL,EAAc,EAAY,cAAc,GAW3C,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;MACV,cAAY,EAAG,gCAAgC,kBAAkB;gBAEjE,kBAAC,GAAD;OAAW,MAAM;OAAI,WAAU;OAAuC,CAAA;MAC/D,CAAA,EACT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAU,MAAM;OAAI,WAAU;OAAqC,CAAA,EACnE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,wBAAwB,gBAAgB;OACzC,CAAA,CACD;QACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,uBAAuB,cAAc;OACtC,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,8BACA,kJACD;OACC,CAAA;MAEH,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,QAAQ,SAAS,WAAW,GAC/B,EACE,iCACA,mDACD,GACD,EAAM,QAAQ,SAAS,mBAAmB,GACxC,EACE,sCACA,qDACD,GACD,EAAM;QACV,CAAA;OACA,CAAA;MAGR,kBAAC,GAAD;OACkB;OAChB,UAAU;OACV,UAAU;OACD;OACT,CAAA;MACE;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAoC,mBAAmB,CAAA;KACnD,CAAA;IACF;;EACF,CAAA,GAvEJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,+BAA+B,+CAA+C;GAC9E,CAAA;EACF,CAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { useAcceptInvite as e, useMyInvites as t, useRejectInvite as n } from "../hooks/useInvites.js";
|
|
2
|
+
import { InviteStatus as r } from "../../../../generated/global-types.js";
|
|
3
|
+
import { useTr as i } from "../../../shared/hooks/useTr.js";
|
|
4
|
+
import { LoadingState as a } from "../../../shared/components/feedback/LoadingState.js";
|
|
5
|
+
import { useOrganizationPageView as o } from "../../../hooks/useOrganizationPageView.js";
|
|
6
6
|
import { MyInviteCard as s } from "../components/MyInviteCard.js";
|
|
7
7
|
import { useState as c } from "react";
|
|
8
8
|
import { AlertCircle as l, Loader2 as u, Mail as d } from "lucide-react";
|
|
@@ -10,8 +10,8 @@ import { usePermissions as f } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
|
10
10
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
11
|
//#region src/organization-management/modules/members/pages/MyInvitesPage.tsx
|
|
12
12
|
function h() {
|
|
13
|
-
|
|
14
|
-
let h =
|
|
13
|
+
o("organizations.my-invites");
|
|
14
|
+
let h = i(), { isLoading: g } = f(), { invites: _, loading: v, error: y, total: b } = t(r.Pending), { acceptInvite: x, loading: S } = e(), { rejectInvite: C, loading: w } = n(), [T, E] = c(null), D = async (e) => {
|
|
15
15
|
try {
|
|
16
16
|
E(null), await x(e);
|
|
17
17
|
} catch (e) {
|
|
@@ -26,7 +26,7 @@ function h() {
|
|
|
26
26
|
};
|
|
27
27
|
return g ? /* @__PURE__ */ p("div", {
|
|
28
28
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
29
|
-
children: /* @__PURE__ */ p(
|
|
29
|
+
children: /* @__PURE__ */ p(a, { message: h("common.loadingPermissions", "Loading permissions...") })
|
|
30
30
|
}) : v && _.length === 0 ? /* @__PURE__ */ p("div", {
|
|
31
31
|
className: "flex items-center justify-center min-h-[300px]",
|
|
32
32
|
children: /* @__PURE__ */ p(u, {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter a set of organization ids down to those where the current user has
|
|
3
|
+
* a specific RBAC permission. Bulk-checks against the rbac service so the
|
|
4
|
+
* result tracks the authoritative evaluator — including overrides and any
|
|
5
|
+
* future OPA policies — instead of client-side-duplicating the
|
|
6
|
+
* role→permission mapping.
|
|
7
|
+
*
|
|
8
|
+
* The server caps a single bulk call at `CHECK_PERMISSIONS_BATCH_LIMIT`
|
|
9
|
+
* inputs, so when a user has more orgs than that we chunk into multiple
|
|
10
|
+
* parallel queries via `client.query()` and merge results positionally.
|
|
11
|
+
* Most users will fit in one batch; the chunking is defensive for power
|
|
12
|
+
* users / service accounts with many memberships (the WorkspaceForm
|
|
13
|
+
* caller fetches up to 100 orgs, so two batches is realistic).
|
|
14
|
+
*
|
|
15
|
+
* Use this whenever a control surface needs to constrain itself to "orgs
|
|
16
|
+
* where I can do X" rather than "orgs I'm a member of" — e.g. the org
|
|
17
|
+
* picker on Create Workspace, Invite Member targets, role-assignment
|
|
18
|
+
* dropdowns. The backend `@rbac` directives still enforce on submission;
|
|
19
|
+
* this is purely UX (don't show options that will 403).
|
|
20
|
+
*/
|
|
21
|
+
export declare function useOrgsWithPermission(orgIds: readonly string[], resourceType: string, action: string): {
|
|
22
|
+
allowedIds: Set<string>;
|
|
23
|
+
loading: boolean;
|
|
24
|
+
isError: boolean;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=useOrgsWithPermission.d.ts.map
|
package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrgsWithPermission.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/organizations/hooks/useOrgsWithPermission.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb;IAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAgGjE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CheckRbacPermissionsDocument as e } from "../../../../generated/global-operations.js";
|
|
2
|
+
import { ActorType as t } from "../../../../generated/global-types.js";
|
|
3
|
+
import { useEffect as n, useMemo as r, useState as i } from "react";
|
|
4
|
+
import { useAuth as a } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
5
|
+
import { useApolloClient as o } from "@apollo/client/react";
|
|
6
|
+
//#region src/organization-management/modules/organizations/hooks/useOrgsWithPermission.ts
|
|
7
|
+
var s = 50;
|
|
8
|
+
function c(c, l, u) {
|
|
9
|
+
let { user: d } = a(), f = d?.id ?? "", p = o(), m = r(() => [...c], [r(() => c.join(""), [c])]), h = r(() => m.map((e) => ({
|
|
10
|
+
actorId: f,
|
|
11
|
+
actorType: t.User,
|
|
12
|
+
scopeId: e,
|
|
13
|
+
resourceType: l,
|
|
14
|
+
action: u
|
|
15
|
+
})), [
|
|
16
|
+
m,
|
|
17
|
+
f,
|
|
18
|
+
l,
|
|
19
|
+
u
|
|
20
|
+
]), g = !f || m.length === 0, [_, v] = i([]), [y, b] = i(!1), [x, S] = i(!1);
|
|
21
|
+
return n(() => {
|
|
22
|
+
if (g) {
|
|
23
|
+
v([]), b(!1), S(!1);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let t = !1;
|
|
27
|
+
b(!0), S(!1);
|
|
28
|
+
let n = [];
|
|
29
|
+
for (let e = 0; e < h.length; e += s) n.push(h.slice(e, e + s));
|
|
30
|
+
return Promise.all(n.map((t) => p.query({
|
|
31
|
+
query: e,
|
|
32
|
+
variables: { inputs: t },
|
|
33
|
+
fetchPolicy: "cache-first"
|
|
34
|
+
}))).then((e) => {
|
|
35
|
+
t || (v(e.flatMap((e) => e.data?.checkPermissions ?? [])), b(!1));
|
|
36
|
+
}).catch(() => {
|
|
37
|
+
t || (v([]), S(!0), b(!1));
|
|
38
|
+
}), () => {
|
|
39
|
+
t = !0;
|
|
40
|
+
};
|
|
41
|
+
}, [
|
|
42
|
+
p,
|
|
43
|
+
h,
|
|
44
|
+
g
|
|
45
|
+
]), {
|
|
46
|
+
allowedIds: r(() => {
|
|
47
|
+
if (g) return /* @__PURE__ */ new Set();
|
|
48
|
+
let e = /* @__PURE__ */ new Set(), t = Math.min(_.length, m.length);
|
|
49
|
+
for (let n = 0; n < t; n += 1) _[n].allowed && m[n] && e.add(m[n]);
|
|
50
|
+
return e;
|
|
51
|
+
}, [
|
|
52
|
+
_,
|
|
53
|
+
m,
|
|
54
|
+
g
|
|
55
|
+
]),
|
|
56
|
+
loading: !g && y,
|
|
57
|
+
isError: !g && x
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { c as useOrgsWithPermission };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=useOrgsWithPermission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrgsWithPermission.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/hooks/useOrgsWithPermission.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { useAuth } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n CheckRbacPermissionsDocument,\n type CheckRbacPermissionsQuery,\n type CheckRbacPermissionsQueryVariables,\n} from '../../../../generated/global-operations';\nimport { ActorType } from '../../../../generated/global-types';\n\ntype PermissionCheckResult = CheckRbacPermissionsQuery['checkPermissions'][number];\n\n// Matches the server-side `MAX_BULK_CHECK_SIZE` on `checkPermissions`.\n// Kept as a constant so the chunking intent is explicit at the call site.\nconst CHECK_PERMISSIONS_BATCH_LIMIT = 50;\n\n/**\n * Filter a set of organization ids down to those where the current user has\n * a specific RBAC permission. Bulk-checks against the rbac service so the\n * result tracks the authoritative evaluator — including overrides and any\n * future OPA policies — instead of client-side-duplicating the\n * role→permission mapping.\n *\n * The server caps a single bulk call at `CHECK_PERMISSIONS_BATCH_LIMIT`\n * inputs, so when a user has more orgs than that we chunk into multiple\n * parallel queries via `client.query()` and merge results positionally.\n * Most users will fit in one batch; the chunking is defensive for power\n * users / service accounts with many memberships (the WorkspaceForm\n * caller fetches up to 100 orgs, so two batches is realistic).\n *\n * Use this whenever a control surface needs to constrain itself to \"orgs\n * where I can do X\" rather than \"orgs I'm a member of\" — e.g. the org\n * picker on Create Workspace, Invite Member targets, role-assignment\n * dropdowns. The backend `@rbac` directives still enforce on submission;\n * this is purely UX (don't show options that will 403).\n */\nexport function useOrgsWithPermission(\n orgIds: readonly string[],\n resourceType: string,\n action: string\n): { allowedIds: Set<string>; loading: boolean; isError: boolean } {\n const { user } = useAuth();\n const actorId = user?.id ?? '';\n const client = useApolloClient();\n\n // `stableKey` lets memos depend on the *values* of orgIds, not the array\n // reference — callers passing a fresh array with the same ids won't bust\n // the query cache or trigger refetch loops.\n const stableKey = useMemo(() => orgIds.join(''), [orgIds]);\n const stableIds = useMemo(() => [...orgIds], [stableKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const inputs = useMemo(\n () =>\n stableIds.map((scopeId) => ({\n actorId,\n actorType: ActorType.User,\n scopeId,\n resourceType,\n action,\n })),\n [stableIds, actorId, resourceType, action]\n );\n\n const skip = !actorId || stableIds.length === 0;\n\n // Fan out the bulk check into <=BATCH_LIMIT-sized chunks, run them in\n // parallel via the Apollo client (so per-chunk caching still works), and\n // concat results in input order to preserve the positional contract.\n // We can't use multiple useQuery hooks here because the chunk count is\n // dynamic, hence the client.query + Promise.all pattern in an effect.\n const [results, setResults] = useState<PermissionCheckResult[]>([]);\n const [loading, setLoading] = useState(false);\n const [hadError, setHadError] = useState(false);\n\n useEffect(() => {\n if (skip) {\n setResults([]);\n setLoading(false);\n setHadError(false);\n return;\n }\n\n let cancelled = false;\n setLoading(true);\n setHadError(false);\n\n const chunks: (typeof inputs)[] = [];\n for (let i = 0; i < inputs.length; i += CHECK_PERMISSIONS_BATCH_LIMIT) {\n chunks.push(inputs.slice(i, i + CHECK_PERMISSIONS_BATCH_LIMIT));\n }\n\n Promise.all(\n chunks.map((chunk) =>\n client.query<CheckRbacPermissionsQuery, CheckRbacPermissionsQueryVariables>({\n query: CheckRbacPermissionsDocument,\n variables: { inputs: chunk },\n fetchPolicy: 'cache-first',\n })\n )\n )\n .then((responses) => {\n if (cancelled) return;\n const merged = responses.flatMap((r) => r.data?.checkPermissions ?? []);\n setResults(merged);\n setLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setResults([]);\n setHadError(true);\n setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, inputs, skip]);\n\n const allowedIds = useMemo(() => {\n if (skip) return new Set<string>();\n const out = new Set<string>();\n // Length-guard the positional mapping so we never silently\n // mis-attribute a permission to the wrong org if the backend ever\n // truncates, reorders, or filters the response.\n const bounded = Math.min(results.length, stableIds.length);\n for (let i = 0; i < bounded; i += 1) {\n if (results[i].allowed && stableIds[i]) out.add(stableIds[i]);\n }\n return out;\n }, [results, stableIds, skip]);\n\n return {\n allowedIds,\n loading: !skip && loading,\n isError: !skip && hadError,\n };\n}\n"],"mappings":";;;;;;AAcA,IAAM,IAAgC;AAsBtC,SAAgB,EACd,GACA,GACA,GACiE;CACjE,IAAM,EAAE,YAAS,GAAS,EACpB,IAAU,GAAM,MAAM,IACtB,IAAS,GAAiB,EAM1B,IAAY,QAAc,CAAC,GAAG,EAAO,EAAE,CAD3B,QAAc,EAAO,KAAK,GAAG,EAAE,CAAC,EAAO,CAAC,CACF,CAAC,EAEnD,IAAS,QAEX,EAAU,KAAK,OAAa;EAC1B;EACA,WAAW,EAAU;EACrB;EACA;EACA;EACD,EAAE,EACL;EAAC;EAAW;EAAS;EAAc;EAAO,CAC3C,EAEK,IAAO,CAAC,KAAW,EAAU,WAAW,GAOxC,CAAC,GAAS,KAAc,EAAkC,EAAE,CAAC,EAC7D,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAU,KAAe,EAAS,GAAM;AA2D/C,QAzDA,QAAgB;AACd,MAAI,GAAM;AAGR,GAFA,EAAW,EAAE,CAAC,EACd,EAAW,GAAM,EACjB,EAAY,GAAM;AAClB;;EAGF,IAAI,IAAY;AAEhB,EADA,EAAW,GAAK,EAChB,EAAY,GAAM;EAElB,IAAM,IAA4B,EAAE;AACpC,OAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK,EACtC,GAAO,KAAK,EAAO,MAAM,GAAG,IAAI,EAA8B,CAAC;AAyBjE,SAtBA,QAAQ,IACN,EAAO,KAAK,MACV,EAAO,MAAqE;GAC1E,OAAO;GACP,WAAW,EAAE,QAAQ,GAAO;GAC5B,aAAa;GACd,CAAC,CACH,CACF,CACE,MAAM,MAAc;AACf,SAEJ,EADe,EAAU,SAAS,MAAM,EAAE,MAAM,oBAAoB,EAAE,CAAC,CACrD,EAClB,EAAW,GAAM;IACjB,CACD,YAAY;AACP,SACJ,EAAW,EAAE,CAAC,EACd,EAAY,GAAK,EACjB,EAAW,GAAM;IACjB,QAES;AACX,OAAY;;IAEb;EAAC;EAAQ;EAAQ;EAAK,CAAC,EAenB;EACL,YAdiB,QAAc;AAC/B,OAAI,EAAM,wBAAO,IAAI,KAAa;GAClC,IAAM,oBAAM,IAAI,KAAa,EAIvB,IAAU,KAAK,IAAI,EAAQ,QAAQ,EAAU,OAAO;AAC1D,QAAK,IAAI,IAAI,GAAG,IAAI,GAAS,KAAK,EAChC,CAAI,EAAQ,GAAG,WAAW,EAAU,MAAI,EAAI,IAAI,EAAU,GAAG;AAE/D,UAAO;KACN;GAAC;GAAS;GAAW;GAAK,CAAC;EAI5B,SAAS,CAAC,KAAQ;EAClB,SAAS,CAAC,KAAQ;EACnB"}
|
package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrganizationDetailPage.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"OrganizationDetailPage.d.ts","sourceRoot":"","sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,sBAAsB,4CAmWrC"}
|
|
@@ -102,13 +102,13 @@ function C() {
|
|
|
102
102
|
})]
|
|
103
103
|
}), /* @__PURE__ */ b("div", {
|
|
104
104
|
className: "flex flex-col gap-3 sm:flex-row",
|
|
105
|
-
children: [/* @__PURE__ */ b("button", {
|
|
105
|
+
children: [D(s.MEMBER_LIST) && /* @__PURE__ */ b("button", {
|
|
106
106
|
onClick: () => T(`${E}/${w}/members`),
|
|
107
107
|
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]",
|
|
108
108
|
children: [/* @__PURE__ */ y(_, {
|
|
109
109
|
size: 16,
|
|
110
110
|
"aria-hidden": "true"
|
|
111
|
-
}), /* @__PURE__ */ y("span", { children: C("organizations.detail.manageMembers", "Manage Members") })]
|
|
111
|
+
}), /* @__PURE__ */ y("span", { children: D(s.MEMBER_INVITE) ? C("organizations.detail.manageMembers", "Manage Members") : C("organizations.detail.viewMembers", "View Members") })]
|
|
112
112
|
}), D(s.ORG_UPDATE) && /* @__PURE__ */ b("button", {
|
|
113
113
|
onClick: () => T(`${E}/${w}/edit`),
|
|
114
114
|
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]",
|
|
@@ -281,11 +281,11 @@ function C() {
|
|
|
281
281
|
"aria-hidden": "true"
|
|
282
282
|
}),
|
|
283
283
|
title: C("workspaces.list.emptyTitle", "No workspaces yet"),
|
|
284
|
-
description: C("workspaces.list.emptyDescription", "Create a workspace when this organization is ready to collaborate with teams or projects."),
|
|
285
|
-
action: {
|
|
284
|
+
description: D(s.WORKSPACE_CREATE) ? C("workspaces.list.emptyDescription", "Create a workspace when this organization is ready to collaborate with teams or projects.") : C("workspaces.list.emptyDescriptionViewer", "No workspaces have been created in this organization yet."),
|
|
285
|
+
action: D(s.WORKSPACE_CREATE) ? {
|
|
286
286
|
label: C("workspaces.create.button", "Create Workspace"),
|
|
287
287
|
onClick: () => T(`${E}/workspaces/new?organizationId=${w}`)
|
|
288
|
-
}
|
|
288
|
+
} : void 0
|
|
289
289
|
})
|
|
290
290
|
})]
|
|
291
291
|
})
|