@burdenoff/microfe-workspaces 2026.830.1 → 2026.915.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/components/CreateProjectDialog.js +65 -61
- package/dist/components/CreateProjectDialog.js.map +1 -1
- package/dist/components/CreateWorkspaceDialog.js +93 -90
- package/dist/components/CreateWorkspaceDialog.js.map +1 -1
- package/dist/components/InviteMemberDialog.js +122 -111
- package/dist/components/InviteMemberDialog.js.map +1 -1
- package/dist/components/MemberListItem.js +55 -58
- package/dist/components/MemberListItem.js.map +1 -1
- package/dist/components/ProjectCard.js +93 -92
- package/dist/components/ProjectCard.js.map +1 -1
- package/dist/components/ProjectMemberPickerDialog.js +78 -93
- package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
- package/dist/components/RenameProjectDialog.js +47 -44
- package/dist/components/RenameProjectDialog.js.map +1 -1
- package/dist/components/WorkspaceCard.js +42 -43
- package/dist/components/WorkspaceCard.js.map +1 -1
- package/dist/components/WorkspacesErrorBoundary.js +43 -34
- package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
- package/dist/components/tags/ProjectTagsWidget.js +75 -77
- package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
- package/dist/hooks/useTr.js +14 -0
- package/dist/hooks/useTr.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/pages/ActivityListPage.js +3 -3
- package/dist/pages/CreateWorkspacePage.js +10 -10
- package/dist/pages/InviteAcceptancePage.js +49 -49
- package/dist/pages/InviteAcceptancePage.js.map +1 -1
- package/dist/pages/MembersListPage.js +6 -6
- package/dist/pages/PendingInvitesPage.js +3 -3
- package/dist/pages/ProjectDetailPage.js +4 -4
- package/dist/pages/ProjectDetailPage.js.map +1 -1
- package/dist/pages/ProjectMembersPage.js +3 -3
- package/dist/pages/ProjectsListPage.js +5 -5
- package/dist/pages/WorkspaceDetailPage.js +4 -4
- package/dist/pages/WorkspaceInvitePage.js +4 -4
- package/dist/pages/WorkspaceSettingsPage.js +7 -7
- package/dist/pages/WorkspacesListPage.js +9 -9
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js +4 -4
- package/dist/pages/dashboard/widgets/StorageWidget.js +3 -3
- package/dist/pages/dashboard/widgets/WelcomeWidget.js +26 -16
- package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
- package/dist/translations/en.js +908 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,227 +1,238 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { useTr as e } from "../hooks/useTr.js";
|
|
2
|
+
import { useWorkspacesStore as t } from "../store/workspacesStore.js";
|
|
3
|
+
import { useCreateInvitation as n } from "../hooks/useMemberMutations.js";
|
|
4
|
+
import { useWorkspaceRoles as r } from "../hooks/useWorkspaceRoles.js";
|
|
5
|
+
import { useProjects as i } from "../hooks/useProjects.js";
|
|
6
|
+
import { useMemo as a, useState as o } from "react";
|
|
7
|
+
import { AlertCircle as s } from "lucide-react";
|
|
8
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
9
|
+
import { Badge as u, Button as d, Checkbox as f, ResponsiveDialog as p, ResponsiveDialogContent as m, ResponsiveDialogDescription as h, ResponsiveDialogHeader as g, ResponsiveDialogTitle as _, Tooltip as v, TooltipContent as y, TooltipProvider as b, TooltipTrigger as x } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { FormControl as S, FormDescription as C, FormField as w, FormItem as T, FormLabel as E, FormMessage as D, FormProvider as O, Input as k, useForm as A, z as j } from "@burdenoff/fe-libs/form";
|
|
10
11
|
//#region src/components/InviteMemberDialog.tsx
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
function M(e) {
|
|
13
|
+
return j.object({
|
|
14
|
+
email: j.string().email(e("inviteMemberDialog.validation.emailInvalid", "Invalid email address")),
|
|
15
|
+
roleIds: j.array(j.string()),
|
|
16
|
+
projectIds: j.array(j.string())
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function N({ workspaceId: j }) {
|
|
20
|
+
let N = e(), { isInviteMemberDialogOpen: P, closeInviteMemberDialog: F } = t(), { mutate: I, isPending: L } = n(j), { data: R = [], isLoading: z } = r(j), { data: B, isLoading: V } = i({ archived: !1 }, { limit: 100 }, j), [H, U] = o(null), W = B?.items ?? [], G = A({
|
|
21
|
+
schema: a(() => M(N), [N]),
|
|
19
22
|
defaultValues: {
|
|
20
23
|
email: "",
|
|
21
24
|
roleIds: [],
|
|
22
25
|
projectIds: []
|
|
23
26
|
}
|
|
24
|
-
}),
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
+
}), K = (e) => {
|
|
28
|
+
if (U(null), !j) {
|
|
29
|
+
U(N("inviteMemberDialog.noWorkspaceError", "No workspace selected. Please select a workspace first."));
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
29
|
-
|
|
32
|
+
I({
|
|
30
33
|
email: e.email,
|
|
31
34
|
roleIds: e.roleIds,
|
|
32
|
-
workspaceId:
|
|
35
|
+
workspaceId: j,
|
|
33
36
|
...e.projectIds.length > 0 ? { context: {
|
|
34
37
|
type: "workspace.invitation",
|
|
35
38
|
metadata: { projectIds: e.projectIds }
|
|
36
39
|
} } : {}
|
|
37
40
|
}, {
|
|
38
41
|
onSuccess: () => {
|
|
39
|
-
|
|
42
|
+
F(), G.reset(), U(null);
|
|
40
43
|
},
|
|
41
44
|
onError: (e) => {
|
|
42
|
-
let t = e instanceof Error ? e.message : "Failed to send invitation";
|
|
43
|
-
console.error("Failed to create invitation:", e),
|
|
45
|
+
let t = e instanceof Error ? e.message : N("inviteMemberDialog.sendError", "Failed to send invitation");
|
|
46
|
+
console.error("Failed to create invitation:", e), U(t);
|
|
44
47
|
}
|
|
45
48
|
});
|
|
46
|
-
},
|
|
47
|
-
e || (
|
|
49
|
+
}, q = (e) => {
|
|
50
|
+
e || (F(), G.reset(), U(null));
|
|
48
51
|
};
|
|
49
|
-
return /* @__PURE__ */
|
|
50
|
-
open:
|
|
51
|
-
onOpenChange:
|
|
52
|
-
children: /* @__PURE__ */
|
|
52
|
+
return /* @__PURE__ */ c(p, {
|
|
53
|
+
open: P,
|
|
54
|
+
onOpenChange: q,
|
|
55
|
+
children: /* @__PURE__ */ l(m, {
|
|
53
56
|
className: "z-[120] flex h-[min(88vh,900px)] flex-col overflow-hidden sm:max-w-[960px] lg:max-w-[1120px]",
|
|
54
|
-
children: [/* @__PURE__ */
|
|
55
|
-
...
|
|
56
|
-
children: /* @__PURE__ */
|
|
57
|
-
onSubmit:
|
|
57
|
+
children: [/* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(_, { children: N("inviteMemberDialog.title", "Invite Member") }), /* @__PURE__ */ c(h, { children: N("inviteMemberDialog.description", "Invite a new member to this workspace by email. They'll receive an invitation link to join.") })] }), /* @__PURE__ */ c(O, {
|
|
58
|
+
...G,
|
|
59
|
+
children: /* @__PURE__ */ l("form", {
|
|
60
|
+
onSubmit: G.handleSubmit(K),
|
|
58
61
|
className: "flex min-h-0 flex-1 flex-col",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
62
|
+
children: [/* @__PURE__ */ c("div", {
|
|
60
63
|
className: "min-h-0 flex-1 overflow-y-auto pr-2",
|
|
61
|
-
children: /* @__PURE__ */
|
|
64
|
+
children: /* @__PURE__ */ l("div", {
|
|
62
65
|
className: "space-y-5 pb-4 pr-2",
|
|
63
66
|
children: [
|
|
64
|
-
|
|
67
|
+
H && /* @__PURE__ */ l("div", {
|
|
65
68
|
className: "flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ c(s, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ c("span", { children: H })]
|
|
67
70
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
control:
|
|
71
|
+
/* @__PURE__ */ c(w, {
|
|
72
|
+
control: G.control,
|
|
70
73
|
name: "email",
|
|
71
|
-
render: ({ field: e }) => /* @__PURE__ */
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
+
render: ({ field: e }) => /* @__PURE__ */ l(T, { children: [
|
|
75
|
+
/* @__PURE__ */ c(E, { children: N("inviteMemberDialog.emailLabel", "Email Address *") }),
|
|
76
|
+
/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(k, {
|
|
74
77
|
type: "email",
|
|
75
|
-
placeholder: "member@example.com",
|
|
78
|
+
placeholder: N("inviteMemberDialog.emailPlaceholder", "member@example.com"),
|
|
76
79
|
...e,
|
|
77
|
-
disabled:
|
|
80
|
+
disabled: L,
|
|
78
81
|
onChange: (t) => {
|
|
79
|
-
e.onChange(t),
|
|
82
|
+
e.onChange(t), U(null);
|
|
80
83
|
}
|
|
81
84
|
}) }),
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
/* @__PURE__ */
|
|
85
|
+
/* @__PURE__ */ c(C, { children: N("inviteMemberDialog.emailHint", "Enter the email address of the person you want to invite") }),
|
|
86
|
+
/* @__PURE__ */ c(D, {})
|
|
84
87
|
] })
|
|
85
88
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
87
|
-
control:
|
|
89
|
+
/* @__PURE__ */ c(w, {
|
|
90
|
+
control: G.control,
|
|
88
91
|
name: "roleIds",
|
|
89
|
-
render: ({ field: e }) => /* @__PURE__ */
|
|
90
|
-
/* @__PURE__ */
|
|
92
|
+
render: ({ field: e }) => /* @__PURE__ */ l(T, { children: [
|
|
93
|
+
/* @__PURE__ */ l("div", {
|
|
91
94
|
className: "flex flex-wrap items-center justify-between gap-2",
|
|
92
|
-
children: [/* @__PURE__ */
|
|
95
|
+
children: [/* @__PURE__ */ l("div", {
|
|
93
96
|
className: "space-y-1",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
97
|
+
children: [/* @__PURE__ */ c(E, { children: N("inviteMemberDialog.rolesLabel", "Roles (Optional)") }), /* @__PURE__ */ c("p", {
|
|
95
98
|
className: "text-sm text-text-secondary",
|
|
96
|
-
children: "Choose roles. Hover any role to preview its full description."
|
|
99
|
+
children: N("inviteMemberDialog.rolesHint", "Choose roles. Hover any role to preview its full description.")
|
|
97
100
|
})]
|
|
98
|
-
}), e.value.length > 0 ? /* @__PURE__ */
|
|
101
|
+
}), e.value.length > 0 ? /* @__PURE__ */ l(u, {
|
|
99
102
|
variant: "secondary",
|
|
100
|
-
children: [
|
|
103
|
+
children: [
|
|
104
|
+
e.value.length,
|
|
105
|
+
" ",
|
|
106
|
+
N("inviteMemberDialog.selectedCount", "selected")
|
|
107
|
+
]
|
|
101
108
|
}) : null]
|
|
102
109
|
}),
|
|
103
|
-
/* @__PURE__ */
|
|
110
|
+
/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(b, { children: /* @__PURE__ */ c("div", {
|
|
104
111
|
className: "grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3",
|
|
105
|
-
children:
|
|
112
|
+
children: z ? /* @__PURE__ */ c("p", {
|
|
106
113
|
className: "text-sm text-text-secondary",
|
|
107
|
-
children: "Loading roles…"
|
|
108
|
-
}) :
|
|
114
|
+
children: N("inviteMemberDialog.loadingRoles", "Loading roles…")
|
|
115
|
+
}) : R.length === 0 ? /* @__PURE__ */ c("p", {
|
|
109
116
|
className: "text-sm text-text-secondary",
|
|
110
|
-
children: "No assignable roles were returned for this workspace."
|
|
111
|
-
}) :
|
|
117
|
+
children: N("inviteMemberDialog.noRoles", "No assignable roles were returned for this workspace.")
|
|
118
|
+
}) : R.map((t) => {
|
|
112
119
|
let n = e.value.includes(t.id);
|
|
113
|
-
return /* @__PURE__ */
|
|
120
|
+
return /* @__PURE__ */ l(v, { children: [/* @__PURE__ */ c(x, {
|
|
114
121
|
asChild: !0,
|
|
115
|
-
children: /* @__PURE__ */
|
|
122
|
+
children: /* @__PURE__ */ l("label", {
|
|
116
123
|
className: `flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${n ? "border-primary bg-action-primary-bg/10 text-text-primary shadow-sm" : "border-border-subtle bg-bg-surface text-text-primary hover:bg-accent"}`,
|
|
117
|
-
children: [/* @__PURE__ */
|
|
124
|
+
children: [/* @__PURE__ */ c("span", {
|
|
118
125
|
className: "min-w-0 flex-1 truncate",
|
|
119
126
|
children: t.name
|
|
120
|
-
}), /* @__PURE__ */
|
|
127
|
+
}), /* @__PURE__ */ c(f, {
|
|
121
128
|
checked: n,
|
|
122
|
-
disabled:
|
|
129
|
+
disabled: L,
|
|
123
130
|
onCheckedChange: (n) => {
|
|
124
131
|
let r = n ? [...e.value, t.id] : e.value.filter((e) => e !== t.id);
|
|
125
132
|
e.onChange(r);
|
|
126
133
|
},
|
|
127
|
-
"aria-label":
|
|
134
|
+
"aria-label": N("inviteMemberDialog.selectRoleAriaLabel", "Select {{roleName}}", { roleName: t.name })
|
|
128
135
|
})]
|
|
129
136
|
})
|
|
130
|
-
}), /* @__PURE__ */
|
|
137
|
+
}), /* @__PURE__ */ l(y, {
|
|
131
138
|
className: "max-w-xs text-sm leading-6",
|
|
132
139
|
children: [
|
|
133
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ c("p", {
|
|
134
141
|
className: "font-medium",
|
|
135
142
|
children: t.name
|
|
136
143
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ c("p", {
|
|
138
145
|
className: "mt-1 text-text-secondary",
|
|
139
|
-
children: t.description || "No description available for this role."
|
|
146
|
+
children: t.description || N("inviteMemberDialog.noRoleDescription", "No description available for this role.")
|
|
140
147
|
}),
|
|
141
|
-
t.isSystem ? /* @__PURE__ */
|
|
148
|
+
t.isSystem ? /* @__PURE__ */ c("p", {
|
|
142
149
|
className: "mt-2 text-xs",
|
|
143
|
-
children: "System role"
|
|
150
|
+
children: N("inviteMemberDialog.systemRole", "System role")
|
|
144
151
|
}) : null
|
|
145
152
|
]
|
|
146
153
|
})] }, t.id);
|
|
147
154
|
})
|
|
148
155
|
}) }) }),
|
|
149
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ c(D, {})
|
|
150
157
|
] })
|
|
151
158
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
153
|
-
control:
|
|
159
|
+
/* @__PURE__ */ c(w, {
|
|
160
|
+
control: G.control,
|
|
154
161
|
name: "projectIds",
|
|
155
|
-
render: ({ field: e }) => /* @__PURE__ */
|
|
156
|
-
/* @__PURE__ */
|
|
162
|
+
render: ({ field: e }) => /* @__PURE__ */ l(T, { children: [
|
|
163
|
+
/* @__PURE__ */ l("div", {
|
|
157
164
|
className: "flex flex-wrap items-center justify-between gap-2",
|
|
158
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ l("div", {
|
|
159
166
|
className: "space-y-1",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ c(E, { children: N("inviteMemberDialog.projectsLabel", "Projects to add on acceptance (Optional)") }), /* @__PURE__ */ c("p", {
|
|
161
168
|
className: "text-sm text-text-secondary",
|
|
162
|
-
children: "The invited user will be added to these projects automatically after they accept the workspace invitation."
|
|
169
|
+
children: N("inviteMemberDialog.projectsHint", "The invited user will be added to these projects automatically after they accept the workspace invitation.")
|
|
163
170
|
})]
|
|
164
|
-
}), e.value.length > 0 ? /* @__PURE__ */
|
|
171
|
+
}), e.value.length > 0 ? /* @__PURE__ */ l(u, {
|
|
165
172
|
variant: "secondary",
|
|
166
|
-
children: [
|
|
173
|
+
children: [
|
|
174
|
+
e.value.length,
|
|
175
|
+
" ",
|
|
176
|
+
N("inviteMemberDialog.selectedCount", "selected")
|
|
177
|
+
]
|
|
167
178
|
}) : null]
|
|
168
179
|
}),
|
|
169
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c("div", {
|
|
170
181
|
className: "grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3",
|
|
171
|
-
children:
|
|
182
|
+
children: V ? /* @__PURE__ */ c("p", {
|
|
172
183
|
className: "text-sm text-text-secondary",
|
|
173
|
-
children: "Loading projects…"
|
|
174
|
-
}) :
|
|
184
|
+
children: N("inviteMemberDialog.loadingProjects", "Loading projects…")
|
|
185
|
+
}) : W.length === 0 ? /* @__PURE__ */ c("p", {
|
|
175
186
|
className: "text-sm text-text-secondary",
|
|
176
|
-
children: "No active projects are available in this workspace yet."
|
|
177
|
-
}) :
|
|
187
|
+
children: N("inviteMemberDialog.noProjects", "No active projects are available in this workspace yet.")
|
|
188
|
+
}) : W.map((t) => {
|
|
178
189
|
let n = e.value.includes(t.id);
|
|
179
|
-
return /* @__PURE__ */
|
|
190
|
+
return /* @__PURE__ */ l("label", {
|
|
180
191
|
className: `flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${n ? "border-primary bg-action-primary-bg/10 text-text-primary shadow-sm" : "border-border-subtle bg-bg-surface text-text-primary hover:bg-accent"}`,
|
|
181
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ l("div", {
|
|
182
193
|
className: "min-w-0 flex-1 space-y-1",
|
|
183
|
-
children: [/* @__PURE__ */
|
|
194
|
+
children: [/* @__PURE__ */ c("p", {
|
|
184
195
|
className: "truncate text-sm font-semibold leading-tight",
|
|
185
196
|
children: t.name
|
|
186
|
-
}), /* @__PURE__ */
|
|
197
|
+
}), /* @__PURE__ */ l("div", {
|
|
187
198
|
className: "flex items-center gap-2 text-xs text-text-secondary",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ c("span", {
|
|
189
200
|
className: "truncate font-mono",
|
|
190
201
|
children: t.key
|
|
191
|
-
}), /* @__PURE__ */
|
|
202
|
+
}), /* @__PURE__ */ c(u, {
|
|
192
203
|
variant: "outline",
|
|
193
204
|
children: t.status
|
|
194
205
|
})]
|
|
195
206
|
})]
|
|
196
|
-
}), /* @__PURE__ */
|
|
207
|
+
}), /* @__PURE__ */ c(f, {
|
|
197
208
|
checked: n,
|
|
198
|
-
disabled:
|
|
209
|
+
disabled: L,
|
|
199
210
|
onCheckedChange: (n) => {
|
|
200
211
|
let r = n ? [...e.value, t.id] : e.value.filter((e) => e !== t.id);
|
|
201
212
|
e.onChange(r);
|
|
202
213
|
},
|
|
203
|
-
"aria-label":
|
|
214
|
+
"aria-label": N("inviteMemberDialog.selectProjectAriaLabel", "Select {{projectName}}", { projectName: t.name })
|
|
204
215
|
})]
|
|
205
216
|
}, t.id);
|
|
206
217
|
})
|
|
207
218
|
}) }),
|
|
208
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ c(D, {})
|
|
209
220
|
] })
|
|
210
221
|
})
|
|
211
222
|
]
|
|
212
223
|
})
|
|
213
|
-
}), /* @__PURE__ */
|
|
224
|
+
}), /* @__PURE__ */ l("div", {
|
|
214
225
|
className: "flex justify-end gap-3 border-t pt-4",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
226
|
+
children: [/* @__PURE__ */ c(d, {
|
|
216
227
|
type: "button",
|
|
217
228
|
variant: "outline",
|
|
218
|
-
onClick: () =>
|
|
219
|
-
disabled:
|
|
220
|
-
children: "Cancel"
|
|
221
|
-
}), /* @__PURE__ */
|
|
229
|
+
onClick: () => q(!1),
|
|
230
|
+
disabled: L,
|
|
231
|
+
children: N("common.cancel", "Cancel")
|
|
232
|
+
}), /* @__PURE__ */ c(d, {
|
|
222
233
|
type: "submit",
|
|
223
|
-
disabled:
|
|
224
|
-
children:
|
|
234
|
+
disabled: L,
|
|
235
|
+
children: L ? N("inviteMemberDialog.sending", "Sending...") : N("inviteMemberDialog.submit", "Send Invitation")
|
|
225
236
|
})]
|
|
226
237
|
})]
|
|
227
238
|
})
|
|
@@ -230,6 +241,6 @@ function j({ workspaceId: k }) {
|
|
|
230
241
|
});
|
|
231
242
|
}
|
|
232
243
|
//#endregion
|
|
233
|
-
export {
|
|
244
|
+
export { N as InviteMemberDialog };
|
|
234
245
|
|
|
235
246
|
//# sourceMappingURL=InviteMemberDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberDialog.js","names":[],"sources":["../../src/components/InviteMemberDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\n Badge,\n Checkbox,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateInvitation } from '../hooks/useMemberMutations';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\n\nconst inviteMemberSchema = z.object({\n email: z.string().email('Invalid email address'),\n // Using a lambda default instead of `.default([])` to keep input type required\n // (zod v4 widens defaulted inputs to `T | undefined`, which breaks useForm typing).\n roleIds: z.array(z.string()),\n projectIds: z.array(z.string()),\n});\n\ntype InviteMemberFormData = z.infer<typeof inviteMemberSchema>;\n\ninterface InviteMemberDialogProps {\n /** Workspace ID to invite the member to (from URL params) */\n workspaceId?: string;\n}\n\nexport function InviteMemberDialog({ workspaceId }: InviteMemberDialogProps) {\n const { isInviteMemberDialogOpen, closeInviteMemberDialog } = useWorkspacesStore();\n const { mutate: createInvitation, isPending } = useCreateInvitation(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const { data: projectList, isLoading: isLoadingProjects } = useProjects(\n { archived: false },\n { limit: 100 },\n workspaceId\n );\n const [error, setError] = useState<string | null>(null);\n const availableProjects = projectList?.items ?? [];\n\n const form = useForm<InviteMemberFormData>({\n schema: inviteMemberSchema,\n defaultValues: {\n email: '',\n roleIds: [],\n projectIds: [],\n },\n });\n\n const onSubmit = (data: InviteMemberFormData) => {\n setError(null);\n\n if (!workspaceId) {\n setError('No workspace selected. Please select a workspace first.');\n return;\n }\n\n createInvitation(\n {\n email: data.email,\n roleIds: data.roleIds,\n workspaceId,\n ...(data.projectIds.length > 0\n ? {\n context: {\n type: 'workspace.invitation',\n metadata: { projectIds: data.projectIds },\n },\n }\n : {}),\n },\n {\n onSuccess: () => {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n },\n onError: (err) => {\n const errorMessage = err instanceof Error ? err.message : 'Failed to send invitation';\n console.error('Failed to create invitation:', err);\n setError(errorMessage);\n },\n }\n );\n };\n\n const handleOpenChange = (open: boolean) => {\n if (!open) {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n }\n };\n\n return (\n <ResponsiveDialog open={isInviteMemberDialogOpen} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(88vh,900px)] flex-col overflow-hidden sm:max-w-[960px] lg:max-w-[1120px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Invite Member</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Invite a new member to this workspace by email. They'll receive an invitation link to\n join.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto pr-2\">\n <div className=\"space-y-5 pb-4 pr-2\">\n {error && (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email Address *</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"member@example.com\"\n {...field}\n disabled={isPending}\n onChange={(e) => {\n field.onChange(e);\n setError(null);\n }}\n />\n </FormControl>\n <FormDescription>\n Enter the email address of the person you want to invite\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"roleIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>Roles (Optional)</FormLabel>\n <p className=\"text-sm text-text-secondary\">\n Choose roles. Hover any role to preview its full description.\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">{field.value.length} selected</Badge>\n ) : null}\n </div>\n <FormControl>\n <TooltipProvider>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-text-secondary\">Loading roles…</p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No assignable roles were returned for this workspace.\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = field.value.includes(role.id);\n return (\n <Tooltip key={role.id}>\n <TooltipTrigger asChild>\n <label\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{role.name}</span>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, role.id]\n : field.value.filter((id) => id !== role.id);\n field.onChange(nextValue);\n }}\n aria-label={`Select ${role.name}`}\n />\n </label>\n </TooltipTrigger>\n <TooltipContent className=\"max-w-xs text-sm leading-6\">\n <p className=\"font-medium\">{role.name}</p>\n <p className=\"mt-1 text-text-secondary\">\n {role.description ||\n 'No description available for this role.'}\n </p>\n {role.isSystem ? (\n <p className=\"mt-2 text-xs\">System role</p>\n ) : null}\n </TooltipContent>\n </Tooltip>\n );\n })\n )}\n </div>\n </TooltipProvider>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"projectIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>Projects to add on acceptance (Optional)</FormLabel>\n <p className=\"text-sm text-text-secondary\">\n The invited user will be added to these projects automatically after\n they accept the workspace invitation.\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">{field.value.length} selected</Badge>\n ) : null}\n </div>\n <FormControl>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingProjects ? (\n <p className=\"text-sm text-text-secondary\">Loading projects…</p>\n ) : availableProjects.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No active projects are available in this workspace yet.\n </p>\n ) : (\n availableProjects.map((project) => {\n const isSelected = field.value.includes(project.id);\n\n return (\n <label\n key={project.id}\n className={`flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n >\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"truncate text-sm font-semibold leading-tight\">\n {project.name}\n </p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"truncate font-mono\">{project.key}</span>\n <Badge variant=\"outline\">{project.status}</Badge>\n </div>\n </div>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, project.id]\n : field.value.filter((id) => id !== project.id);\n field.onChange(nextValue);\n }}\n aria-label={`Select ${project.name}`}\n />\n </label>\n );\n })\n )}\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-3 border-t pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isPending}>\n {isPending ? 'Sending...' : 'Send Invitation'}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InviteMemberDialog.js","names":[],"sources":["../../src/components/InviteMemberDialog.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport { useTr } from '../hooks/useTr';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\n Badge,\n Checkbox,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateInvitation } from '../hooks/useMemberMutations';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\n\nfunction inviteMemberSchema(tr: ReturnType<typeof useTr>) {\n return z.object({\n email: z\n .string()\n .email(tr('inviteMemberDialog.validation.emailInvalid', 'Invalid email address')),\n // Using a lambda default instead of `.default([])` to keep input type required\n // (zod v4 widens defaulted inputs to `T | undefined`, which breaks useForm typing).\n roleIds: z.array(z.string()),\n projectIds: z.array(z.string()),\n });\n}\n\ntype InviteMemberFormData = z.infer<ReturnType<typeof inviteMemberSchema>>;\n\ninterface InviteMemberDialogProps {\n /** Workspace ID to invite the member to (from URL params) */\n workspaceId?: string;\n}\n\nexport function InviteMemberDialog({ workspaceId }: InviteMemberDialogProps) {\n const tr = useTr();\n const { isInviteMemberDialogOpen, closeInviteMemberDialog } = useWorkspacesStore();\n const { mutate: createInvitation, isPending } = useCreateInvitation(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const { data: projectList, isLoading: isLoadingProjects } = useProjects(\n { archived: false },\n { limit: 100 },\n workspaceId\n );\n const [error, setError] = useState<string | null>(null);\n const availableProjects = projectList?.items ?? [];\n\n const schema = useMemo(() => inviteMemberSchema(tr), [tr]);\n\n const form = useForm<InviteMemberFormData>({\n schema,\n defaultValues: {\n email: '',\n roleIds: [],\n projectIds: [],\n },\n });\n\n const onSubmit = (data: InviteMemberFormData) => {\n setError(null);\n\n if (!workspaceId) {\n setError(\n tr(\n 'inviteMemberDialog.noWorkspaceError',\n 'No workspace selected. Please select a workspace first.'\n )\n );\n return;\n }\n\n createInvitation(\n {\n email: data.email,\n roleIds: data.roleIds,\n workspaceId,\n ...(data.projectIds.length > 0\n ? {\n context: {\n type: 'workspace.invitation',\n metadata: { projectIds: data.projectIds },\n },\n }\n : {}),\n },\n {\n onSuccess: () => {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n },\n onError: (err) => {\n const errorMessage =\n err instanceof Error\n ? err.message\n : tr('inviteMemberDialog.sendError', 'Failed to send invitation');\n console.error('Failed to create invitation:', err);\n setError(errorMessage);\n },\n }\n );\n };\n\n const handleOpenChange = (open: boolean) => {\n if (!open) {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n }\n };\n\n return (\n <ResponsiveDialog open={isInviteMemberDialogOpen} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(88vh,900px)] flex-col overflow-hidden sm:max-w-[960px] lg:max-w-[1120px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('inviteMemberDialog.title', 'Invite Member')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'inviteMemberDialog.description',\n \"Invite a new member to this workspace by email. They'll receive an invitation link to join.\"\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto pr-2\">\n <div className=\"space-y-5 pb-4 pr-2\">\n {error && (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n {tr('inviteMemberDialog.emailLabel', 'Email Address *')}\n </FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder={tr(\n 'inviteMemberDialog.emailPlaceholder',\n 'member@example.com'\n )}\n {...field}\n disabled={isPending}\n onChange={(e) => {\n field.onChange(e);\n setError(null);\n }}\n />\n </FormControl>\n <FormDescription>\n {tr(\n 'inviteMemberDialog.emailHint',\n 'Enter the email address of the person you want to invite'\n )}\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"roleIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>\n {tr('inviteMemberDialog.rolesLabel', 'Roles (Optional)')}\n </FormLabel>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'inviteMemberDialog.rolesHint',\n 'Choose roles. Hover any role to preview its full description.'\n )}\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">\n {field.value.length}{' '}\n {tr('inviteMemberDialog.selectedCount', 'selected')}\n </Badge>\n ) : null}\n </div>\n <FormControl>\n <TooltipProvider>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('inviteMemberDialog.loadingRoles', 'Loading roles…')}\n </p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'inviteMemberDialog.noRoles',\n 'No assignable roles were returned for this workspace.'\n )}\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = field.value.includes(role.id);\n return (\n <Tooltip key={role.id}>\n <TooltipTrigger asChild>\n <label\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{role.name}</span>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, role.id]\n : field.value.filter((id) => id !== role.id);\n field.onChange(nextValue);\n }}\n aria-label={tr(\n 'inviteMemberDialog.selectRoleAriaLabel',\n 'Select {{roleName}}',\n { roleName: role.name }\n )}\n />\n </label>\n </TooltipTrigger>\n <TooltipContent className=\"max-w-xs text-sm leading-6\">\n <p className=\"font-medium\">{role.name}</p>\n <p className=\"mt-1 text-text-secondary\">\n {role.description ||\n tr(\n 'inviteMemberDialog.noRoleDescription',\n 'No description available for this role.'\n )}\n </p>\n {role.isSystem ? (\n <p className=\"mt-2 text-xs\">\n {tr('inviteMemberDialog.systemRole', 'System role')}\n </p>\n ) : null}\n </TooltipContent>\n </Tooltip>\n );\n })\n )}\n </div>\n </TooltipProvider>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"projectIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>\n {tr(\n 'inviteMemberDialog.projectsLabel',\n 'Projects to add on acceptance (Optional)'\n )}\n </FormLabel>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'inviteMemberDialog.projectsHint',\n 'The invited user will be added to these projects automatically after they accept the workspace invitation.'\n )}\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">\n {field.value.length}{' '}\n {tr('inviteMemberDialog.selectedCount', 'selected')}\n </Badge>\n ) : null}\n </div>\n <FormControl>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingProjects ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('inviteMemberDialog.loadingProjects', 'Loading projects…')}\n </p>\n ) : availableProjects.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'inviteMemberDialog.noProjects',\n 'No active projects are available in this workspace yet.'\n )}\n </p>\n ) : (\n availableProjects.map((project) => {\n const isSelected = field.value.includes(project.id);\n\n return (\n <label\n key={project.id}\n className={`flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n >\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"truncate text-sm font-semibold leading-tight\">\n {project.name}\n </p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"truncate font-mono\">{project.key}</span>\n <Badge variant=\"outline\">{project.status}</Badge>\n </div>\n </div>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, project.id]\n : field.value.filter((id) => id !== project.id);\n field.onChange(nextValue);\n }}\n aria-label={tr(\n 'inviteMemberDialog.selectProjectAriaLabel',\n 'Select {{projectName}}',\n { projectName: project.name }\n )}\n />\n </label>\n );\n })\n )}\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-3 border-t pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isPending}\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isPending}>\n {isPending\n ? tr('inviteMemberDialog.sending', 'Sending...')\n : tr('inviteMemberDialog.submit', 'Send Invitation')}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|