@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
package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddWorkspaceMemberDialog.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Loader2, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useMembers } from '../../members/hooks/useMembers';\nimport { useAddWorkspaceMember } from '../hooks/useWorkspaceMembers';\nimport type { WorkspaceMember } from '../types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AddWorkspaceMemberDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n organizationId: string;\n existingMembers: WorkspaceMember[];\n onInviteOrganizationMember?: () => void;\n}\n\nexport function AddWorkspaceMemberDialog({\n open,\n onOpenChange,\n workspaceId,\n organizationId,\n existingMembers,\n onInviteOrganizationMember,\n}: AddWorkspaceMemberDialogProps) {\n const tr = useTr();\n const [selectedUserId, setSelectedUserId] = useState('');\n const [selectedRole, setSelectedRole] = useState<'MEMBER' | 'WORKSPACE_ADMIN'>('MEMBER');\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n\n const { members: organizationMembers, loading: membersLoading } = useMembers(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId,\n 100,\n 0,\n // react-doctor-disable-next-line react-doctor/no-event-handler\n open\n );\n const { addWorkspaceMember, loading: addingMember } = useAddWorkspaceMember();\n\n const existingUserIds = useMemo(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n () => new Set(existingMembers.map((member) => member.userId)),\n [existingMembers]\n );\n\n const availableMembers = useMemo(\n () => organizationMembers.filter((member) => !existingUserIds.has(member.userId)),\n [existingUserIds, organizationMembers]\n );\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => availableMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [availableMembers]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n useEffect(() => {\n if (!open) {\n setSelectedUserId('');\n setSelectedRole('MEMBER');\n setSubmissionError(null);\n return;\n }\n\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (!selectedUserId && availableMembers.length > 0) {\n setSelectedUserId(availableMembers[0]?.userId ?? '');\n }\n }, [availableMembers, open, selectedUserId]);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!selectedUserId) {\n setSubmissionError(\n tr(\n 'workspaces.addMember.selectRequired',\n 'Select an organization member to add to this workspace.'\n )\n );\n return;\n }\n\n try {\n setSubmissionError(null);\n const result = await addWorkspaceMember({\n workspaceId,\n userId: selectedUserId,\n role: selectedRole,\n });\n\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n\n toast.success(tr('workspaces.addMember.success', 'Workspace member added successfully'));\n onOpenChange(false);\n } catch (error) {\n setSubmissionError(\n error instanceof Error\n ? error.message\n : tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[520px] border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2 text-[var(--color-text-primary)]\">\n <UserPlus size={18} aria-hidden=\"true\" />\n {tr('workspaces.addMember.title', 'Add workspace member')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.addMember.description',\n 'Choose an existing organization member and assign their workspace role.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {submissionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3 text-sm text-[var(--color-status-error-text)]\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" aria-hidden=\"true\" />\n <span>{submissionError}</span>\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"workspace-member-user\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.memberLabel', 'Organization member')}\n </label>\n <select\n id=\"workspace-member-user\"\n value={selectedUserId}\n onChange={(event) => setSelectedUserId(event.target.value)}\n disabled={membersLoading || addingMember || availableMembers.length === 0}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n {availableMembers.length === 0 ? (\n <option value=\"\">\n {tr(\n 'workspaces.addMember.noneAvailable',\n 'No eligible organization members available'\n )}\n </option>\n ) : null}\n {availableMembers.map((member) => (\n <option key={member.id} value={member.userId}>\n {profileMap[member.userId]?.displayName ?? member.userId} ({member.role})\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.filterHelp',\n 'Only members that belong to this organization and are not already in the workspace are listed.'\n )}\n </p>\n {availableMembers.length === 0 && onInviteOrganizationMember ? (\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"mt-2 inline-flex items-center gap-2 text-sm font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.inviteInstead',\n 'Invite a new organization member instead'\n )}\n </button>\n ) : null}\n {onInviteOrganizationMember ? (\n <p className=\"mt-2 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.bulkInviteHint',\n 'Need to add many people who are not in this organization yet?'\n )}{' '}\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.bulkInviteLink',\n 'Bulk-invite them from the organization invite page'\n )}\n </button>\n .\n </p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"workspace-member-role\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.roleLabel', 'Workspace role')}\n </label>\n <select\n id=\"workspace-member-role\"\n value={selectedRole}\n onChange={(event) =>\n setSelectedRole(event.target.value as 'MEMBER' | 'WORKSPACE_ADMIN')\n }\n disabled={addingMember}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"MEMBER\">{tr('members.role.member', 'Member')}</option>\n <option value=\"WORKSPACE_ADMIN\">\n {tr('members.role.workspaceAdmin', 'Workspace Admin')}\n </option>\n </select>\n </div>\n\n <ResponsiveDialogFooter className=\"gap-3 sm:gap-0\">\n <button\n type=\"button\"\n onClick={() => onOpenChange(false)}\n className=\"rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n disabled={addingMember}\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={addingMember || membersLoading || availableMembers.length === 0}\n >\n {addingMember ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {tr('workspaces.addMember.submit', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddWorkspaceMemberDialog.js","names":["AddWorkspaceMemberDialog","tr","useTr","selectedUserId","setSelectedUserId","useState","selectedRole","setSelectedRole","submissionError","setSubmissionError","organizationMembers","membersLoading","useMembers","organizationId","open","addingMember","useAddWorkspaceMember","existingUserIds","useMemo","existingMembers","member","availableMembers","useOrgConsole","memberActors","profileMap","useActorProfiles","useEffect","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","UserPlus","ResponsiveDialogDescription","event","result","addWorkspaceMember","toast","onOpenChange","error","AlertCircle","onInviteOrganizationMember","ResponsiveDialogFooter","Loader2"],"sources":["../../../../../src/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Loader2, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useMembers } from '../../members/hooks/useMembers';\nimport { useAddWorkspaceMember } from '../hooks/useWorkspaceMembers';\nimport type { WorkspaceMember } from '../types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AddWorkspaceMemberDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n organizationId: string;\n existingMembers: WorkspaceMember[];\n onInviteOrganizationMember?: () => void;\n}\n\nexport function AddWorkspaceMemberDialog({\n open,\n onOpenChange,\n workspaceId,\n organizationId,\n existingMembers,\n onInviteOrganizationMember,\n}: AddWorkspaceMemberDialogProps) {\n const tr = useTr();\n const [selectedUserId, setSelectedUserId] = useState('');\n const [selectedRole, setSelectedRole] = useState<'MEMBER' | 'WORKSPACE_ADMIN'>('MEMBER');\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n\n const { members: organizationMembers, loading: membersLoading } = useMembers(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId,\n 100,\n 0,\n // react-doctor-disable-next-line react-doctor/no-event-handler\n open\n );\n const { addWorkspaceMember, loading: addingMember } = useAddWorkspaceMember();\n\n const existingUserIds = useMemo(\n // react-doctor-disable-next-line react-doctor/no-event-handler\n () => new Set(existingMembers.map((member) => member.userId)),\n [existingMembers]\n );\n\n const availableMembers = useMemo(\n () => organizationMembers.filter((member) => !existingUserIds.has(member.userId)),\n [existingUserIds, organizationMembers]\n );\n\n const { apiGatewayUrl, authToken } = useOrgConsole();\n const memberActors = useMemo<ActorInput[]>(\n () => availableMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [availableMembers]\n );\n const profileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n useEffect(() => {\n if (!open) {\n setSelectedUserId('');\n setSelectedRole('MEMBER');\n setSubmissionError(null);\n return;\n }\n\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (!selectedUserId && availableMembers.length > 0) {\n setSelectedUserId(availableMembers[0]?.userId ?? '');\n }\n }, [availableMembers, open, selectedUserId]);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!selectedUserId) {\n setSubmissionError(\n tr(\n 'workspaces.addMember.selectRequired',\n 'Select an organization member to add to this workspace.'\n )\n );\n return;\n }\n\n try {\n setSubmissionError(null);\n const result = await addWorkspaceMember({\n workspaceId,\n userId: selectedUserId,\n role: selectedRole,\n });\n\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n\n toast.success(tr('workspaces.addMember.success', 'Workspace member added successfully'));\n onOpenChange(false);\n } catch (error) {\n setSubmissionError(\n error instanceof Error\n ? error.message\n : tr('workspaces.addMember.failed', 'Failed to add workspace member')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[520px] border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2 text-[var(--color-text-primary)]\">\n <UserPlus size={18} aria-hidden=\"true\" />\n {tr('workspaces.addMember.title', 'Add workspace member')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"text-[var(--color-text-secondary)]\">\n {tr(\n 'workspaces.addMember.description',\n 'Choose an existing organization member and assign their workspace role.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {submissionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3 text-sm text-[var(--color-status-error-text)]\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" aria-hidden=\"true\" />\n <span>{submissionError}</span>\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"workspace-member-user\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.memberLabel', 'Organization member')}\n </label>\n <select\n id=\"workspace-member-user\"\n value={selectedUserId}\n onChange={(event) => setSelectedUserId(event.target.value)}\n disabled={membersLoading || addingMember || availableMembers.length === 0}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n {availableMembers.length === 0 ? (\n <option value=\"\">\n {tr(\n 'workspaces.addMember.noneAvailable',\n 'No eligible organization members available'\n )}\n </option>\n ) : null}\n {availableMembers.map((member) => (\n <option key={member.id} value={member.userId}>\n {profileMap[member.userId]?.displayName ?? member.userId} ({member.role})\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.filterHelp',\n 'Only members that belong to this organization and are not already in the workspace are listed.'\n )}\n </p>\n {availableMembers.length === 0 && onInviteOrganizationMember ? (\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"mt-2 inline-flex items-center gap-2 text-sm font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.inviteInstead',\n 'Invite a new organization member instead'\n )}\n </button>\n ) : null}\n {onInviteOrganizationMember ? (\n <p className=\"mt-2 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.addMember.bulkInviteHint',\n 'Need to add many people who are not in this organization yet?'\n )}{' '}\n <button\n type=\"button\"\n onClick={() => {\n onOpenChange(false);\n onInviteOrganizationMember();\n }}\n className=\"font-medium text-[var(--color-action-primary-bg)] hover:underline\"\n >\n {tr(\n 'workspaces.addMember.bulkInviteLink',\n 'Bulk-invite them from the organization invite page'\n )}\n </button>\n .\n </p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"workspace-member-role\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.addMember.roleLabel', 'Workspace role')}\n </label>\n <select\n id=\"workspace-member-role\"\n value={selectedRole}\n onChange={(event) =>\n setSelectedRole(event.target.value as 'MEMBER' | 'WORKSPACE_ADMIN')\n }\n disabled={addingMember}\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"MEMBER\">{tr('members.role.member', 'Member')}</option>\n <option value=\"WORKSPACE_ADMIN\">\n {tr('members.role.workspaceAdmin', 'Workspace Admin')}\n </option>\n </select>\n </div>\n\n <ResponsiveDialogFooter className=\"gap-3 sm:gap-0\">\n <button\n type=\"button\"\n onClick={() => onOpenChange(false)}\n className=\"rounded-lg border border-[var(--color-border-default)] px-4 py-2 text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n disabled={addingMember}\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={addingMember || membersLoading || availableMembers.length === 0}\n >\n {addingMember ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {tr('workspaces.addMember.submit', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { WorkspaceStatusBadge as e } from "./WorkspaceStatusBadge.js";
|
|
2
|
+
import { useTr as t } from "../../../shared/hooks/useTr.js";
|
|
3
3
|
import { Briefcase as n, MoreVertical as r } from "lucide-react";
|
|
4
4
|
import { GlassCard as i } from "@burdenoff/fe-libs/ui";
|
|
5
5
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
-
|
|
6
|
+
// src/organization-management/modules/workspaces/components/WorkspaceCard.tsx
|
|
7
7
|
function s({ workspace: s, onClick: c, onEdit: l, onDelete: u, showActions: d = !0 }) {
|
|
8
|
-
let f =
|
|
8
|
+
let f = t(), p = () => {
|
|
9
9
|
c && c(s);
|
|
10
10
|
};
|
|
11
11
|
return /* @__PURE__ */ o(i, {
|
|
@@ -49,7 +49,7 @@ function s({ workspace: s, onClick: c, onEdit: l, onDelete: u, showActions: d =
|
|
|
49
49
|
}),
|
|
50
50
|
/* @__PURE__ */ a("div", {
|
|
51
51
|
className: "flex gap-2 mb-4",
|
|
52
|
-
children: /* @__PURE__ */ a(
|
|
52
|
+
children: /* @__PURE__ */ a(e, { status: s.status })
|
|
53
53
|
}),
|
|
54
54
|
s.memberCount !== void 0 && /* @__PURE__ */ o("p", {
|
|
55
55
|
className: "text-sm text-[var(--color-text-secondary)] mb-4",
|
|
@@ -91,7 +91,6 @@ function s({ workspace: s, onClick: c, onEdit: l, onDelete: u, showActions: d =
|
|
|
91
91
|
]
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
//#endregion
|
|
95
94
|
export { s as WorkspaceCard };
|
|
96
95
|
|
|
97
96
|
//# sourceMappingURL=WorkspaceCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceCard.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceCard.tsx"],"sourcesContent":["import { Briefcase, MoreVertical } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { WorkspaceStatusBadge } from './WorkspaceStatusBadge';\nimport type { Workspace } from '../types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface WorkspaceCardProps {\n workspace: Workspace;\n onClick?: (workspace: Workspace) => void;\n onEdit?: (workspace: Workspace) => void;\n onDelete?: (workspace: Workspace) => void;\n showActions?: boolean;\n}\n\n/**\n * WorkspaceCard\n * Displays a workspace in a card format for grid views\n */\nexport function WorkspaceCard({\n workspace,\n onClick,\n onEdit,\n onDelete,\n showActions = true,\n}: WorkspaceCardProps) {\n const tr = useTr();\n const handleCardClick = () => {\n if (onClick) {\n onClick(workspace);\n }\n };\n\n const handleEdit = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (onEdit) {\n onEdit(workspace);\n }\n };\n\n const handleDelete = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (onDelete) {\n onDelete(workspace);\n }\n };\n\n return (\n // react-doctor-disable-next-line react-doctor/no-static-element-interactions\n <GlassCard\n treatment={onClick ? 'insight' : 'plain'}\n onClick={onClick ? handleCardClick : undefined}\n onKeyDown={\n onClick\n ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n }\n : undefined\n }\n className={`w-full border-border-seam p-6 text-left ${\n onClick\n ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'\n : ''\n }`}\n aria-label={\n onClick ? `${tr('workspaces.card.open', 'Open workspace')} ${workspace.name}` : undefined\n }\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-12 rounded-lg bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Briefcase size={24} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div className=\"flex-1\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] text-lg\">\n {workspace.name}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)] font-mono\">/{workspace.slug}</p>\n </div>\n </div>\n\n {showActions && (\n <button\n type=\"button\"\n onClick={(e) => e.stopPropagation()}\n className=\"\n p-1.5 rounded-lg\n text-[var(--color-text-secondary)]\n hover:bg-[var(--color-bg-subtle)]\n \n transition-all duration-200\n \"\n aria-label={`${tr('workspaces.card.actionsFor', 'Workspace actions for')} ${workspace.name}`}\n >\n <MoreVertical size={18} />\n </button>\n )}\n </div>\n\n {/* Badges */}\n <div className=\"flex gap-2 mb-4\">\n <WorkspaceStatusBadge status={workspace.status} />\n </div>\n\n {/* Member Count */}\n {workspace.memberCount !== undefined && (\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-4\">\n {workspace.memberCount}{' '}\n {workspace.memberCount === 1\n ? tr('members.role.member', 'Member').toLowerCase()\n : tr('workspaces.list.members', 'Members').toLowerCase()}\n </p>\n )}\n\n {/* Metadata */}\n <div className=\"pt-2 border-t border-[var(--color-border-default)]\">\n <p className=\"text-xs text-[var(--color-text-muted)]\">\n {tr('common.created', 'Created')} {new Date(workspace.createdAt).toLocaleDateString()}\n </p>\n </div>\n\n {/* Footer Actions */}\n {showActions && (onEdit || onDelete) && (\n <div className=\"flex gap-2 mt-4 pt-4 border-t border-[var(--color-border-default)]\">\n {onEdit && (\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"\n flex-1 px-3 py-1.5 text-sm\n bg-[var(--color-bg-subtle)]\n text-[var(--color-text-primary)]\n rounded-lg\n hover:bg-[var(--color-bg-muted)]\n \n transition-all duration-200\n font-medium\n \"\n >\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {onDelete && (\n <button\n type=\"button\"\n onClick={handleDelete}\n className=\"\n flex-1 px-3 py-1.5 text-sm\n bg-[var(--color-status-error-muted)]\n text-[var(--color-status-error)]\n rounded-lg\n hover:opacity-80\n \n transition-all duration-200\n font-medium\n \"\n >\n {tr('common.delete', 'Delete')}\n </button>\n )}\n </div>\n )}\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;AAkBA,
|
|
1
|
+
{"version":3,"file":"WorkspaceCard.js","names":["WorkspaceCard","tr","useTr","handleCardClick","onClick","workspace","GlassCard","event","Briefcase","showActions","MoreVertical","WorkspaceStatusBadge","onEdit","onDelete"],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceCard.tsx"],"sourcesContent":["import { Briefcase, MoreVertical } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { WorkspaceStatusBadge } from './WorkspaceStatusBadge';\nimport type { Workspace } from '../types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface WorkspaceCardProps {\n workspace: Workspace;\n onClick?: (workspace: Workspace) => void;\n onEdit?: (workspace: Workspace) => void;\n onDelete?: (workspace: Workspace) => void;\n showActions?: boolean;\n}\n\n/**\n * WorkspaceCard\n * Displays a workspace in a card format for grid views\n */\nexport function WorkspaceCard({\n workspace,\n onClick,\n onEdit,\n onDelete,\n showActions = true,\n}: WorkspaceCardProps) {\n const tr = useTr();\n const handleCardClick = () => {\n if (onClick) {\n onClick(workspace);\n }\n };\n\n const handleEdit = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (onEdit) {\n onEdit(workspace);\n }\n };\n\n const handleDelete = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (onDelete) {\n onDelete(workspace);\n }\n };\n\n return (\n // react-doctor-disable-next-line react-doctor/no-static-element-interactions\n <GlassCard\n treatment={onClick ? 'insight' : 'plain'}\n onClick={onClick ? handleCardClick : undefined}\n onKeyDown={\n onClick\n ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n }\n : undefined\n }\n className={`w-full border-border-seam p-6 text-left ${\n onClick\n ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'\n : ''\n }`}\n aria-label={\n onClick ? `${tr('workspaces.card.open', 'Open workspace')} ${workspace.name}` : undefined\n }\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-12 rounded-lg bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Briefcase size={24} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div className=\"flex-1\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] text-lg\">\n {workspace.name}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)] font-mono\">/{workspace.slug}</p>\n </div>\n </div>\n\n {showActions && (\n <button\n type=\"button\"\n onClick={(e) => e.stopPropagation()}\n className=\"\n p-1.5 rounded-lg\n text-[var(--color-text-secondary)]\n hover:bg-[var(--color-bg-subtle)]\n \n transition-all duration-200\n \"\n aria-label={`${tr('workspaces.card.actionsFor', 'Workspace actions for')} ${workspace.name}`}\n >\n <MoreVertical size={18} />\n </button>\n )}\n </div>\n\n {/* Badges */}\n <div className=\"flex gap-2 mb-4\">\n <WorkspaceStatusBadge status={workspace.status} />\n </div>\n\n {/* Member Count */}\n {workspace.memberCount !== undefined && (\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-4\">\n {workspace.memberCount}{' '}\n {workspace.memberCount === 1\n ? tr('members.role.member', 'Member').toLowerCase()\n : tr('workspaces.list.members', 'Members').toLowerCase()}\n </p>\n )}\n\n {/* Metadata */}\n <div className=\"pt-2 border-t border-[var(--color-border-default)]\">\n <p className=\"text-xs text-[var(--color-text-muted)]\">\n {tr('common.created', 'Created')} {new Date(workspace.createdAt).toLocaleDateString()}\n </p>\n </div>\n\n {/* Footer Actions */}\n {showActions && (onEdit || onDelete) && (\n <div className=\"flex gap-2 mt-4 pt-4 border-t border-[var(--color-border-default)]\">\n {onEdit && (\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"\n flex-1 px-3 py-1.5 text-sm\n bg-[var(--color-bg-subtle)]\n text-[var(--color-text-primary)]\n rounded-lg\n hover:bg-[var(--color-bg-muted)]\n \n transition-all duration-200\n font-medium\n \"\n >\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {onDelete && (\n <button\n type=\"button\"\n onClick={handleDelete}\n className=\"\n flex-1 px-3 py-1.5 text-sm\n bg-[var(--color-status-error-muted)]\n text-[var(--color-status-error)]\n rounded-lg\n hover:opacity-80\n \n transition-all duration-200\n font-medium\n \"\n >\n {tr('common.delete', 'Delete')}\n </button>\n )}\n </div>\n )}\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAgBA,EAAc,EAC5B,cACA,YACA,WACA,aACA,iBAAc,MACO;CACrB,IAAMC,IAAKC,EAAM,GACXC,UAAwB;EAC5B,AAAIC,KACFA,EAAQC,CAAS;CAErB;CAgBA,OAEE,kBAACC,GAAD;EACE,WAAWF,IAAU,YAAY;EACjC,SAASA,IAAUD,IAAkB,KAAA;EACrC,WACEC,KACK,MAAU;GACT,CAAIG,EAAM,QAAQ,WAAWA,EAAM,QAAQ,SACzCA,EAAM,eAAe,GACrBJ,EAAgB;EAEpB,IACA,KAAA;EAEN,WAAW,2CACTC,IACI,gHACA;EAEN,cACEA,IAAU,GAAGH,EAAG,wBAAwB,gBAAgB,EAAE,GAAGI,EAAU,SAAS,KAAA;EAElF,MAAMD,IAAU,WAAW,KAAA;EAC3B,UAAUA,IAAU,IAAI,KAAA;EAtB1B,UAAA;GAyBE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;MACb,UAAA,kBAACI,GAAD;OAAW,MAAM;OAAI,WAAU;MAAmC,CAAA;KAC/D,CAAA,GACL,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;OACX,UAAAH,EAAU;MACT,CAAA,GACJ,kBAAC,KAAD;OAAG,WAAU;OAAb,UAAA,CAAgE,KAAEA,EAAU,IAAQ;MACjF,CAAA,CAAA;KACF,CAAA,CAAA;IAEJ,CAAA,GAAAI,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM,EAAE,gBAAgB;KAClC,WAAU;KAOV,cAAY,GAAGR,EAAG,8BAA8B,uBAAuB,EAAE,GAAGI,EAAU;KAEtF,UAAA,kBAACK,GAAD,EAAc,MAAM,GAAK,CAAA;IACnB,CAAA,CAEP;;GAGL,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAACC,GAAD,EAAsB,QAAQN,EAAU,OAAS,CAAA;GAC9C,CAAA;GAGJA,EAAU,gBAAgB,KAAA,KACzB,kBAAC,KAAD;IAAG,WAAU;IAAb,UAAA;KACGA,EAAU;KAAa;KACvBA,EAAU,gBAAgB,IACvBJ,EAAG,uBAAuB,QAAQ,CAAC,CAAC,YAAY,IAChDA,EAAG,2BAA2B,SAAS,CAAC,CAAC,YAAY;IACxD;;GAIL,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,KAAD;KAAG,WAAU;KAAb,UAAA;MACGA,EAAG,kBAAkB,SAAS;MAAE;MAAE,IAAI,KAAKI,EAAU,SAAS,CAAC,CAAC,mBAAmB;KACnF;;GACA,CAAA;GAGJI,MAAgBG,KAAUC,MACzB,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACGD,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UApGQ,MAAwB;MAE1C,AADA,EAAE,gBAAgB,GACdA,KACFA,EAAOP,CAAS;KAEpB;KAgGY,WAAU;KAWT,UAAAJ,EAAG,eAAe,MAAM;IACnB,CAAA,GAETY,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UA/GU,MAAwB;MAE5C,AADA,EAAE,gBAAgB,GACdA,KACFA,EAASR,CAAS;KAEtB;KA2GY,WAAU;KAWT,UAAAJ,EAAG,iBAAiB,QAAQ;IACvB,CAAA,CAEP;;EAEE;;AAEf"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { ZodError as e } from "../../../../node_modules/zod/v4/classic/errors.js";
|
|
2
|
+
import { createWorkspaceSchema as t, updateWorkspaceSchema as n } from "../schemas/index.js";
|
|
3
|
+
import { useOrganizations as r } from "../../organizations/hooks/useOrganizations.js";
|
|
4
|
+
import { useOrgsWithPermission as i } from "../../organizations/hooks/useOrgsWithPermission.js";
|
|
5
|
+
import { useTenants as a } from "../hooks/useWorkspaces.js";
|
|
6
|
+
import { useTr as o } from "../../../shared/hooks/useTr.js";
|
|
7
7
|
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
8
8
|
import { Building as u, Home as d, Loader2 as f } from "lucide-react";
|
|
9
9
|
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
10
|
-
|
|
10
|
+
// src/organization-management/modules/workspaces/components/WorkspaceForm.tsx
|
|
11
11
|
function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y, showOrganizationSelector: b = !1, onSubmit: x, onCancel: S, loading: C }) {
|
|
12
|
-
let w =
|
|
12
|
+
let w = o(), [T, E] = l({
|
|
13
13
|
name: g?.name || "",
|
|
14
14
|
slug: g?.slug || "",
|
|
15
15
|
tenantId: _ || g?.tenantId || "",
|
|
16
16
|
organizationId: v || g?.organizationId || ""
|
|
17
|
-
}), [D, O] = l({}), k = !g && b && !_, A = !g && b && !v, { tenants: j, loading: M } =
|
|
17
|
+
}), [D, O] = l({}), k = !g && b && !_, A = !g && b && !v, { tenants: j, loading: M } = a(100, 0, k), N = k && j.length > 1;
|
|
18
18
|
s(() => {
|
|
19
19
|
if (!k || T.tenantId || j.length === 0) return;
|
|
20
20
|
let e = j.find((e) => e.type === "SHARED")?.id ?? j[0]?.id;
|
|
@@ -27,10 +27,10 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
27
27
|
j,
|
|
28
28
|
T.tenantId
|
|
29
29
|
]);
|
|
30
|
-
let { organizations: P, loading: F } =
|
|
30
|
+
let { organizations: P, loading: F } = r(T.tenantId ? { tenantId: T.tenantId } : void 0, 100, 0, A), I = c(() => P.map((e) => e.id), [P]), { allowedIds: L, loading: R } = i(I, "workspace", "create"), z = !g && b && !v, B = c(() => z ? P.filter((e) => L.has(e.id)) : P, [
|
|
31
31
|
P,
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
L,
|
|
33
|
+
z
|
|
34
34
|
]);
|
|
35
35
|
s(() => {
|
|
36
36
|
_ && _ !== T.tenantId && E((e) => ({
|
|
@@ -43,37 +43,37 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
43
43
|
organizationId: v
|
|
44
44
|
}));
|
|
45
45
|
}, [T.organizationId, v]), s(() => {
|
|
46
|
-
!b || v || g || E((e) => !e.organizationId ||
|
|
46
|
+
!b || v || g || E((e) => !e.organizationId || B.some((t) => t.id === e.organizationId) ? e : {
|
|
47
47
|
...e,
|
|
48
48
|
organizationId: ""
|
|
49
49
|
});
|
|
50
50
|
}, [
|
|
51
51
|
v,
|
|
52
|
-
|
|
52
|
+
B,
|
|
53
53
|
b,
|
|
54
54
|
g
|
|
55
55
|
]);
|
|
56
|
-
let
|
|
56
|
+
let V = c(() => y || (B.find((e) => e.id === T.organizationId)?.name ?? T.organizationId), [
|
|
57
57
|
T.organizationId,
|
|
58
58
|
y,
|
|
59
|
-
|
|
60
|
-
]),
|
|
59
|
+
B
|
|
60
|
+
]), H = () => {
|
|
61
61
|
try {
|
|
62
|
-
return (g ?
|
|
63
|
-
} catch (
|
|
64
|
-
if (
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
let n =
|
|
68
|
-
|
|
69
|
-
}), O(
|
|
62
|
+
return (g ? n(w) : t(w)).parse(T), O({}), !0;
|
|
63
|
+
} catch (t) {
|
|
64
|
+
if (t instanceof e) {
|
|
65
|
+
let e = {};
|
|
66
|
+
t.issues.forEach((t) => {
|
|
67
|
+
let n = t.path.join(".");
|
|
68
|
+
e[n] = t.message;
|
|
69
|
+
}), O(e);
|
|
70
70
|
}
|
|
71
71
|
return !1;
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
74
|
return /* @__PURE__ */ h("form", {
|
|
75
75
|
onSubmit: async (e) => {
|
|
76
|
-
e.preventDefault(),
|
|
76
|
+
e.preventDefault(), H() && await x(g ? { name: T.name === g.name ? void 0 : T.name } : {
|
|
77
77
|
tenantId: T.tenantId,
|
|
78
78
|
organizationId: T.organizationId,
|
|
79
79
|
name: T.name,
|
|
@@ -178,12 +178,12 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
178
178
|
organizationId: e.target.value
|
|
179
179
|
})),
|
|
180
180
|
className: "w-full appearance-none rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-10 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
181
|
-
disabled: C || F ||
|
|
181
|
+
disabled: C || F || R || !T.tenantId,
|
|
182
182
|
required: !0,
|
|
183
183
|
children: [/* @__PURE__ */ m("option", {
|
|
184
184
|
value: "",
|
|
185
|
-
children: T.tenantId ?
|
|
186
|
-
}),
|
|
185
|
+
children: T.tenantId ? B.length === 0 && !F && !R ? w("workspaces.form.noOrganizationsAvailable", "No organizations available — you need workspace create permission") : w("workspaces.form.selectOrganization", "Select an organization...") : w("workspaces.form.selectTenantFirst", "Select a tenant first")
|
|
186
|
+
}), B.map((e) => /* @__PURE__ */ h("option", {
|
|
187
187
|
value: e.id,
|
|
188
188
|
children: [
|
|
189
189
|
e.name,
|
|
@@ -212,7 +212,7 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
212
212
|
})
|
|
213
213
|
})
|
|
214
214
|
}),
|
|
215
|
-
F ||
|
|
215
|
+
F || R ? /* @__PURE__ */ m("div", {
|
|
216
216
|
className: "absolute right-10 top-1/2 -translate-y-1/2",
|
|
217
217
|
children: /* @__PURE__ */ m(f, {
|
|
218
218
|
size: 16,
|
|
@@ -225,7 +225,7 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
225
225
|
children: [/* @__PURE__ */ m("input", {
|
|
226
226
|
id: "organizationId",
|
|
227
227
|
type: "text",
|
|
228
|
-
value:
|
|
228
|
+
value: V,
|
|
229
229
|
"aria-label": w("common.organization", "Organization"),
|
|
230
230
|
onChange: (e) => E((t) => ({
|
|
231
231
|
...t,
|
|
@@ -341,7 +341,6 @@ function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y,
|
|
|
341
341
|
]
|
|
342
342
|
});
|
|
343
343
|
}
|
|
344
|
-
//#endregion
|
|
345
344
|
export { g as WorkspaceForm };
|
|
346
345
|
|
|
347
346
|
//# sourceMappingURL=WorkspaceForm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceForm.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building, Home, Loader2 } from 'lucide-react';\nimport { ZodError } from 'zod';\nimport type { CreateWorkspaceInput, UpdateWorkspaceInput, Workspace } from '../types';\nimport { createWorkspaceSchema, updateWorkspaceSchema } from '../schemas';\nimport { useOrganizations } from '../../organizations/hooks/useOrganizations';\nimport { useOrgsWithPermission } from '../../organizations/hooks/useOrgsWithPermission';\nimport { useTenants } from '../hooks/useWorkspaces';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface WorkspaceFormProps {\n workspace?: Workspace | null;\n tenantId?: string;\n tenantName?: string;\n organizationId?: string;\n organizationName?: string;\n showOrganizationSelector?: boolean;\n onSubmit: (data: CreateWorkspaceInput | UpdateWorkspaceInput) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\nexport function WorkspaceForm({\n workspace,\n tenantId,\n organizationId,\n organizationName,\n showOrganizationSelector = false,\n onSubmit,\n onCancel,\n loading,\n}: WorkspaceFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n // react-doctor-disable-next-line react-doctor/no-event-handler\n name: workspace?.name || '',\n slug: workspace?.slug || '',\n // react-doctor-disable-next-line react-doctor/no-event-handler\n tenantId: tenantId || workspace?.tenantId || '',\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId: organizationId || workspace?.organizationId || '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n\n const shouldLoadTenants = !workspace && showOrganizationSelector && !tenantId;\n const shouldLoadOrganizations = !workspace && showOrganizationSelector && !organizationId;\n\n const { tenants, loading: tenantsLoading } = useTenants(100, 0, shouldLoadTenants);\n\n // Show the tenant picker only when the user genuinely has more than one\n // tenant to choose between. With a single shared tenant there is nothing to\n // choose, so the picker is hidden and the tenant is auto-selected.\n const showTenantPicker = shouldLoadTenants && tenants.length > 1;\n\n // Auto-select the shared (or only) tenant when there is no explicit tenant\n // and no real choice to make, so the workspace lands in the shared tenant\n // without surfacing a picker.\n useEffect(() => {\n if (!shouldLoadTenants || formData.tenantId || tenants.length === 0) {\n return;\n }\n const sharedTenant = tenants.find((tenant) => tenant.type === 'SHARED');\n const resolved = sharedTenant?.id ?? tenants[0]?.id;\n if (resolved) {\n setFormData((previousState) =>\n previousState.tenantId ? previousState : { ...previousState, tenantId: resolved }\n );\n }\n }, [shouldLoadTenants, tenants, formData.tenantId]);\n const { organizations, loading: organizationsLoading } = useOrganizations(\n formData.tenantId ? { tenantId: formData.tenantId } : undefined,\n 100,\n 0,\n shouldLoadOrganizations\n );\n\n // Hide orgs where the user can't actually create a workspace, so the\n // dropdown only surfaces options the gateway @rbac(workspace:create) will\n // accept. Membership alone isn't enough — a Viewer of an org is a member\n // but has workspace=VIEWER (no create). The bulk checkPermissions call is\n // one round-trip regardless of how many orgs are on the list.\n const allOrgIds = useMemo(\n () => organizations.map((organization) => organization.id),\n [organizations]\n );\n const { allowedIds: workspaceCreateAllowedOrgIds, loading: permissionCheckLoading } =\n useOrgsWithPermission(allOrgIds, 'workspace', 'create');\n const filterByPermission = !workspace && showOrganizationSelector && !organizationId;\n const permittedOrganizations = useMemo(\n () =>\n filterByPermission\n ? organizations.filter((organization) => workspaceCreateAllowedOrgIds.has(organization.id))\n : organizations,\n [organizations, workspaceCreateAllowedOrgIds, filterByPermission]\n );\n\n useEffect(() => {\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (tenantId && tenantId !== formData.tenantId) {\n setFormData((previousState) => ({ ...previousState, tenantId }));\n }\n }, [formData.tenantId, tenantId]);\n\n useEffect(() => {\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (organizationId && organizationId !== formData.organizationId) {\n setFormData((previousState) => ({ ...previousState, organizationId }));\n }\n }, [formData.organizationId, organizationId]);\n\n useEffect(() => {\n if (!showOrganizationSelector || organizationId || workspace) {\n return;\n }\n\n setFormData((previousState) => {\n if (!previousState.organizationId) {\n return previousState;\n }\n\n const hasMatchingOrganization = permittedOrganizations.some(\n (organization) => organization.id === previousState.organizationId\n );\n\n if (hasMatchingOrganization) {\n return previousState;\n }\n\n return { ...previousState, organizationId: '' };\n });\n }, [organizationId, permittedOrganizations, showOrganizationSelector, workspace]);\n\n const selectedOrganizationLabel = useMemo(() => {\n if (organizationName) return organizationName;\n\n const selectedOrganization = permittedOrganizations.find(\n (organization) => organization.id === formData.organizationId\n );\n return selectedOrganization?.name ?? formData.organizationId;\n }, [formData.organizationId, organizationName, permittedOrganizations]);\n\n const validate = () => {\n try {\n const schema = workspace ? updateWorkspaceSchema(tr) : createWorkspaceSchema(tr);\n schema.parse(formData);\n setErrors({});\n return true;\n } catch (validationError) {\n if (validationError instanceof ZodError) {\n const nextErrors: Record<string, string> = {};\n validationError.issues.forEach((issue) => {\n const path = issue.path.join('.');\n nextErrors[path] = issue.message;\n });\n setErrors(nextErrors);\n }\n\n return false;\n }\n };\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!validate()) {\n return;\n }\n\n const submitData = workspace\n ? {\n name: formData.name !== workspace.name ? formData.name : undefined,\n }\n : {\n tenantId: formData.tenantId,\n organizationId: formData.organizationId,\n name: formData.name,\n slug: formData.slug,\n };\n\n await onSubmit(submitData);\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {!workspace ? (\n <>\n {/* Tenant picker — shown ONLY when the user has more than one tenant\n to choose between. For single-tenant (shared) users the tenant is\n auto-selected and no picker is rendered. */}\n {showTenantPicker ? (\n <div>\n <label\n htmlFor=\"tenantId\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('organizations.form.tenant', 'Tenant')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n\n <div className=\"relative\">\n <select\n id=\"tenantId\"\n value={formData.tenantId}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n tenantId: event.target.value,\n organizationId: '',\n }))\n }\n className=\"w-full appearance-none rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-10 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={loading || tenantsLoading}\n required\n >\n <option value=\"\">\n {tr('workspaces.form.selectTenant', 'Select a tenant...')}\n </option>\n {tenants.map((tenant) => (\n <option key={tenant.id} value={tenant.id}>\n {tenant.name} ({tenant.slug})\n </option>\n ))}\n </select>\n <Home\n size={18}\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n <div className=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </div>\n {tenantsLoading ? (\n <div className=\"absolute right-10 top-1/2 -translate-y-1/2\">\n <Loader2 size={16} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : null}\n </div>\n {errors.tenantId ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">\n {errors.tenantId}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"organizationId\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('common.organization', 'Organization')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n\n {showOrganizationSelector && !organizationId ? (\n <div className=\"relative\">\n <select\n id=\"organizationId\"\n value={formData.organizationId}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n organizationId: event.target.value,\n }))\n }\n className=\"w-full appearance-none rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-10 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={\n loading || organizationsLoading || permissionCheckLoading || !formData.tenantId\n }\n required\n >\n <option value=\"\">\n {!formData.tenantId\n ? tr('workspaces.form.selectTenantFirst', 'Select a tenant first')\n : permittedOrganizations.length === 0 &&\n !organizationsLoading &&\n !permissionCheckLoading\n ? tr(\n 'workspaces.form.noOrganizationsAvailable',\n 'No organizations available — you need workspace create permission'\n )\n : tr('workspaces.form.selectOrganization', 'Select an organization...')}\n </option>\n {permittedOrganizations.map((organization) => (\n <option key={organization.id} value={organization.id}>\n {organization.name} ({organization.slug})\n </option>\n ))}\n </select>\n <Building\n size={18}\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n <div className=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </div>\n {organizationsLoading || permissionCheckLoading ? (\n <div className=\"absolute right-10 top-1/2 -translate-y-1/2\">\n <Loader2 size={16} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : null}\n </div>\n ) : (\n <div className=\"relative\">\n <input\n id=\"organizationId\"\n type=\"text\"\n value={selectedOrganizationLabel}\n aria-label={tr('common.organization', 'Organization')}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n organizationId: event.target.value,\n }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-4 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.orgIdPlaceholder', 'Enter organization ID')}\n disabled={loading || Boolean(organizationId)}\n required\n autoComplete=\"off\"\n />\n <Building\n size={18}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n </div>\n )}\n\n {(organizationId || formData.organizationId) && (\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.form.creatingUnderOrg',\n 'Creating workspace under the selected organization.'\n )}\n </p>\n )}\n {errors.organizationId ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">\n {errors.organizationId}\n </p>\n ) : null}\n </div>\n </>\n ) : null}\n\n <div>\n <label\n htmlFor=\"name\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.form.name', 'Workspace Name')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n aria-label={tr('workspaces.form.name', 'Workspace Name')}\n onChange={(event) =>\n setFormData((previousState) => ({ ...previousState, name: event.target.value }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.namePlaceholder', 'Enter workspace name')}\n disabled={loading}\n required\n autoComplete=\"organization-title\"\n />\n {errors.name ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">{errors.name}</p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"slug\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('common.slug', 'Slug')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <input\n id=\"slug\"\n type=\"text\"\n value={formData.slug}\n aria-label={tr('common.slug', 'Slug')}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n slug: event.target.value.toLowerCase().replace(/\\s+/g, '-'),\n }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.slugPlaceholder', 'workspace-slug')}\n disabled={loading || Boolean(workspace)}\n required={!workspace}\n autoComplete=\"off\"\n />\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'organizations.form.slugImmutable',\n 'Lowercase letters, numbers, and hyphens only. Cannot be changed after creation.'\n )}\n </p>\n {errors.slug ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">{errors.slug}</p>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[220px]\"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {workspace\n ? tr('workspaces.form.updateSubmit', 'Update Workspace')\n : tr('workspaces.create.button', 'Create Workspace')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[140px]\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkspaceForm.js","names":["_Fragment","WorkspaceForm","tr","useTr","formData","setFormData","useState","workspace","tenantId","organizationId","errors","setErrors","shouldLoadTenants","showOrganizationSelector","shouldLoadOrganizations","tenantsLoading","useTenants","showTenantPicker","tenants","resolved","tenant","previousState","organizationsLoading","useOrganizations","allOrgIds","useMemo","organizations","organization","workspaceCreateAllowedOrgIds","permissionCheckLoading","useOrgsWithPermission","filterByPermission","permittedOrganizations","useEffect","selectedOrganizationLabel","organizationName","validate","updateWorkspaceSchema","createWorkspaceSchema","validationError","ZodError","nextErrors","path","issue","event","onSubmit","Fragment","loading","Home","Loader2","Building","onCancel"],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building, Home, Loader2 } from 'lucide-react';\nimport { ZodError } from 'zod';\nimport type { CreateWorkspaceInput, UpdateWorkspaceInput, Workspace } from '../types';\nimport { createWorkspaceSchema, updateWorkspaceSchema } from '../schemas';\nimport { useOrganizations } from '../../organizations/hooks/useOrganizations';\nimport { useOrgsWithPermission } from '../../organizations/hooks/useOrgsWithPermission';\nimport { useTenants } from '../hooks/useWorkspaces';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface WorkspaceFormProps {\n workspace?: Workspace | null;\n tenantId?: string;\n tenantName?: string;\n organizationId?: string;\n organizationName?: string;\n showOrganizationSelector?: boolean;\n onSubmit: (data: CreateWorkspaceInput | UpdateWorkspaceInput) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\nexport function WorkspaceForm({\n workspace,\n tenantId,\n organizationId,\n organizationName,\n showOrganizationSelector = false,\n onSubmit,\n onCancel,\n loading,\n}: WorkspaceFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n // react-doctor-disable-next-line react-doctor/no-event-handler\n name: workspace?.name || '',\n slug: workspace?.slug || '',\n // react-doctor-disable-next-line react-doctor/no-event-handler\n tenantId: tenantId || workspace?.tenantId || '',\n // react-doctor-disable-next-line react-doctor/no-event-handler\n organizationId: organizationId || workspace?.organizationId || '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n\n const shouldLoadTenants = !workspace && showOrganizationSelector && !tenantId;\n const shouldLoadOrganizations = !workspace && showOrganizationSelector && !organizationId;\n\n const { tenants, loading: tenantsLoading } = useTenants(100, 0, shouldLoadTenants);\n\n // Show the tenant picker only when the user genuinely has more than one\n // tenant to choose between. With a single shared tenant there is nothing to\n // choose, so the picker is hidden and the tenant is auto-selected.\n const showTenantPicker = shouldLoadTenants && tenants.length > 1;\n\n // Auto-select the shared (or only) tenant when there is no explicit tenant\n // and no real choice to make, so the workspace lands in the shared tenant\n // without surfacing a picker.\n useEffect(() => {\n if (!shouldLoadTenants || formData.tenantId || tenants.length === 0) {\n return;\n }\n const sharedTenant = tenants.find((tenant) => tenant.type === 'SHARED');\n const resolved = sharedTenant?.id ?? tenants[0]?.id;\n if (resolved) {\n setFormData((previousState) =>\n previousState.tenantId ? previousState : { ...previousState, tenantId: resolved }\n );\n }\n }, [shouldLoadTenants, tenants, formData.tenantId]);\n const { organizations, loading: organizationsLoading } = useOrganizations(\n formData.tenantId ? { tenantId: formData.tenantId } : undefined,\n 100,\n 0,\n shouldLoadOrganizations\n );\n\n // Hide orgs where the user can't actually create a workspace, so the\n // dropdown only surfaces options the gateway @rbac(workspace:create) will\n // accept. Membership alone isn't enough — a Viewer of an org is a member\n // but has workspace=VIEWER (no create). The bulk checkPermissions call is\n // one round-trip regardless of how many orgs are on the list.\n const allOrgIds = useMemo(\n () => organizations.map((organization) => organization.id),\n [organizations]\n );\n const { allowedIds: workspaceCreateAllowedOrgIds, loading: permissionCheckLoading } =\n useOrgsWithPermission(allOrgIds, 'workspace', 'create');\n const filterByPermission = !workspace && showOrganizationSelector && !organizationId;\n const permittedOrganizations = useMemo(\n () =>\n filterByPermission\n ? organizations.filter((organization) => workspaceCreateAllowedOrgIds.has(organization.id))\n : organizations,\n [organizations, workspaceCreateAllowedOrgIds, filterByPermission]\n );\n\n useEffect(() => {\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (tenantId && tenantId !== formData.tenantId) {\n setFormData((previousState) => ({ ...previousState, tenantId }));\n }\n }, [formData.tenantId, tenantId]);\n\n useEffect(() => {\n // react-doctor-disable-next-line react-doctor/no-event-handler\n if (organizationId && organizationId !== formData.organizationId) {\n setFormData((previousState) => ({ ...previousState, organizationId }));\n }\n }, [formData.organizationId, organizationId]);\n\n useEffect(() => {\n if (!showOrganizationSelector || organizationId || workspace) {\n return;\n }\n\n setFormData((previousState) => {\n if (!previousState.organizationId) {\n return previousState;\n }\n\n const hasMatchingOrganization = permittedOrganizations.some(\n (organization) => organization.id === previousState.organizationId\n );\n\n if (hasMatchingOrganization) {\n return previousState;\n }\n\n return { ...previousState, organizationId: '' };\n });\n }, [organizationId, permittedOrganizations, showOrganizationSelector, workspace]);\n\n const selectedOrganizationLabel = useMemo(() => {\n if (organizationName) return organizationName;\n\n const selectedOrganization = permittedOrganizations.find(\n (organization) => organization.id === formData.organizationId\n );\n return selectedOrganization?.name ?? formData.organizationId;\n }, [formData.organizationId, organizationName, permittedOrganizations]);\n\n const validate = () => {\n try {\n const schema = workspace ? updateWorkspaceSchema(tr) : createWorkspaceSchema(tr);\n schema.parse(formData);\n setErrors({});\n return true;\n } catch (validationError) {\n if (validationError instanceof ZodError) {\n const nextErrors: Record<string, string> = {};\n validationError.issues.forEach((issue) => {\n const path = issue.path.join('.');\n nextErrors[path] = issue.message;\n });\n setErrors(nextErrors);\n }\n\n return false;\n }\n };\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n\n if (!validate()) {\n return;\n }\n\n const submitData = workspace\n ? {\n name: formData.name !== workspace.name ? formData.name : undefined,\n }\n : {\n tenantId: formData.tenantId,\n organizationId: formData.organizationId,\n name: formData.name,\n slug: formData.slug,\n };\n\n await onSubmit(submitData);\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {!workspace ? (\n <>\n {/* Tenant picker — shown ONLY when the user has more than one tenant\n to choose between. For single-tenant (shared) users the tenant is\n auto-selected and no picker is rendered. */}\n {showTenantPicker ? (\n <div>\n <label\n htmlFor=\"tenantId\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('organizations.form.tenant', 'Tenant')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n\n <div className=\"relative\">\n <select\n id=\"tenantId\"\n value={formData.tenantId}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n tenantId: event.target.value,\n organizationId: '',\n }))\n }\n className=\"w-full appearance-none rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-10 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={loading || tenantsLoading}\n required\n >\n <option value=\"\">\n {tr('workspaces.form.selectTenant', 'Select a tenant...')}\n </option>\n {tenants.map((tenant) => (\n <option key={tenant.id} value={tenant.id}>\n {tenant.name} ({tenant.slug})\n </option>\n ))}\n </select>\n <Home\n size={18}\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n <div className=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </div>\n {tenantsLoading ? (\n <div className=\"absolute right-10 top-1/2 -translate-y-1/2\">\n <Loader2 size={16} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : null}\n </div>\n {errors.tenantId ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">\n {errors.tenantId}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <div>\n <label\n htmlFor=\"organizationId\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('common.organization', 'Organization')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n\n {showOrganizationSelector && !organizationId ? (\n <div className=\"relative\">\n <select\n id=\"organizationId\"\n value={formData.organizationId}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n organizationId: event.target.value,\n }))\n }\n className=\"w-full appearance-none rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-10 text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)] scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={\n loading || organizationsLoading || permissionCheckLoading || !formData.tenantId\n }\n required\n >\n <option value=\"\">\n {!formData.tenantId\n ? tr('workspaces.form.selectTenantFirst', 'Select a tenant first')\n : permittedOrganizations.length === 0 &&\n !organizationsLoading &&\n !permissionCheckLoading\n ? tr(\n 'workspaces.form.noOrganizationsAvailable',\n 'No organizations available — you need workspace create permission'\n )\n : tr('workspaces.form.selectOrganization', 'Select an organization...')}\n </option>\n {permittedOrganizations.map((organization) => (\n <option key={organization.id} value={organization.id}>\n {organization.name} ({organization.slug})\n </option>\n ))}\n </select>\n <Building\n size={18}\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n <div className=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </div>\n {organizationsLoading || permissionCheckLoading ? (\n <div className=\"absolute right-10 top-1/2 -translate-y-1/2\">\n <Loader2 size={16} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : null}\n </div>\n ) : (\n <div className=\"relative\">\n <input\n id=\"organizationId\"\n type=\"text\"\n value={selectedOrganizationLabel}\n aria-label={tr('common.organization', 'Organization')}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n organizationId: event.target.value,\n }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] py-2 pl-10 pr-4 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.orgIdPlaceholder', 'Enter organization ID')}\n disabled={loading || Boolean(organizationId)}\n required\n autoComplete=\"off\"\n />\n <Building\n size={18}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]\"\n />\n </div>\n )}\n\n {(organizationId || formData.organizationId) && (\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.form.creatingUnderOrg',\n 'Creating workspace under the selected organization.'\n )}\n </p>\n )}\n {errors.organizationId ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">\n {errors.organizationId}\n </p>\n ) : null}\n </div>\n </>\n ) : null}\n\n <div>\n <label\n htmlFor=\"name\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.form.name', 'Workspace Name')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n aria-label={tr('workspaces.form.name', 'Workspace Name')}\n onChange={(event) =>\n setFormData((previousState) => ({ ...previousState, name: event.target.value }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.namePlaceholder', 'Enter workspace name')}\n disabled={loading}\n required\n autoComplete=\"organization-title\"\n />\n {errors.name ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">{errors.name}</p>\n ) : null}\n </div>\n\n <div>\n <label\n htmlFor=\"slug\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('common.slug', 'Slug')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <input\n id=\"slug\"\n type=\"text\"\n value={formData.slug}\n aria-label={tr('common.slug', 'Slug')}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n slug: event.target.value.toLowerCase().replace(/\\s+/g, '-'),\n }))\n }\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-3 py-2 text-[var(--color-text-primary)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n placeholder={tr('workspaces.form.slugPlaceholder', 'workspace-slug')}\n disabled={loading || Boolean(workspace)}\n required={!workspace}\n autoComplete=\"off\"\n />\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'organizations.form.slugImmutable',\n 'Lowercase letters, numbers, and hyphens only. Cannot be changed after creation.'\n )}\n </p>\n {errors.slug ? (\n <p className=\"mt-1 text-sm text-[var(--color-status-error-text)]\">{errors.slug}</p>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[220px]\"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : null}\n {workspace\n ? tr('workspaces.form.updateSubmit', 'Update Workspace')\n : tr('workspaces.create.button', 'Create Workspace')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[140px]\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { useCallback as t } from "react";
|
|
|
3
3
|
import { CircleAlert as n, CircleCheck as r, CircleX as i } from "lucide-react";
|
|
4
4
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
5
|
import { useI18n as s } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
-
|
|
6
|
+
// src/organization-management/modules/workspaces/components/WorkspaceStatusBadge.tsx
|
|
7
7
|
function c({ status: c, showIcon: l = !0 }) {
|
|
8
8
|
let { t: u } = s(), d = t((e, t) => {
|
|
9
9
|
let n = u(e);
|
|
@@ -47,7 +47,6 @@ function c({ status: c, showIcon: l = !0 }) {
|
|
|
47
47
|
}), f.label]
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
//#endregion
|
|
51
50
|
export { c as WorkspaceStatusBadge };
|
|
52
51
|
|
|
53
52
|
//# sourceMappingURL=WorkspaceStatusBadge.js.map
|
package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceStatusBadge.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceStatusBadge.tsx"],"sourcesContent":["import { CircleCheck, CircleX, CircleAlert } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WorkspaceStatus } from '../types';\n\ninterface WorkspaceStatusBadgeProps {\n status: WorkspaceStatus;\n showIcon?: boolean;\n}\n\n/**\n * WorkspaceStatusBadge\n * Displays the status of a workspace with color coding and optional icon\n */\nexport function WorkspaceStatusBadge({ status, showIcon = true }: WorkspaceStatusBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const getStatusConfig = () => {\n switch (status) {\n case WorkspaceStatus.ACTIVE:\n return {\n label: tr('workspaces.status.active', 'Active'),\n icon: CircleCheck,\n pulse: true,\n className:\n 'bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)] border-[var(--color-status-success-border)]',\n };\n case WorkspaceStatus.INACTIVE:\n return {\n label: tr('workspaces.status.inactive', 'Inactive'),\n icon: CircleX,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n case WorkspaceStatus.ARCHIVED:\n return {\n label: tr('workspaces.status.archived', 'Archived'),\n icon: CircleAlert,\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border-[var(--color-status-warning-border)]',\n };\n default:\n return {\n label: status,\n icon: CircleAlert,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n }\n };\n\n const config = getStatusConfig();\n const Icon = config.icon;\n\n return (\n <span\n className={`\n inline-flex items-center gap-1.5 px-2.5 py-1\n rounded-full text-xs font-medium\n border\n transition-colors duration-200\n ${config.className}\n `}\n >\n {showIcon && (\n <Icon\n size={14}\n className={'pulse' in config && config.pulse ? 'motion-safe:animate-vc-glow-pulse' : ''}\n />\n )}\n {config.label}\n </span>\n );\n}\n"],"mappings":";;;;;;AAcA,
|
|
1
|
+
{"version":3,"file":"WorkspaceStatusBadge.js","names":["WorkspaceStatusBadge","useI18n","tr","useCallback","translated","t","key","fallback","config","status","WorkspaceStatus","CircleCheck","CircleX","CircleAlert","Icon","showIcon"],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceStatusBadge.tsx"],"sourcesContent":["import { CircleCheck, CircleX, CircleAlert } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WorkspaceStatus } from '../types';\n\ninterface WorkspaceStatusBadgeProps {\n status: WorkspaceStatus;\n showIcon?: boolean;\n}\n\n/**\n * WorkspaceStatusBadge\n * Displays the status of a workspace with color coding and optional icon\n */\nexport function WorkspaceStatusBadge({ status, showIcon = true }: WorkspaceStatusBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const getStatusConfig = () => {\n switch (status) {\n case WorkspaceStatus.ACTIVE:\n return {\n label: tr('workspaces.status.active', 'Active'),\n icon: CircleCheck,\n pulse: true,\n className:\n 'bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)] border-[var(--color-status-success-border)]',\n };\n case WorkspaceStatus.INACTIVE:\n return {\n label: tr('workspaces.status.inactive', 'Inactive'),\n icon: CircleX,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n case WorkspaceStatus.ARCHIVED:\n return {\n label: tr('workspaces.status.archived', 'Archived'),\n icon: CircleAlert,\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border-[var(--color-status-warning-border)]',\n };\n default:\n return {\n label: status,\n icon: CircleAlert,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n }\n };\n\n const config = getStatusConfig();\n const Icon = config.icon;\n\n return (\n <span\n className={`\n inline-flex items-center gap-1.5 px-2.5 py-1\n rounded-full text-xs font-medium\n border\n transition-colors duration-200\n ${config.className}\n `}\n >\n {showIcon && (\n <Icon\n size={14}\n className={'pulse' in config && config.pulse ? 'motion-safe:animate-vc-glow-pulse' : ''}\n />\n )}\n {config.label}\n </span>\n );\n}\n"],"mappings":";;;;;;AAcA,SAAgBA,EAAqB,EAAE,WAAQ,cAAW,MAAmC;CAC3F,IAAM,EAAE,SAAMC,EAAQ,GAChBC,IAAKC,GACR,GAAa,MAAqB;EACjC,IAAMC,IAAaC,EAAEC,CAAG;EACxB,OAAOF,MAAeE,IAAMC,IAAWH;CACzC,GACA,CAACC,CAAC,CACJ,GAmCMG,WAlCwB;EAC5B,QAAQC,GAAR;GACE,KAAKC,EAAgB,QACnB,OAAO;IACL,OAAOR,EAAG,4BAA4B,QAAQ;IAC9C,MAAMS;IACN,OAAO;IACP,WACE;GACJ;GACF,KAAKD,EAAgB,UACnB,OAAO;IACL,OAAOR,EAAG,8BAA8B,UAAU;IAClD,MAAMU;IACN,WACE;GACJ;GACF,KAAKF,EAAgB,UACnB,OAAO;IACL,OAAOR,EAAG,8BAA8B,UAAU;IAClD,MAAMW;IACN,WACE;GACJ;GACF,SACE,OAAO;IACL,OAAOJ;IACP,MAAMI;IACN,WACE;GACJ;EACJ;CACF,EAEe,CAAgB,GACzBC,IAAON,EAAO;CAEpB,OACE,kBAAC,QAAD;EACE,WAAW;;;;;UAKPA,EAAO,UAAU;;EANvB,UAAA,CASGO,KACC,kBAACD,GAAD;GACE,MAAM;GACN,WAAW,WAAWN,KAAUA,EAAO,QAAQ,sCAAsC;EACtF,CAAA,GAEFA,EAAO,KACJ;;AAEV"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GetWorkspaceMembersDocument as e, useAddWorkspaceMemberMutation as t, useGetWorkspaceMembersQuery as n, useRemoveWorkspaceMemberMutation as r } from "../../../../generated/global-operations.js";
|
|
2
|
-
|
|
2
|
+
// src/organization-management/modules/workspaces/hooks/useWorkspaceMembers.ts
|
|
3
3
|
function i(e, t = 50, r = 0) {
|
|
4
4
|
let { data: i, loading: a, error: o, refetch: s, fetchMore: c } = n({
|
|
5
5
|
variables: {
|
|
@@ -40,7 +40,6 @@ function o() {
|
|
|
40
40
|
error: i
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
//#endregion
|
|
44
43
|
export { a as useAddWorkspaceMember, o as useRemoveWorkspaceMember, i as useWorkspaceMembers };
|
|
45
44
|
|
|
46
45
|
//# sourceMappingURL=useWorkspaceMembers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWorkspaceMembers.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaceMembers.ts"],"sourcesContent":["import {\n GetWorkspaceMembersDocument,\n useAddWorkspaceMemberMutation,\n useGetWorkspaceMembersQuery,\n useRemoveWorkspaceMemberMutation,\n} from '../../../../generated/global-operations';\nimport type { AddWorkspaceMemberInput, WorkspaceMember } from '../types';\n\n/**\n * Hook to fetch workspace members with pagination\n * Requires an explicit workspace ID from the route/page context.\n *\n * @param limit - Number of members to fetch (default: 50)\n * @param offset - Offset for pagination (default: 0)\n * @returns Query result with members, loading state, and error\n */\nexport function useWorkspaceMembers(workspaceId: string, limit = 50, offset = 0) {\n const { data, loading, error, refetch, fetchMore } = useGetWorkspaceMembersQuery({\n variables: {\n workspaceId,\n limit,\n offset,\n },\n skip: !workspaceId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.workspaceMembers?.items as WorkspaceMember[]) ?? [],\n total: data?.workspaceMembers?.total ?? 0,\n hasMore: data?.workspaceMembers?.hasMore ?? false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\nexport function useAddWorkspaceMember() {\n const [addWorkspaceMember, { loading, error }] = useAddWorkspaceMemberMutation({\n refetchQueries: [GetWorkspaceMembersDocument],\n });\n\n return {\n addWorkspaceMember: async (input: AddWorkspaceMemberInput) => {\n return addWorkspaceMember({ variables: { input } });\n },\n loading,\n error,\n };\n}\n\nexport function useRemoveWorkspaceMember() {\n const [removeWorkspaceMember, { loading, error }] = useRemoveWorkspaceMemberMutation({\n refetchQueries: [GetWorkspaceMembersDocument],\n });\n\n return {\n removeWorkspaceMember: async (workspaceId: string, userId: string) => {\n return removeWorkspaceMember({ variables: { workspaceId, userId } });\n },\n loading,\n error,\n };\n}\n"],"mappings":";;AAgBA,
|
|
1
|
+
{"version":3,"file":"useWorkspaceMembers.js","names":["useWorkspaceMembers","useGetWorkspaceMembersQuery","workspaceId","data","useAddWorkspaceMember","addWorkspaceMember","useAddWorkspaceMemberMutation","GetWorkspaceMembersDocument","useRemoveWorkspaceMember","removeWorkspaceMember","useRemoveWorkspaceMemberMutation"],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaceMembers.ts"],"sourcesContent":["import {\n GetWorkspaceMembersDocument,\n useAddWorkspaceMemberMutation,\n useGetWorkspaceMembersQuery,\n useRemoveWorkspaceMemberMutation,\n} from '../../../../generated/global-operations';\nimport type { AddWorkspaceMemberInput, WorkspaceMember } from '../types';\n\n/**\n * Hook to fetch workspace members with pagination\n * Requires an explicit workspace ID from the route/page context.\n *\n * @param limit - Number of members to fetch (default: 50)\n * @param offset - Offset for pagination (default: 0)\n * @returns Query result with members, loading state, and error\n */\nexport function useWorkspaceMembers(workspaceId: string, limit = 50, offset = 0) {\n const { data, loading, error, refetch, fetchMore } = useGetWorkspaceMembersQuery({\n variables: {\n workspaceId,\n limit,\n offset,\n },\n skip: !workspaceId,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n members: (data?.workspaceMembers?.items as WorkspaceMember[]) ?? [],\n total: data?.workspaceMembers?.total ?? 0,\n hasMore: data?.workspaceMembers?.hasMore ?? false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\nexport function useAddWorkspaceMember() {\n const [addWorkspaceMember, { loading, error }] = useAddWorkspaceMemberMutation({\n refetchQueries: [GetWorkspaceMembersDocument],\n });\n\n return {\n addWorkspaceMember: async (input: AddWorkspaceMemberInput) => {\n return addWorkspaceMember({ variables: { input } });\n },\n loading,\n error,\n };\n}\n\nexport function useRemoveWorkspaceMember() {\n const [removeWorkspaceMember, { loading, error }] = useRemoveWorkspaceMemberMutation({\n refetchQueries: [GetWorkspaceMembersDocument],\n });\n\n return {\n removeWorkspaceMember: async (workspaceId: string, userId: string) => {\n return removeWorkspaceMember({ variables: { workspaceId, userId } });\n },\n loading,\n error,\n };\n}\n"],"mappings":";;AAgBA,SAAgBA,EAAoB,GAAqB,IAAQ,IAAI,IAAS,GAAG;CAC/E,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAcC,EAA4B;EAC/E,WAAW;GACT;GACA;GACA;EACF;EACA,MAAM,CAACC;EACP,aAAa;EACb,iBAAiB;CACnB,CAAC;CAED,OAAO;EACL,SAAUC,GAAM,kBAAkB,SAA+B,CAAC;EAClE,OAAOA,GAAM,kBAAkB,SAAS;EACxC,SAASA,GAAM,kBAAkB,WAAW;EAC5C;EACA;EACA;EACA;CACF;AACF;AAEA,SAAgBC,IAAwB;CACtC,IAAM,CAACC,GAAoB,EAAE,YAAS,cAAWC,EAA8B,EAC7E,gBAAgB,CAACC,CAA2B,EAC9C,CAAC;CAED,OAAO;EACL,oBAAoB,OAAO,MAClBF,EAAmB,EAAE,WAAW,EAAE,SAAM,EAAE,CAAC;EAEpD;EACA;CACF;AACF;AAEA,SAAgBG,IAA2B;CACzC,IAAM,CAACC,GAAuB,EAAE,YAAS,cAAWC,EAAiC,EACnF,gBAAgB,CAACH,CAA2B,EAC9C,CAAC;CAED,OAAO;EACL,uBAAuB,OAAO,GAAqB,MAC1CE,EAAsB,EAAE,WAAW;GAAE;GAAa;EAAO,EAAE,CAAC;EAErE;EACA;CACF;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GetWorkspacesDocument as e, useCreateWorkspaceMutation as t, useGetMyWorkspacesQuery as n, useGetTenantsQuery as r, useGetWorkspaceQuery as i, useGetWorkspacesQuery as a, useSuspendWorkspaceMutation as o, useUpdateWorkspaceMutation as s } from "../../../../generated/global-operations.js";
|
|
2
|
-
|
|
2
|
+
// src/organization-management/modules/workspaces/hooks/useWorkspaces.ts
|
|
3
3
|
function c(e, t = 20, r = 0, i = "all") {
|
|
4
4
|
let o = a({
|
|
5
5
|
variables: {
|
|
@@ -46,23 +46,23 @@ function l(e) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
function u(e, t = 20, r = 0) {
|
|
49
|
-
let { data:
|
|
49
|
+
let i = Math.max(t + r, 200), { data: a, loading: o, error: s, refetch: c, fetchMore: l } = n({
|
|
50
50
|
variables: {
|
|
51
|
-
limit:
|
|
51
|
+
limit: i,
|
|
52
52
|
offset: 0
|
|
53
53
|
},
|
|
54
54
|
skip: !e,
|
|
55
55
|
fetchPolicy: "cache-and-network",
|
|
56
56
|
nextFetchPolicy: "cache-first"
|
|
57
|
-
}),
|
|
57
|
+
}), u = (a?.myWorkspaces?.items ?? []).filter((t) => t.organizationId === e), d = u.slice(r, r + t);
|
|
58
58
|
return {
|
|
59
|
-
workspaces:
|
|
60
|
-
total:
|
|
61
|
-
hasMore: r +
|
|
62
|
-
loading:
|
|
63
|
-
error:
|
|
64
|
-
refetch:
|
|
65
|
-
fetchMore:
|
|
59
|
+
workspaces: d,
|
|
60
|
+
total: u.length,
|
|
61
|
+
hasMore: r + d.length < u.length,
|
|
62
|
+
loading: o,
|
|
63
|
+
error: s,
|
|
64
|
+
refetch: c,
|
|
65
|
+
fetchMore: l
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
function d() {
|
|
@@ -113,7 +113,6 @@ function m(e = 100, t = 0, n = !0) {
|
|
|
113
113
|
refetch: s
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
-
//#endregion
|
|
117
116
|
export { d as useCreateWorkspace, p as useDeleteWorkspace, m as useTenants, f as useUpdateWorkspace, l as useWorkspace, c as useWorkspaces, u as useWorkspacesByOrganization };
|
|
118
117
|
|
|
119
118
|
//# sourceMappingURL=useWorkspaces.js.map
|