@burdenoff/microfe-organization 2026.608.1 → 2026.616.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 +78 -63
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +13 -9
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.js +79 -65
- package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +287 -0
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -0
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js +112 -112
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +8 -7
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +2 -5
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +86 -134
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +4 -18
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +4 -22
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -54,12 +54,9 @@ function o({ workspace: o, onClick: s, onEdit: c, onDelete: l, showActions: u =
|
|
|
54
54
|
children: /* @__PURE__ */ i(r, { size: 18 })
|
|
55
55
|
})]
|
|
56
56
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ i("div", {
|
|
58
58
|
className: "flex gap-2 mb-4",
|
|
59
|
-
children:
|
|
60
|
-
className: "inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-[var(--color-bg-subtle)] text-[var(--color-text-secondary)]",
|
|
61
|
-
children: o.type
|
|
62
|
-
})]
|
|
59
|
+
children: /* @__PURE__ */ i(t, { status: o.status })
|
|
63
60
|
}),
|
|
64
61
|
o.memberCount !== void 0 && /* @__PURE__ */ a("p", {
|
|
65
62
|
className: "text-sm text-[var(--color-text-secondary)] mb-4",
|
|
@@ -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 { 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 <div\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={`\n w-full text-left\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n p-6\n shadow-[var(--shadow-elevation-1)]\n transition-all\n ${onClick ? 'cursor-pointer hover:shadow-[var(--shadow-elevation-2)] hover:border-[var(--color-border-hover)]' : ''}\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 active:scale-95\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
|
|
1
|
+
{"version":3,"file":"WorkspaceCard.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/components/WorkspaceCard.tsx"],"sourcesContent":["import { Briefcase, MoreVertical } from 'lucide-react';\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 <div\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={`\n w-full text-left\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n p-6\n shadow-[var(--shadow-elevation-1)]\n transition-all\n ${onClick ? 'cursor-pointer hover:shadow-[var(--shadow-elevation-2)] hover:border-[var(--color-border-hover)]' : ''}\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 active:scale-95\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 active:scale-[0.98]\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 active:scale-[0.98]\n transition-all duration-200\n font-medium\n \"\n >\n {tr('common.delete', 'Delete')}\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAiBA,SAAgB,EAAc,EAC5B,cACA,YACA,WACA,aACA,iBAAc,MACO;CACrB,IAAM,IAAK,GAAO,EACZ,UAAwB;AAC5B,EAAI,KACF,EAAQ,EAAU;;AAkBtB,QAEE,kBAAC,OAAD;EACE,SAAS,IAAU,IAAkB,KAAA;EACrC,WACE,KACK,MAAU;AACT,IAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,gBAAgB,EACtB,GAAiB;MAGrB,KAAA;EAEN,WAAW;;;;;;;;UAQP,IAAU,qGAAqG,GAAG;;EAEtH,cACE,IAAU,GAAG,EAAG,wBAAwB,iBAAiB,CAAC,GAAG,EAAU,SAAS,KAAA;EAElF,MAAM,IAAU,WAAW,KAAA;EAC3B,UAAU,IAAU,IAAI,KAAA;YA1B1B;GA6BE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAW,MAAM;OAAI,WAAU;OAAoC,CAAA;MAC/D,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAU;OACR,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAAb,CAAgE,KAAE,EAAU,KAAS;SACjF;QACF;QAEL,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM,EAAE,iBAAiB;KACnC,WAAU;KAOV,cAAY,GAAG,EAAG,8BAA8B,wBAAwB,CAAC,GAAG,EAAU;eAEtF,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;KACnB,CAAA,CAEP;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAsB,QAAQ,EAAU,QAAU,CAAA;IAC9C,CAAA;GAGL,EAAU,gBAAgB,KAAA,KACzB,kBAAC,KAAD;IAAG,WAAU;cAAb;KACG,EAAU;KAAa;KACvB,EAAU,gBAAgB,IACvB,EAAG,uBAAuB,SAAS,CAAC,aAAa,GACjD,EAAG,2BAA2B,UAAU,CAAC,aAAa;KACxD;;GAIN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAG,kBAAkB,UAAU;MAAC;MAAE,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB;MACnF;;IACA,CAAA;GAGL,MAAgB,KAAU,MACzB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAxGQ,MAAwB;AAE1C,MADA,EAAE,iBAAiB,EACf,KACF,EAAO,EAAU;;KAsGT,WAAU;eAWT,EAAG,eAAe,OAAO;KACnB,CAAA,EAEV,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAnHU,MAAwB;AAE5C,MADA,EAAE,iBAAiB,EACf,KACF,EAAS,EAAU;;KAiHX,WAAU;eAWT,EAAG,iBAAiB,SAAS;KACvB,CAAA,CAEP;;GAEJ"}
|
|
@@ -8,82 +8,86 @@ 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
|
//#region src/organization-management/modules/workspaces/components/WorkspaceForm.tsx
|
|
11
|
-
function g({ workspace: g, tenantId: _,
|
|
12
|
-
let
|
|
11
|
+
function g({ workspace: g, tenantId: _, organizationId: v, organizationName: y, showOrganizationSelector: b = !1, onSubmit: x, onCancel: S, loading: C }) {
|
|
12
|
+
let w = t(), [T, E] = l({
|
|
13
13
|
name: g?.name || "",
|
|
14
14
|
slug: g?.slug || "",
|
|
15
|
-
type: g?.type || "PERSONAL",
|
|
16
15
|
tenantId: _ || g?.tenantId || "",
|
|
17
|
-
organizationId:
|
|
18
|
-
}), [
|
|
16
|
+
organizationId: v || g?.organizationId || ""
|
|
17
|
+
}), [D, O] = l({}), k = !g && b && !_, A = !g && b && !v, { tenants: j, loading: M } = r(100, 0, k), N = k && j.length > 1;
|
|
18
|
+
s(() => {
|
|
19
|
+
if (!k || T.tenantId || j.length === 0) return;
|
|
20
|
+
let e = j.find((e) => e.type === "SHARED")?.id ?? j[0]?.id;
|
|
21
|
+
e && E((t) => t.tenantId ? t : {
|
|
22
|
+
...t,
|
|
23
|
+
tenantId: e
|
|
24
|
+
});
|
|
25
|
+
}, [
|
|
26
|
+
k,
|
|
27
|
+
j,
|
|
28
|
+
T.tenantId
|
|
29
|
+
]);
|
|
30
|
+
let { organizations: P, loading: F } = e(T.tenantId ? { tenantId: T.tenantId } : void 0, 100, 0, A), { allowedIds: I, loading: L } = n(c(() => P.map((e) => e.id), [P]), "workspace", "create"), R = !g && b && !v, z = c(() => R ? P.filter((e) => I.has(e.id)) : P, [
|
|
19
31
|
P,
|
|
20
32
|
I,
|
|
21
33
|
R
|
|
22
34
|
]);
|
|
23
35
|
s(() => {
|
|
24
|
-
_ && _ !==
|
|
36
|
+
_ && _ !== T.tenantId && E((e) => ({
|
|
25
37
|
...e,
|
|
26
38
|
tenantId: _
|
|
27
39
|
}));
|
|
28
|
-
}, [
|
|
29
|
-
|
|
40
|
+
}, [T.tenantId, _]), s(() => {
|
|
41
|
+
v && v !== T.organizationId && E((e) => ({
|
|
30
42
|
...e,
|
|
31
|
-
organizationId:
|
|
43
|
+
organizationId: v
|
|
32
44
|
}));
|
|
33
|
-
}, [
|
|
34
|
-
!
|
|
45
|
+
}, [T.organizationId, v]), s(() => {
|
|
46
|
+
!b || v || g || E((e) => !e.organizationId || z.some((t) => t.id === e.organizationId) ? e : {
|
|
35
47
|
...e,
|
|
36
48
|
organizationId: ""
|
|
37
49
|
});
|
|
38
50
|
}, [
|
|
39
|
-
|
|
51
|
+
v,
|
|
40
52
|
z,
|
|
41
|
-
|
|
53
|
+
b,
|
|
42
54
|
g
|
|
43
55
|
]);
|
|
44
|
-
let B = c(() =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
M
|
|
48
|
-
]), V = c(() => b || (z.find((e) => e.id === E.organizationId)?.name ?? E.organizationId), [
|
|
49
|
-
E.organizationId,
|
|
50
|
-
b,
|
|
56
|
+
let B = c(() => y || (z.find((e) => e.id === T.organizationId)?.name ?? T.organizationId), [
|
|
57
|
+
T.organizationId,
|
|
58
|
+
y,
|
|
51
59
|
z
|
|
52
|
-
]),
|
|
60
|
+
]), V = () => {
|
|
53
61
|
try {
|
|
54
|
-
return (g ? o : a).parse(
|
|
62
|
+
return (g ? o : a).parse(T), O({}), !0;
|
|
55
63
|
} catch (e) {
|
|
56
64
|
if (e instanceof i) {
|
|
57
65
|
let t = {};
|
|
58
66
|
e.issues.forEach((e) => {
|
|
59
67
|
let n = e.path.join(".");
|
|
60
68
|
t[n] = e.message;
|
|
61
|
-
}),
|
|
69
|
+
}), O(t);
|
|
62
70
|
}
|
|
63
71
|
return !1;
|
|
64
72
|
}
|
|
65
73
|
};
|
|
66
74
|
return /* @__PURE__ */ h("form", {
|
|
67
75
|
onSubmit: async (e) => {
|
|
68
|
-
e.preventDefault(),
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
organizationId: E.organizationId,
|
|
74
|
-
name: E.name,
|
|
75
|
-
slug: E.slug,
|
|
76
|
-
type: E.type || void 0
|
|
76
|
+
e.preventDefault(), V() && await x(g ? { name: T.name === g.name ? void 0 : T.name } : {
|
|
77
|
+
tenantId: T.tenantId,
|
|
78
|
+
organizationId: T.organizationId,
|
|
79
|
+
name: T.name,
|
|
80
|
+
slug: T.slug
|
|
77
81
|
});
|
|
78
82
|
},
|
|
79
83
|
className: "space-y-6",
|
|
80
84
|
children: [
|
|
81
|
-
g ? null : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ h("div", { children: [
|
|
85
|
+
g ? null : /* @__PURE__ */ h(p, { children: [N ? /* @__PURE__ */ h("div", { children: [
|
|
82
86
|
/* @__PURE__ */ h("label", {
|
|
83
87
|
htmlFor: "tenantId",
|
|
84
88
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
85
89
|
children: [
|
|
86
|
-
|
|
90
|
+
w("organizations.form.tenant", "Tenant"),
|
|
87
91
|
" ",
|
|
88
92
|
/* @__PURE__ */ m("span", {
|
|
89
93
|
className: "text-[var(--color-status-error-text)]",
|
|
@@ -91,24 +95,24 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
91
95
|
})
|
|
92
96
|
]
|
|
93
97
|
}),
|
|
94
|
-
|
|
98
|
+
/* @__PURE__ */ h("div", {
|
|
95
99
|
className: "relative",
|
|
96
100
|
children: [
|
|
97
101
|
/* @__PURE__ */ h("select", {
|
|
98
102
|
id: "tenantId",
|
|
99
|
-
value:
|
|
100
|
-
onChange: (e) =>
|
|
103
|
+
value: T.tenantId,
|
|
104
|
+
onChange: (e) => E((t) => ({
|
|
101
105
|
...t,
|
|
102
106
|
tenantId: e.target.value,
|
|
103
107
|
organizationId: ""
|
|
104
108
|
})),
|
|
105
109
|
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)]",
|
|
106
|
-
disabled:
|
|
110
|
+
disabled: C || M,
|
|
107
111
|
required: !0,
|
|
108
112
|
children: [/* @__PURE__ */ m("option", {
|
|
109
113
|
value: "",
|
|
110
|
-
children:
|
|
111
|
-
}),
|
|
114
|
+
children: w("workspaces.form.selectTenant", "Select a tenant...")
|
|
115
|
+
}), j.map((e) => /* @__PURE__ */ h("option", {
|
|
112
116
|
value: e.id,
|
|
113
117
|
children: [
|
|
114
118
|
e.name,
|
|
@@ -137,7 +141,7 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
137
141
|
})
|
|
138
142
|
})
|
|
139
143
|
}),
|
|
140
|
-
|
|
144
|
+
M ? /* @__PURE__ */ m("div", {
|
|
141
145
|
className: "absolute right-10 top-1/2 -translate-y-1/2",
|
|
142
146
|
children: /* @__PURE__ */ m(f, {
|
|
143
147
|
size: 16,
|
|
@@ -145,41 +149,17 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
145
149
|
})
|
|
146
150
|
}) : null
|
|
147
151
|
]
|
|
148
|
-
}) : /* @__PURE__ */ h("div", {
|
|
149
|
-
className: "relative",
|
|
150
|
-
children: [/* @__PURE__ */ m("input", {
|
|
151
|
-
id: "tenantId",
|
|
152
|
-
type: "text",
|
|
153
|
-
value: B,
|
|
154
|
-
"aria-label": T("organizations.form.tenant", "Tenant"),
|
|
155
|
-
onChange: (e) => D((t) => ({
|
|
156
|
-
...t,
|
|
157
|
-
tenantId: e.target.value
|
|
158
|
-
})),
|
|
159
|
-
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)]",
|
|
160
|
-
placeholder: T("organizations.form.tenantPlaceholder", "Enter tenant ID"),
|
|
161
|
-
disabled: w || !!_,
|
|
162
|
-
required: !0,
|
|
163
|
-
autoComplete: "off"
|
|
164
|
-
}), /* @__PURE__ */ m(d, {
|
|
165
|
-
size: 18,
|
|
166
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]"
|
|
167
|
-
})]
|
|
168
152
|
}),
|
|
169
|
-
|
|
170
|
-
className: "mt-1 text-xs text-[var(--color-text-muted)]",
|
|
171
|
-
children: T("workspaces.form.creatingUnderTenant", "Creating workspace under the selected tenant.")
|
|
172
|
-
}),
|
|
173
|
-
O.tenantId ? /* @__PURE__ */ m("p", {
|
|
153
|
+
D.tenantId ? /* @__PURE__ */ m("p", {
|
|
174
154
|
className: "mt-1 text-sm text-[var(--color-status-error-text)]",
|
|
175
|
-
children:
|
|
155
|
+
children: D.tenantId
|
|
176
156
|
}) : null
|
|
177
|
-
] }), /* @__PURE__ */ h("div", { children: [
|
|
157
|
+
] }) : null, /* @__PURE__ */ h("div", { children: [
|
|
178
158
|
/* @__PURE__ */ h("label", {
|
|
179
159
|
htmlFor: "organizationId",
|
|
180
160
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
181
161
|
children: [
|
|
182
|
-
|
|
162
|
+
w("common.organization", "Organization"),
|
|
183
163
|
" ",
|
|
184
164
|
/* @__PURE__ */ m("span", {
|
|
185
165
|
className: "text-[var(--color-status-error-text)]",
|
|
@@ -187,22 +167,22 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
187
167
|
})
|
|
188
168
|
]
|
|
189
169
|
}),
|
|
190
|
-
|
|
170
|
+
b && !v ? /* @__PURE__ */ h("div", {
|
|
191
171
|
className: "relative",
|
|
192
172
|
children: [
|
|
193
173
|
/* @__PURE__ */ h("select", {
|
|
194
174
|
id: "organizationId",
|
|
195
|
-
value:
|
|
196
|
-
onChange: (e) =>
|
|
175
|
+
value: T.organizationId,
|
|
176
|
+
onChange: (e) => E((t) => ({
|
|
197
177
|
...t,
|
|
198
178
|
organizationId: e.target.value
|
|
199
179
|
})),
|
|
200
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)]",
|
|
201
|
-
disabled:
|
|
181
|
+
disabled: C || F || L || !T.tenantId,
|
|
202
182
|
required: !0,
|
|
203
183
|
children: [/* @__PURE__ */ m("option", {
|
|
204
184
|
value: "",
|
|
205
|
-
children:
|
|
185
|
+
children: T.tenantId ? z.length === 0 && !F && !L ? 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")
|
|
206
186
|
}), z.map((e) => /* @__PURE__ */ h("option", {
|
|
207
187
|
value: e.id,
|
|
208
188
|
children: [
|
|
@@ -245,15 +225,15 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
245
225
|
children: [/* @__PURE__ */ m("input", {
|
|
246
226
|
id: "organizationId",
|
|
247
227
|
type: "text",
|
|
248
|
-
value:
|
|
249
|
-
"aria-label":
|
|
250
|
-
onChange: (e) =>
|
|
228
|
+
value: B,
|
|
229
|
+
"aria-label": w("common.organization", "Organization"),
|
|
230
|
+
onChange: (e) => E((t) => ({
|
|
251
231
|
...t,
|
|
252
232
|
organizationId: e.target.value
|
|
253
233
|
})),
|
|
254
234
|
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)]",
|
|
255
|
-
placeholder:
|
|
256
|
-
disabled:
|
|
235
|
+
placeholder: w("workspaces.form.orgIdPlaceholder", "Enter organization ID"),
|
|
236
|
+
disabled: C || !!v,
|
|
257
237
|
required: !0,
|
|
258
238
|
autoComplete: "off"
|
|
259
239
|
}), /* @__PURE__ */ m(u, {
|
|
@@ -261,13 +241,13 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
261
241
|
className: "absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)]"
|
|
262
242
|
})]
|
|
263
243
|
}),
|
|
264
|
-
(
|
|
244
|
+
(v || T.organizationId) && /* @__PURE__ */ m("p", {
|
|
265
245
|
className: "mt-1 text-xs text-[var(--color-text-muted)]",
|
|
266
|
-
children:
|
|
246
|
+
children: w("workspaces.form.creatingUnderOrg", "Creating workspace under the selected organization.")
|
|
267
247
|
}),
|
|
268
|
-
|
|
248
|
+
D.organizationId ? /* @__PURE__ */ m("p", {
|
|
269
249
|
className: "mt-1 text-sm text-[var(--color-status-error-text)]",
|
|
270
|
-
children:
|
|
250
|
+
children: D.organizationId
|
|
271
251
|
}) : null
|
|
272
252
|
] })] }),
|
|
273
253
|
/* @__PURE__ */ h("div", { children: [
|
|
@@ -275,7 +255,7 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
275
255
|
htmlFor: "name",
|
|
276
256
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
277
257
|
children: [
|
|
278
|
-
|
|
258
|
+
w("workspaces.form.name", "Workspace Name"),
|
|
279
259
|
" ",
|
|
280
260
|
/* @__PURE__ */ m("span", {
|
|
281
261
|
className: "text-[var(--color-status-error-text)]",
|
|
@@ -286,21 +266,21 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
286
266
|
/* @__PURE__ */ m("input", {
|
|
287
267
|
id: "name",
|
|
288
268
|
type: "text",
|
|
289
|
-
value:
|
|
290
|
-
"aria-label":
|
|
291
|
-
onChange: (e) =>
|
|
269
|
+
value: T.name,
|
|
270
|
+
"aria-label": w("workspaces.form.name", "Workspace Name"),
|
|
271
|
+
onChange: (e) => E((t) => ({
|
|
292
272
|
...t,
|
|
293
273
|
name: e.target.value
|
|
294
274
|
})),
|
|
295
275
|
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)]",
|
|
296
|
-
placeholder:
|
|
297
|
-
disabled:
|
|
276
|
+
placeholder: w("workspaces.form.namePlaceholder", "Enter workspace name"),
|
|
277
|
+
disabled: C,
|
|
298
278
|
required: !0,
|
|
299
279
|
autoComplete: "organization-title"
|
|
300
280
|
}),
|
|
301
|
-
|
|
281
|
+
D.name ? /* @__PURE__ */ m("p", {
|
|
302
282
|
className: "mt-1 text-sm text-[var(--color-status-error-text)]",
|
|
303
|
-
children:
|
|
283
|
+
children: D.name
|
|
304
284
|
}) : null
|
|
305
285
|
] }),
|
|
306
286
|
/* @__PURE__ */ h("div", { children: [
|
|
@@ -308,7 +288,7 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
308
288
|
htmlFor: "slug",
|
|
309
289
|
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
310
290
|
children: [
|
|
311
|
-
|
|
291
|
+
w("common.slug", "Slug"),
|
|
312
292
|
" ",
|
|
313
293
|
/* @__PURE__ */ m("span", {
|
|
314
294
|
className: "text-[var(--color-status-error-text)]",
|
|
@@ -319,71 +299,43 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
319
299
|
/* @__PURE__ */ m("input", {
|
|
320
300
|
id: "slug",
|
|
321
301
|
type: "text",
|
|
322
|
-
value:
|
|
323
|
-
"aria-label":
|
|
324
|
-
onChange: (e) =>
|
|
302
|
+
value: T.slug,
|
|
303
|
+
"aria-label": w("common.slug", "Slug"),
|
|
304
|
+
onChange: (e) => E((t) => ({
|
|
325
305
|
...t,
|
|
326
306
|
slug: e.target.value.toLowerCase().replace(/\s+/g, "-")
|
|
327
307
|
})),
|
|
328
308
|
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)]",
|
|
329
|
-
placeholder:
|
|
330
|
-
disabled:
|
|
309
|
+
placeholder: w("workspaces.form.slugPlaceholder", "workspace-slug"),
|
|
310
|
+
disabled: C || !!g,
|
|
331
311
|
required: !g,
|
|
332
312
|
autoComplete: "off"
|
|
333
313
|
}),
|
|
334
314
|
/* @__PURE__ */ m("p", {
|
|
335
315
|
className: "mt-1 text-xs text-[var(--color-text-muted)]",
|
|
336
|
-
children:
|
|
316
|
+
children: w("organizations.form.slugImmutable", "Lowercase letters, numbers, and hyphens only. Cannot be changed after creation.")
|
|
337
317
|
}),
|
|
338
|
-
|
|
318
|
+
D.slug ? /* @__PURE__ */ m("p", {
|
|
339
319
|
className: "mt-1 text-sm text-[var(--color-status-error-text)]",
|
|
340
|
-
children:
|
|
320
|
+
children: D.slug
|
|
341
321
|
}) : null
|
|
342
322
|
] }),
|
|
343
|
-
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
|
|
344
|
-
htmlFor: "type",
|
|
345
|
-
className: "mb-1 block text-sm font-medium text-[var(--color-text-primary)]",
|
|
346
|
-
children: T("workspaces.list.column.type", "Type")
|
|
347
|
-
}), /* @__PURE__ */ h("select", {
|
|
348
|
-
id: "type",
|
|
349
|
-
value: E.type,
|
|
350
|
-
onChange: (e) => D((t) => ({
|
|
351
|
-
...t,
|
|
352
|
-
type: e.target.value
|
|
353
|
-
})),
|
|
354
|
-
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)]",
|
|
355
|
-
disabled: w,
|
|
356
|
-
children: [
|
|
357
|
-
/* @__PURE__ */ m("option", {
|
|
358
|
-
value: "PERSONAL",
|
|
359
|
-
children: T("workspaces.type.personal", "Personal")
|
|
360
|
-
}),
|
|
361
|
-
/* @__PURE__ */ m("option", {
|
|
362
|
-
value: "TEAM",
|
|
363
|
-
children: T("workspaces.type.team", "Team")
|
|
364
|
-
}),
|
|
365
|
-
/* @__PURE__ */ m("option", {
|
|
366
|
-
value: "ENTERPRISE",
|
|
367
|
-
children: T("workspaces.type.enterprise", "Enterprise")
|
|
368
|
-
})
|
|
369
|
-
]
|
|
370
|
-
})] }),
|
|
371
323
|
/* @__PURE__ */ h("div", {
|
|
372
324
|
className: "flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row",
|
|
373
325
|
children: [/* @__PURE__ */ h("button", {
|
|
374
326
|
type: "submit",
|
|
375
|
-
disabled:
|
|
327
|
+
disabled: C,
|
|
376
328
|
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 hover:scale-[1.02] active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[220px]",
|
|
377
|
-
children: [
|
|
329
|
+
children: [C ? /* @__PURE__ */ m(f, {
|
|
378
330
|
size: 16,
|
|
379
331
|
className: "animate-spin"
|
|
380
|
-
}) : null, g ?
|
|
332
|
+
}) : null, g ? w("workspaces.form.updateSubmit", "Update Workspace") : w("workspaces.create.button", "Create Workspace")]
|
|
381
333
|
}), /* @__PURE__ */ m("button", {
|
|
382
334
|
type: "button",
|
|
383
|
-
onClick:
|
|
384
|
-
disabled:
|
|
335
|
+
onClick: S,
|
|
336
|
+
disabled: C,
|
|
385
337
|
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)] active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[140px]",
|
|
386
|
-
children:
|
|
338
|
+
children: w("common.cancel", "Cancel")
|
|
387
339
|
})]
|
|
388
340
|
})
|
|
389
341
|
]
|
|
@@ -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 tenantName,\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 type: workspace?.type || 'PERSONAL',\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 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 selectedTenantLabel = useMemo(() => {\n if (tenantName) return tenantName;\n\n const selectedTenant = tenants.find((tenant) => tenant.id === formData.tenantId);\n return selectedTenant?.name ?? formData.tenantId;\n }, [formData.tenantId, tenantName, tenants]);\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 : createWorkspaceSchema;\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 type: formData.type !== workspace.type ? formData.type : undefined,\n }\n : {\n tenantId: formData.tenantId,\n organizationId: formData.organizationId,\n name: formData.name,\n slug: formData.slug,\n type: formData.type || undefined,\n };\n\n await onSubmit(submitData);\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {!workspace ? (\n <>\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 {showOrganizationSelector && !tenantId ? (\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)]\"\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 ) : (\n <div className=\"relative\">\n <input\n id=\"tenantId\"\n type=\"text\"\n value={selectedTenantLabel}\n aria-label={tr('organizations.form.tenant', 'Tenant')}\n onChange={(event) =>\n setFormData((previousState) => ({\n ...previousState,\n tenantId: 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('organizations.form.tenantPlaceholder', 'Enter tenant ID')}\n disabled={loading || Boolean(tenantId)}\n required\n autoComplete=\"off\"\n />\n <Home\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 {(tenantId || formData.tenantId) && (\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr(\n 'workspaces.form.creatingUnderTenant',\n 'Creating workspace under the selected tenant.'\n )}\n </p>\n )}\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\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)]\"\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>\n <label\n htmlFor=\"type\"\n className=\"mb-1 block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr('workspaces.list.column.type', 'Type')}\n </label>\n <select\n id=\"type\"\n value={formData.type}\n onChange={(event) =>\n setFormData((previousState) => ({ ...previousState, type: 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)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)] disabled:cursor-not-allowed disabled:bg-[var(--color-bg-muted)]\"\n disabled={loading}\n >\n <option value=\"PERSONAL\">{tr('workspaces.type.personal', 'Personal')}</option>\n <option value=\"TEAM\">{tr('workspaces.type.team', 'Team')}</option>\n <option value=\"ENTERPRISE\">{tr('workspaces.type.enterprise', 'Enterprise')}</option>\n </select>\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 hover:scale-[1.02] active:scale-[0.98] 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)] active:scale-[0.98] 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":[],"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 : createWorkspaceSchema;\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)]\"\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)]\"\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 hover:scale-[1.02] active:scale-[0.98] 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)] active:scale-[0.98] 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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"}
|