@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.
Files changed (44) hide show
  1. package/dist/components/CreateProjectDialog.js +65 -61
  2. package/dist/components/CreateProjectDialog.js.map +1 -1
  3. package/dist/components/CreateWorkspaceDialog.js +93 -90
  4. package/dist/components/CreateWorkspaceDialog.js.map +1 -1
  5. package/dist/components/InviteMemberDialog.js +122 -111
  6. package/dist/components/InviteMemberDialog.js.map +1 -1
  7. package/dist/components/MemberListItem.js +55 -58
  8. package/dist/components/MemberListItem.js.map +1 -1
  9. package/dist/components/ProjectCard.js +93 -92
  10. package/dist/components/ProjectCard.js.map +1 -1
  11. package/dist/components/ProjectMemberPickerDialog.js +78 -93
  12. package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
  13. package/dist/components/RenameProjectDialog.js +47 -44
  14. package/dist/components/RenameProjectDialog.js.map +1 -1
  15. package/dist/components/WorkspaceCard.js +42 -43
  16. package/dist/components/WorkspaceCard.js.map +1 -1
  17. package/dist/components/WorkspacesErrorBoundary.js +43 -34
  18. package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
  19. package/dist/components/tags/ProjectTagsWidget.js +75 -77
  20. package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
  21. package/dist/hooks/useTr.js +14 -0
  22. package/dist/hooks/useTr.js.map +1 -0
  23. package/dist/index.js +2 -1
  24. package/dist/pages/ActivityListPage.js +3 -3
  25. package/dist/pages/CreateWorkspacePage.js +10 -10
  26. package/dist/pages/InviteAcceptancePage.js +49 -49
  27. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  28. package/dist/pages/MembersListPage.js +6 -6
  29. package/dist/pages/PendingInvitesPage.js +3 -3
  30. package/dist/pages/ProjectDetailPage.js +4 -4
  31. package/dist/pages/ProjectDetailPage.js.map +1 -1
  32. package/dist/pages/ProjectMembersPage.js +3 -3
  33. package/dist/pages/ProjectsListPage.js +5 -5
  34. package/dist/pages/WorkspaceDetailPage.js +4 -4
  35. package/dist/pages/WorkspaceInvitePage.js +4 -4
  36. package/dist/pages/WorkspaceSettingsPage.js +7 -7
  37. package/dist/pages/WorkspacesListPage.js +9 -9
  38. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +4 -4
  39. package/dist/pages/dashboard/widgets/StorageWidget.js +3 -3
  40. package/dist/pages/dashboard/widgets/WelcomeWidget.js +26 -16
  41. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  42. package/dist/translations/en.js +908 -0
  43. package/dist/translations/en.js.map +1 -0
  44. package/package.json +1 -1
@@ -1,227 +1,238 @@
1
- import { useWorkspacesStore as e } from "../store/workspacesStore.js";
2
- import { useCreateInvitation as t } from "../hooks/useMemberMutations.js";
3
- import { useWorkspaceRoles as n } from "../hooks/useWorkspaceRoles.js";
4
- import { useProjects as r } from "../hooks/useProjects.js";
5
- import { useState as i } from "react";
6
- import { AlertCircle as a } from "lucide-react";
7
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
8
- import { Badge as c, Button as l, Checkbox as u, ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogHeader as m, ResponsiveDialogTitle as h, Tooltip as g, TooltipContent as _, TooltipProvider as v, TooltipTrigger as y } from "@burdenoff/fe-libs/ui";
9
- import { FormControl as b, FormDescription as x, FormField as S, FormItem as C, FormLabel as w, FormMessage as T, FormProvider as E, Input as D, useForm as O, z as k } from "@burdenoff/fe-libs/form";
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
- var A = k.object({
12
- email: k.string().email("Invalid email address"),
13
- roleIds: k.array(k.string()),
14
- projectIds: k.array(k.string())
15
- });
16
- function j({ workspaceId: k }) {
17
- let { isInviteMemberDialogOpen: j, closeInviteMemberDialog: M } = e(), { mutate: N, isPending: P } = t(k), { data: F = [], isLoading: I } = n(k), { data: L, isLoading: R } = r({ archived: !1 }, { limit: 100 }, k), [z, B] = i(null), V = L?.items ?? [], H = O({
18
- schema: A,
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
- }), U = (e) => {
25
- if (B(null), !k) {
26
- B("No workspace selected. Please select a workspace first.");
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
- N({
32
+ I({
30
33
  email: e.email,
31
34
  roleIds: e.roleIds,
32
- workspaceId: k,
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
- M(), H.reset(), B(null);
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), B(t);
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
- }, W = (e) => {
47
- e || (M(), H.reset(), B(null));
49
+ }, q = (e) => {
50
+ e || (F(), G.reset(), U(null));
48
51
  };
49
- return /* @__PURE__ */ o(d, {
50
- open: j,
51
- onOpenChange: W,
52
- children: /* @__PURE__ */ s(f, {
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__ */ s(m, { children: [/* @__PURE__ */ o(h, { children: "Invite Member" }), /* @__PURE__ */ o(p, { children: "Invite a new member to this workspace by email. They'll receive an invitation link to join." })] }), /* @__PURE__ */ o(E, {
55
- ...H,
56
- children: /* @__PURE__ */ s("form", {
57
- onSubmit: H.handleSubmit(U),
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__ */ o("div", {
62
+ children: [/* @__PURE__ */ c("div", {
60
63
  className: "min-h-0 flex-1 overflow-y-auto pr-2",
61
- children: /* @__PURE__ */ s("div", {
64
+ children: /* @__PURE__ */ l("div", {
62
65
  className: "space-y-5 pb-4 pr-2",
63
66
  children: [
64
- z && /* @__PURE__ */ s("div", {
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__ */ o(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ o("span", { children: z })]
69
+ children: [/* @__PURE__ */ c(s, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ c("span", { children: H })]
67
70
  }),
68
- /* @__PURE__ */ o(S, {
69
- control: H.control,
71
+ /* @__PURE__ */ c(w, {
72
+ control: G.control,
70
73
  name: "email",
71
- render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
72
- /* @__PURE__ */ o(w, { children: "Email Address *" }),
73
- /* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(D, {
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: P,
80
+ disabled: L,
78
81
  onChange: (t) => {
79
- e.onChange(t), B(null);
82
+ e.onChange(t), U(null);
80
83
  }
81
84
  }) }),
82
- /* @__PURE__ */ o(x, { children: "Enter the email address of the person you want to invite" }),
83
- /* @__PURE__ */ o(T, {})
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__ */ o(S, {
87
- control: H.control,
89
+ /* @__PURE__ */ c(w, {
90
+ control: G.control,
88
91
  name: "roleIds",
89
- render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
90
- /* @__PURE__ */ s("div", {
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__ */ s("div", {
95
+ children: [/* @__PURE__ */ l("div", {
93
96
  className: "space-y-1",
94
- children: [/* @__PURE__ */ o(w, { children: "Roles (Optional)" }), /* @__PURE__ */ o("p", {
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__ */ s(c, {
101
+ }), e.value.length > 0 ? /* @__PURE__ */ l(u, {
99
102
  variant: "secondary",
100
- children: [e.value.length, " selected"]
103
+ children: [
104
+ e.value.length,
105
+ " ",
106
+ N("inviteMemberDialog.selectedCount", "selected")
107
+ ]
101
108
  }) : null]
102
109
  }),
103
- /* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(v, { children: /* @__PURE__ */ o("div", {
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: I ? /* @__PURE__ */ o("p", {
112
+ children: z ? /* @__PURE__ */ c("p", {
106
113
  className: "text-sm text-text-secondary",
107
- children: "Loading roles…"
108
- }) : F.length === 0 ? /* @__PURE__ */ o("p", {
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
- }) : F.map((t) => {
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__ */ s(g, { children: [/* @__PURE__ */ o(y, {
120
+ return /* @__PURE__ */ l(v, { children: [/* @__PURE__ */ c(x, {
114
121
  asChild: !0,
115
- children: /* @__PURE__ */ s("label", {
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__ */ o("span", {
124
+ children: [/* @__PURE__ */ c("span", {
118
125
  className: "min-w-0 flex-1 truncate",
119
126
  children: t.name
120
- }), /* @__PURE__ */ o(u, {
127
+ }), /* @__PURE__ */ c(f, {
121
128
  checked: n,
122
- disabled: P,
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": `Select ${t.name}`
134
+ "aria-label": N("inviteMemberDialog.selectRoleAriaLabel", "Select {{roleName}}", { roleName: t.name })
128
135
  })]
129
136
  })
130
- }), /* @__PURE__ */ s(_, {
137
+ }), /* @__PURE__ */ l(y, {
131
138
  className: "max-w-xs text-sm leading-6",
132
139
  children: [
133
- /* @__PURE__ */ o("p", {
140
+ /* @__PURE__ */ c("p", {
134
141
  className: "font-medium",
135
142
  children: t.name
136
143
  }),
137
- /* @__PURE__ */ o("p", {
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__ */ o("p", {
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__ */ o(T, {})
156
+ /* @__PURE__ */ c(D, {})
150
157
  ] })
151
158
  }),
152
- /* @__PURE__ */ o(S, {
153
- control: H.control,
159
+ /* @__PURE__ */ c(w, {
160
+ control: G.control,
154
161
  name: "projectIds",
155
- render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
156
- /* @__PURE__ */ s("div", {
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__ */ s("div", {
165
+ children: [/* @__PURE__ */ l("div", {
159
166
  className: "space-y-1",
160
- children: [/* @__PURE__ */ o(w, { children: "Projects to add on acceptance (Optional)" }), /* @__PURE__ */ o("p", {
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__ */ s(c, {
171
+ }), e.value.length > 0 ? /* @__PURE__ */ l(u, {
165
172
  variant: "secondary",
166
- children: [e.value.length, " selected"]
173
+ children: [
174
+ e.value.length,
175
+ " ",
176
+ N("inviteMemberDialog.selectedCount", "selected")
177
+ ]
167
178
  }) : null]
168
179
  }),
169
- /* @__PURE__ */ o(b, { children: /* @__PURE__ */ o("div", {
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: R ? /* @__PURE__ */ o("p", {
182
+ children: V ? /* @__PURE__ */ c("p", {
172
183
  className: "text-sm text-text-secondary",
173
- children: "Loading projects…"
174
- }) : V.length === 0 ? /* @__PURE__ */ o("p", {
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
- }) : V.map((t) => {
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__ */ s("label", {
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__ */ s("div", {
192
+ children: [/* @__PURE__ */ l("div", {
182
193
  className: "min-w-0 flex-1 space-y-1",
183
- children: [/* @__PURE__ */ o("p", {
194
+ children: [/* @__PURE__ */ c("p", {
184
195
  className: "truncate text-sm font-semibold leading-tight",
185
196
  children: t.name
186
- }), /* @__PURE__ */ s("div", {
197
+ }), /* @__PURE__ */ l("div", {
187
198
  className: "flex items-center gap-2 text-xs text-text-secondary",
188
- children: [/* @__PURE__ */ o("span", {
199
+ children: [/* @__PURE__ */ c("span", {
189
200
  className: "truncate font-mono",
190
201
  children: t.key
191
- }), /* @__PURE__ */ o(c, {
202
+ }), /* @__PURE__ */ c(u, {
192
203
  variant: "outline",
193
204
  children: t.status
194
205
  })]
195
206
  })]
196
- }), /* @__PURE__ */ o(u, {
207
+ }), /* @__PURE__ */ c(f, {
197
208
  checked: n,
198
- disabled: P,
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": `Select ${t.name}`
214
+ "aria-label": N("inviteMemberDialog.selectProjectAriaLabel", "Select {{projectName}}", { projectName: t.name })
204
215
  })]
205
216
  }, t.id);
206
217
  })
207
218
  }) }),
208
- /* @__PURE__ */ o(T, {})
219
+ /* @__PURE__ */ c(D, {})
209
220
  ] })
210
221
  })
211
222
  ]
212
223
  })
213
- }), /* @__PURE__ */ s("div", {
224
+ }), /* @__PURE__ */ l("div", {
214
225
  className: "flex justify-end gap-3 border-t pt-4",
215
- children: [/* @__PURE__ */ o(l, {
226
+ children: [/* @__PURE__ */ c(d, {
216
227
  type: "button",
217
228
  variant: "outline",
218
- onClick: () => W(!1),
219
- disabled: P,
220
- children: "Cancel"
221
- }), /* @__PURE__ */ o(l, {
229
+ onClick: () => q(!1),
230
+ disabled: L,
231
+ children: N("common.cancel", "Cancel")
232
+ }), /* @__PURE__ */ c(d, {
222
233
  type: "submit",
223
- disabled: P,
224
- children: P ? "Sending..." : "Send Invitation"
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 { j as InviteMemberDialog };
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":";;;;;;;;;;;AAkCA,SAAS,EAAmB,GAA8B;AACxD,QAAO,EAAE,OAAO;EACd,OAAO,EACJ,QAAQ,CACR,MAAM,EAAG,8CAA8C,wBAAwB,CAAC;EAGnF,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC;EAC5B,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC;EAChC,CAAC;;AAUJ,SAAgB,EAAmB,EAAE,kBAAwC;CAC3E,IAAM,IAAK,GAAO,EACZ,EAAE,6BAA0B,+BAA4B,GAAoB,EAC5E,EAAE,QAAQ,GAAkB,iBAAc,EAAoB,EAAY,EAC1E,EAAE,MAAM,IAAiB,EAAE,EAAE,WAAW,MAAmB,EAAkB,EAAY,EACzF,EAAE,MAAM,GAAa,WAAW,MAAsB,EAC1D,EAAE,UAAU,IAAO,EACnB,EAAE,OAAO,KAAK,EACd,EACD,EACK,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAoB,GAAa,SAAS,EAAE,EAI5C,IAAO,EAA8B;EACzC,QAHa,QAAc,EAAmB,EAAG,EAAE,CAAC,EAAG,CAAC;EAIxD,eAAe;GACb,OAAO;GACP,SAAS,EAAE;GACX,YAAY,EAAE;GACf;EACF,CAAC,EAEI,KAAY,MAA+B;AAG/C,MAFA,EAAS,KAAK,EAEV,CAAC,GAAa;AAChB,KACE,EACE,uCACA,0DACD,CACF;AACD;;AAGF,IACE;GACE,OAAO,EAAK;GACZ,SAAS,EAAK;GACd;GACA,GAAI,EAAK,WAAW,SAAS,IACzB,EACE,SAAS;IACP,MAAM;IACN,UAAU,EAAE,YAAY,EAAK,YAAY;IAC1C,EACF,GACD,EAAE;GACP,EACD;GACE,iBAAiB;AAGf,IAFA,GAAyB,EACzB,EAAK,OAAO,EACZ,EAAS,KAAK;;GAEhB,UAAU,MAAQ;IAChB,IAAM,IACJ,aAAe,QACX,EAAI,UACJ,EAAG,gCAAgC,4BAA4B;AAErE,IADA,QAAQ,MAAM,gCAAgC,EAAI,EAClD,EAAS,EAAa;;GAEzB,CACF;IAGG,KAAoB,MAAkB;AAC1C,EAAK,MACH,GAAyB,EACzB,EAAK,OAAO,EACZ,EAAS,KAAK;;AAIlB,QACE,kBAAC,GAAD;EAAkB,MAAM;EAA0B,cAAc;YAC9D,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACG,EAAG,4BAA4B,gBAAgB,EAC1B,CAAA,EACxB,kBAAC,GAAD,EAAA,UACG,EACC,kCACA,8FACD,EAC2B,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,GAAD;IAAc,GAAI;cAChB,kBAAC,QAAD;KAAM,UAAU,EAAK,aAAa,EAAS;KAAE,WAAU;eAAvD,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wBAAyB,CAAA,EAChD,kBAAC,QAAD,EAAA,UAAO,GAAa,CAAA,CAChB;;QAGR,kBAAC,GAAD;SACE,SAAS,EAAK;SACd,MAAK;SACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;UACE,kBAAC,GAAD,EAAA,UACG,EAAG,iCAAiC,kBAAkB,EAC7C,CAAA;UACZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;WACE,MAAK;WACL,aAAa,EACX,uCACA,qBACD;WACD,GAAI;WACJ,UAAU;WACV,WAAW,MAAM;AAEf,YADA,EAAM,SAAS,EAAE,EACjB,EAAS,KAAK;;WAEhB,CAAA,EACU,CAAA;UACd,kBAAC,GAAD,EAAA,UACG,EACC,gCACA,2DACD,EACe,CAAA;UAClB,kBAAC,GAAD,EAAe,CAAA;UACN,EAAA,CAAA;SAEb,CAAA;QAEF,kBAAC,GAAD;SACE,SAAS,EAAK;SACd,MAAK;SACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAA,UACG,EAAG,iCAAiC,mBAAmB,EAC9C,CAAA,EACZ,kBAAC,KAAD;aAAG,WAAU;uBACV,EACC,gCACA,gEACD;aACC,CAAA,CACA;eACL,EAAM,MAAM,SAAS,IACpB,kBAAC,GAAD;YAAO,SAAQ;sBAAf;aACG,EAAM,MAAM;aAAQ;aACpB,EAAG,oCAAoC,WAAW;aAC7C;gBACN,KACA;;UACN,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;WAAK,WAAU;qBACZ,IACC,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAG,mCAAmC,iBAAiB;YACtD,CAAA,GACF,EAAe,WAAW,IAC5B,kBAAC,KAAD;YAAG,WAAU;sBACV,EACC,8BACA,wDACD;YACC,CAAA,GAEJ,EAAe,KAAK,MAAS;YAC3B,IAAM,IAAa,EAAM,MAAM,SAAS,EAAK,GAAG;AAChD,mBACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;aAAgB,SAAA;uBACd,kBAAC,SAAD;cACE,WAAW,iOACT,IACI,uEACA;wBAJR,CAOE,kBAAC,QAAD;eAAM,WAAU;yBAA2B,EAAK;eAAY,CAAA,EAC5D,kBAAC,GAAD;eACE,SAAS;eACT,UAAU;eACV,kBAAkB,MAAY;gBAC5B,IAAM,IAAY,IACd,CAAC,GAAG,EAAM,OAAO,EAAK,GAAG,GACzB,EAAM,MAAM,QAAQ,MAAO,MAAO,EAAK,GAAG;AAC9C,kBAAM,SAAS,EAAU;;eAE3B,cAAY,EACV,0CACA,uBACA,EAAE,UAAU,EAAK,MAAM,CACxB;eACD,CAAA,CACI;;aACO,CAAA,EACjB,kBAAC,GAAD;aAAgB,WAAU;uBAA1B;cACE,kBAAC,KAAD;eAAG,WAAU;yBAAe,EAAK;eAAS,CAAA;cAC1C,kBAAC,KAAD;eAAG,WAAU;yBACV,EAAK,eACJ,EACE,wCACA,0CACD;eACD,CAAA;cACH,EAAK,WACJ,kBAAC,KAAD;eAAG,WAAU;yBACV,EAAG,iCAAiC,cAAc;eACjD,CAAA,GACF;cACW;eACT,EAAA,EA1CI,EAAK,GA0CT;aAEZ;WAEA,CAAA,EACU,CAAA,EACN,CAAA;UACd,kBAAC,GAAD,EAAe,CAAA;UACN,EAAA,CAAA;SAEb,CAAA;QAEF,kBAAC,GAAD;SACE,SAAS,EAAK;SACd,MAAK;SACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAA,UACG,EACC,oCACA,2CACD,EACS,CAAA,EACZ,kBAAC,KAAD;aAAG,WAAU;uBACV,EACC,mCACA,6GACD;aACC,CAAA,CACA;eACL,EAAM,MAAM,SAAS,IACpB,kBAAC,GAAD;YAAO,SAAQ;sBAAf;aACG,EAAM,MAAM;aAAQ;aACpB,EAAG,oCAAoC,WAAW;aAC7C;gBACN,KACA;;UACN,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;WAAK,WAAU;qBACZ,IACC,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAG,sCAAsC,oBAAoB;YAC5D,CAAA,GACF,EAAkB,WAAW,IAC/B,kBAAC,KAAD;YAAG,WAAU;sBACV,EACC,iCACA,0DACD;YACC,CAAA,GAEJ,EAAkB,KAAK,MAAY;YACjC,IAAM,IAAa,EAAM,MAAM,SAAS,EAAQ,GAAG;AAEnD,mBACE,kBAAC,SAAD;aAEE,WAAW,kMACT,IACI,uEACA;uBALR,CAQE,kBAAC,OAAD;cAAK,WAAU;wBAAf,CACE,kBAAC,KAAD;eAAG,WAAU;yBACV,EAAQ;eACP,CAAA,EACJ,kBAAC,OAAD;eAAK,WAAU;yBAAf,CACE,kBAAC,QAAD;gBAAM,WAAU;0BAAsB,EAAQ;gBAAW,CAAA,EACzD,kBAAC,GAAD;gBAAO,SAAQ;0BAAW,EAAQ;gBAAe,CAAA,CAC7C;iBACF;iBACN,kBAAC,GAAD;cACE,SAAS;cACT,UAAU;cACV,kBAAkB,MAAY;eAC5B,IAAM,IAAY,IACd,CAAC,GAAG,EAAM,OAAO,EAAQ,GAAG,GAC5B,EAAM,MAAM,QAAQ,MAAO,MAAO,EAAQ,GAAG;AACjD,iBAAM,SAAS,EAAU;;cAE3B,cAAY,EACV,6CACA,0BACA,EAAE,aAAa,EAAQ,MAAM,CAC9B;cACD,CAAA,CACI;eA/BD,EAAQ,GA+BP;aAEV;WAEA,CAAA,EACM,CAAA;UACd,kBAAC,GAAD,EAAe,CAAA;UACN,EAAA,CAAA;SAEb,CAAA;QACE;;MACF,CAAA,EAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,eAAe,EAAiB,GAAM;OACtC,UAAU;iBAET,EAAG,iBAAiB,SAAS;OACvB,CAAA,EACT,kBAAC,GAAD;OAAQ,MAAK;OAAS,UAAU;iBAC7B,IACG,EAAG,8BAA8B,aAAa,GAC9C,EAAG,6BAA6B,kBAAkB;OAC/C,CAAA,CACL;QACD;;IACM,CAAA,CACS;;EACT,CAAA"}