@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,12 +1,13 @@
1
1
  import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
2
- import { nativeImpact as t, nativeNotify as n } from "../utils/nativeBridge.js";
3
- import { useInfiniteWorkspaceMembers as r } from "../hooks/useWorkspaceMembers.js";
4
- import { useAddProjectMember as ee } from "../hooks/useProjectMemberMutations.js";
5
- import { useInfiniteProjectMembers as te } from "../hooks/useProjectMembers.js";
6
- import { useEffect as ne, useMemo as i, useState as a } from "react";
7
- import { AlertCircle as o, Search as re, UserPlus as s } from "lucide-react";
8
- import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
9
- import { Badge as ie, Button as d, Input as f, ResponsiveDialog as p, ResponsiveDialogContent as ae, ResponsiveDialogDescription as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g, Skeleton as _ } from "@burdenoff/fe-libs/ui";
2
+ import { useTr as t } from "../hooks/useTr.js";
3
+ import { nativeImpact as n, nativeNotify as r } from "../utils/nativeBridge.js";
4
+ import { useInfiniteWorkspaceMembers as i } from "../hooks/useWorkspaceMembers.js";
5
+ import { useAddProjectMember as a } from "../hooks/useProjectMemberMutations.js";
6
+ import { useInfiniteProjectMembers as ee } from "../hooks/useProjectMembers.js";
7
+ import { useEffect as o, useMemo as s, useState as c } from "react";
8
+ import { AlertCircle as l, Search as u, UserPlus as d } from "lucide-react";
9
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
10
+ import { Badge as te, Button as h, Input as ne, ResponsiveDialog as re, ResponsiveDialogContent as ie, ResponsiveDialogDescription as ae, ResponsiveDialogHeader as oe, ResponsiveDialogTitle as g, Skeleton as _ } from "@burdenoff/fe-libs/ui";
10
11
  import { ActorIdentity as v, useActorProfiles as y } from "@burdenoff/fe-libs/shared";
11
12
  //#region src/components/ProjectMemberPickerDialog.tsx
12
13
  var b = 100, x = 10;
@@ -17,9 +18,9 @@ function S(e) {
17
18
  email: typeof t?.email == "string" ? t.email : void 0
18
19
  };
19
20
  }
20
- function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName: D }) {
21
- let { apiGatewayUrl: O, authToken: oe, organizationId: se } = e(), { mutate: ce, isPending: k } = ee(T), [A, j] = a(""), [le, M] = a(null), [N, P] = a(null), { data: F, isLoading: I, isError: L, error: R, hasNextPage: z, fetchNextPage: B, isFetchingNextPage: V, refetch: H } = r(T, b, C), { data: U, isLoading: ue, isError: W, error: G, hasNextPage: K, fetchNextPage: q, isFetchingNextPage: J } = te(E, T, b, C);
22
- ne(() => {
21
+ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName: se }) {
22
+ let D = t(), { apiGatewayUrl: ce, authToken: le, organizationId: ue } = e(), { mutate: O, isPending: k } = a(T), [A, j] = c(""), [M, N] = c(null), [P, F] = c(null), { data: I, isLoading: L, isError: R, error: z, hasNextPage: B, fetchNextPage: V, isFetchingNextPage: H, refetch: de } = i(T, b, C), { data: U, isLoading: fe, isError: W, error: G, hasNextPage: K, fetchNextPage: q, isFetchingNextPage: J } = ee(E, T, b, C);
23
+ o(() => {
23
24
  !C || !K || J || W || (U?.pages.length ?? 0) >= x || q();
24
25
  }, [
25
26
  q,
@@ -29,15 +30,15 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
29
30
  C,
30
31
  U?.pages.length
31
32
  ]);
32
- let Y = i(() => F?.pages.flatMap((e) => e.items) ?? [], [F?.pages]), X = i(() => new Set(U?.pages.flatMap((e) => e.items.map((e) => e.userId)) ?? []), [U?.pages]), de = F?.pages[0]?.total ?? Y.length, Z = (U?.pages.length ?? 0) >= x, fe = !W && K && !Z, Q = y(i(() => Y.map((e) => ({
33
+ let Y = s(() => I?.pages.flatMap((e) => e.items) ?? [], [I?.pages]), X = s(() => new Set(U?.pages.flatMap((e) => e.items.map((e) => e.userId)) ?? []), [U?.pages]), pe = I?.pages[0]?.total ?? Y.length, Z = (U?.pages.length ?? 0) >= x, me = !W && K && !Z, Q = y(s(() => Y.map((e) => ({
33
34
  actorId: e.userId,
34
35
  actorType: "user"
35
36
  })), [Y]), {
36
- apiGatewayUrl: O,
37
- authToken: oe,
38
- orgId: se,
37
+ apiGatewayUrl: ce,
38
+ authToken: le,
39
+ orgId: ue,
39
40
  workspaceId: T
40
- }), $ = i(() => {
41
+ }), $ = s(() => {
41
42
  let e = A.trim().toLowerCase();
42
43
  return Y.filter((t) => {
43
44
  if (X.has(t.userId)) return !1;
@@ -52,151 +53,135 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
52
53
  Q,
53
54
  A,
54
55
  Y
55
- ]), pe = (e) => {
56
- t("medium"), M(e), P(null), ce({
56
+ ]), he = (e) => {
57
+ n("medium"), N(e), F(null), O({
57
58
  projectId: E,
58
59
  userId: e
59
60
  }, {
60
61
  onSuccess: () => {
61
- n("success"), M(null), w(!1);
62
+ r("success"), N(null), w(!1);
62
63
  },
63
64
  onError: (e) => {
64
- n("error"), M(null), P(e instanceof Error ? e.message : "Failed to add member to project");
65
+ r("error"), N(null), F(e instanceof Error ? e.message : D("projectMemberPickerDialog.addError", "Failed to add member to project"));
65
66
  }
66
67
  });
67
68
  };
68
- return /* @__PURE__ */ l(p, {
69
+ return /* @__PURE__ */ p(re, {
69
70
  open: C,
70
71
  onOpenChange: w,
71
- children: /* @__PURE__ */ u(ae, {
72
+ children: /* @__PURE__ */ m(ie, {
72
73
  className: "z-[120] flex h-[min(85vh,760px)] flex-col overflow-hidden sm:max-w-[720px]",
73
- children: [/* @__PURE__ */ u(h, { children: [/* @__PURE__ */ l(g, { children: "Add member to project" }), /* @__PURE__ */ u(m, { children: [
74
- "Add an existing workspace member to ",
75
- D || "this project",
76
- ". Only current workspace members are eligible."
77
- ] })] }), /* @__PURE__ */ u("div", {
74
+ children: [/* @__PURE__ */ m(oe, { children: [/* @__PURE__ */ p(g, { children: D("projectMemberPickerDialog.title", "Add member to project") }), /* @__PURE__ */ p(ae, { children: D("projectMemberPickerDialog.description", "Add an existing workspace member to {{projectName}}. Only current workspace members are eligible.", { projectName: se ?? D("projectMemberPickerDialog.thisProject", "this project") }) })] }), /* @__PURE__ */ m("div", {
78
75
  className: "space-y-4 overflow-hidden",
79
76
  children: [
80
- N ? /* @__PURE__ */ u("div", {
77
+ P ? /* @__PURE__ */ m("div", {
81
78
  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",
82
- children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: N })]
79
+ children: [/* @__PURE__ */ p(l, { className: "size-4 shrink-0" }), /* @__PURE__ */ p("span", { children: P })]
83
80
  }) : null,
84
- G instanceof Error ? /* @__PURE__ */ u("div", {
81
+ G instanceof Error ? /* @__PURE__ */ m("div", {
85
82
  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",
86
- children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: G.message })]
83
+ children: [/* @__PURE__ */ p(l, { className: "size-4 shrink-0" }), /* @__PURE__ */ p("span", { children: G.message })]
87
84
  }) : null,
88
- R instanceof Error ? /* @__PURE__ */ u("div", {
85
+ z instanceof Error ? /* @__PURE__ */ m("div", {
89
86
  className: "space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
90
- children: [/* @__PURE__ */ u("div", {
87
+ children: [/* @__PURE__ */ m("div", {
91
88
  className: "flex items-center gap-2",
92
- children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: R.message })]
93
- }), /* @__PURE__ */ l(d, {
89
+ children: [/* @__PURE__ */ p(l, { className: "size-4 shrink-0" }), /* @__PURE__ */ p("span", { children: z.message })]
90
+ }), /* @__PURE__ */ p(h, {
94
91
  variant: "outline",
95
- onClick: () => void H(),
96
- children: "Retry Loading Members"
92
+ onClick: () => void de(),
93
+ children: D("projectMemberPickerDialog.retryLoadingMembers", "Retry Loading Members")
97
94
  })]
98
95
  }) : null,
99
- /* @__PURE__ */ u("div", {
96
+ /* @__PURE__ */ m("div", {
100
97
  className: "flex items-center gap-2",
101
- children: [/* @__PURE__ */ u("div", {
98
+ children: [/* @__PURE__ */ m("div", {
102
99
  className: "relative flex-1",
103
- children: [/* @__PURE__ */ l(re, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ l(f, {
100
+ children: [/* @__PURE__ */ p(u, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ p(ne, {
104
101
  value: A,
105
102
  onChange: (e) => j(e.target.value),
106
- placeholder: "Search workspace members",
103
+ placeholder: D("projectMemberPickerDialog.searchPlaceholder", "Search workspace members"),
107
104
  className: "pl-9",
108
105
  disabled: k
109
106
  })]
110
- }), /* @__PURE__ */ u(ie, {
107
+ }), /* @__PURE__ */ p(te, {
111
108
  variant: "secondary",
112
- children: [
113
- z ? `${$.length}+` : $.length,
114
- " ",
115
- "eligible"
116
- ]
109
+ children: D("projectMemberPickerDialog.eligibleCount", "{{count}} eligible", { count: B ? `${$.length}+` : $.length })
117
110
  })]
118
111
  }),
119
- /* @__PURE__ */ l("div", {
112
+ /* @__PURE__ */ p("div", {
120
113
  className: "max-h-[480px] space-y-3 overflow-y-auto pr-1",
121
- children: L ? null : I || ue || fe || J ? Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ u("div", {
114
+ children: R ? null : L || fe || me || J ? Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ m("div", {
122
115
  className: "rounded-lg border bg-bg-surface p-4",
123
- children: [/* @__PURE__ */ l(_, { className: "h-5 w-40" }), /* @__PURE__ */ l(_, { className: "mt-2 h-4 w-56" })]
124
- }, t)) : G instanceof Error ? null : $.length === 0 ? /* @__PURE__ */ u("div", {
116
+ children: [/* @__PURE__ */ p(_, { className: "h-5 w-40" }), /* @__PURE__ */ p(_, { className: "mt-2 h-4 w-56" })]
117
+ }, t)) : G instanceof Error ? null : $.length === 0 ? /* @__PURE__ */ m("div", {
125
118
  className: "rounded-lg border bg-bg-surface p-8 text-center",
126
119
  children: [
127
- /* @__PURE__ */ l(s, { className: "mx-auto mb-3 size-10 text-text-secondary" }),
128
- /* @__PURE__ */ l("h3", {
120
+ /* @__PURE__ */ p(d, { className: "mx-auto mb-3 size-10 text-text-secondary" }),
121
+ /* @__PURE__ */ p("h3", {
129
122
  className: "text-lg font-semibold",
130
- children: "No eligible members found"
123
+ children: D("projectMemberPickerDialog.emptyTitle", "No eligible members found")
131
124
  }),
132
- /* @__PURE__ */ l("p", {
125
+ /* @__PURE__ */ p("p", {
133
126
  className: "mt-2 text-sm text-text-secondary",
134
- children: "All workspace members are already part of this project, or nothing matched your search."
127
+ children: D("projectMemberPickerDialog.emptyDescription", "All workspace members are already part of this project, or nothing matched your search.")
135
128
  }),
136
- Z ? /* @__PURE__ */ u("p", {
129
+ Z ? /* @__PURE__ */ p("p", {
137
130
  className: "mt-3 text-xs text-text-secondary",
138
- children: [
139
- "Loaded the first ",
140
- x * b,
141
- " ",
142
- "project members to avoid an excessively long blocking load."
143
- ]
131
+ children: D("projectMemberPickerDialog.autoloadCapNote", "Loaded the first {{count}} project members to avoid an excessively long blocking load.", { count: x * b })
144
132
  }) : null,
145
- z ? /* @__PURE__ */ l("div", {
133
+ B ? /* @__PURE__ */ p("div", {
146
134
  className: "mt-4",
147
- children: /* @__PURE__ */ l(d, {
135
+ children: /* @__PURE__ */ p(h, {
148
136
  variant: "outline",
149
- onClick: () => void B(),
150
- disabled: V || k,
151
- children: V ? "Loading more…" : "Load More Members"
137
+ onClick: () => void V(),
138
+ disabled: H || k,
139
+ children: H ? D("projectMemberPickerDialog.loadingMore", "Loading more…") : D("projectMemberPickerDialog.loadMore", "Load More Members")
152
140
  })
153
141
  }) : null
154
142
  ]
155
- }) : /* @__PURE__ */ u(c, { children: [$.map((e) => {
156
- let t = Q[e.userId], n = S(e), r = k && le === e.userId;
157
- return /* @__PURE__ */ u("div", {
143
+ }) : /* @__PURE__ */ m(f, { children: [$.map((e) => {
144
+ let t = Q[e.userId], n = S(e), r = k && M === e.userId;
145
+ return /* @__PURE__ */ m("div", {
158
146
  className: "flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4",
159
- children: [/* @__PURE__ */ l("div", {
147
+ children: [/* @__PURE__ */ p("div", {
160
148
  className: "min-w-0 flex-1",
161
- children: t ? /* @__PURE__ */ l(v, {
149
+ children: t ? /* @__PURE__ */ p(v, {
162
150
  profile: t,
163
151
  variant: "cell"
164
- }) : /* @__PURE__ */ u("div", {
152
+ }) : /* @__PURE__ */ m("div", {
165
153
  className: "min-w-0",
166
- children: [/* @__PURE__ */ l("p", {
154
+ children: [/* @__PURE__ */ p("p", {
167
155
  className: "truncate font-medium",
168
156
  children: n.name
169
- }), n.email ? /* @__PURE__ */ l("p", {
157
+ }), n.email ? /* @__PURE__ */ p("p", {
170
158
  className: "truncate text-sm text-text-secondary",
171
159
  children: n.email
172
- }) : /* @__PURE__ */ l("p", {
160
+ }) : /* @__PURE__ */ p("p", {
173
161
  className: "truncate text-xs text-text-secondary",
174
162
  children: e.userId
175
163
  })]
176
164
  })
177
- }), /* @__PURE__ */ u(d, {
165
+ }), /* @__PURE__ */ m(h, {
178
166
  size: "sm",
179
- onClick: () => pe(e.userId),
167
+ onClick: () => he(e.userId),
180
168
  disabled: k,
181
- children: [/* @__PURE__ */ l(s, { className: "mr-2 size-4" }), r ? "Adding..." : "Add"]
169
+ children: [/* @__PURE__ */ p(d, { className: "mr-2 size-4" }), r ? D("projectMemberPickerDialog.adding", "Adding...") : D("projectMemberPickerDialog.add", "Add")]
182
170
  })]
183
171
  }, e.id);
184
- }), z ? /* @__PURE__ */ u("div", {
172
+ }), B ? /* @__PURE__ */ m("div", {
185
173
  className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center",
186
- children: [/* @__PURE__ */ u("p", {
174
+ children: [/* @__PURE__ */ p("p", {
187
175
  className: "text-sm text-text-secondary",
188
- children: [
189
- "Showing ",
190
- Y.length,
191
- " of ",
192
- de,
193
- " workspace members."
194
- ]
195
- }), /* @__PURE__ */ l(d, {
176
+ children: D("projectMemberPickerDialog.showingCount", "Showing {{shown}} of {{total}} workspace members.", {
177
+ shown: Y.length,
178
+ total: pe
179
+ })
180
+ }), /* @__PURE__ */ p(h, {
196
181
  variant: "outline",
197
- onClick: () => void B(),
198
- disabled: V || k,
199
- children: V ? "Loading more…" : "Load More Members"
182
+ onClick: () => void V(),
183
+ disabled: H || k,
184
+ children: H ? D("projectMemberPickerDialog.loadingMore", "Loading more…") : D("projectMemberPickerDialog.loadMore", "Load More Members")
200
185
  })]
201
186
  }) : null] })
202
187
  })
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMemberPickerDialog.js","names":[],"sources":["../../src/components/ProjectMemberPickerDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Search, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useAddProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useInfiniteWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport type { WorkspaceMember } from '../types';\n\nconst MEMBER_PAGE_SIZE = 100;\nconst MAX_AUTOLOADED_PROJECT_MEMBER_PAGES = 10;\n\nfunction getFallbackDisplay(member: WorkspaceMember): { name: string; email?: string } {\n const meta = member.context?.metadata;\n const name =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : member.userId;\n const email = typeof meta?.email === 'string' ? meta.email : undefined;\n return { name, email };\n}\n\nexport interface ProjectMemberPickerDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n projectId: string;\n projectName?: string;\n}\n\nexport function ProjectMemberPickerDialog({\n open,\n onOpenChange,\n workspaceId,\n projectId,\n projectName,\n}: ProjectMemberPickerDialogProps) {\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const { mutate: addProjectMember, isPending: isAdding } = useAddProjectMember(workspaceId);\n const [searchValue, setSearchValue] = useState('');\n const [addingUserId, setAddingUserId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const {\n data: workspaceMembersPages,\n isLoading: isLoadingMembers,\n isError: isErrorWorkspaceMembers,\n error: workspaceMembersError,\n hasNextPage: hasMoreWorkspaceMembers,\n fetchNextPage: fetchNextWorkspaceMembersPage,\n isFetchingNextPage: isFetchingNextWorkspaceMembersPage,\n refetch: refetchWorkspaceMembers,\n } = useInfiniteWorkspaceMembers(workspaceId, MEMBER_PAGE_SIZE, open);\n\n const {\n data: projectMembersPages,\n isLoading: isLoadingProjectMembers,\n isError: isErrorProjectMembers,\n error: projectMembersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId, workspaceId, MEMBER_PAGE_SIZE, open);\n\n useEffect(() => {\n if (\n !open ||\n !hasMoreProjectMembers ||\n isFetchingNextProjectMembersPage ||\n isErrorProjectMembers ||\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES\n ) {\n return;\n }\n\n void fetchNextProjectMembersPage();\n }, [\n fetchNextProjectMembersPage,\n hasMoreProjectMembers,\n isErrorProjectMembers,\n isFetchingNextProjectMembersPage,\n open,\n projectMembersPages?.pages.length,\n ]);\n\n const workspaceMembers = useMemo(\n () => workspaceMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [workspaceMembersPages?.pages]\n );\n const existingUserIdSet = useMemo(\n () =>\n new Set(\n projectMembersPages?.pages.flatMap((page) => page.items.map((member) => member.userId)) ??\n []\n ),\n [projectMembersPages?.pages]\n );\n const totalWorkspaceMembers = workspaceMembersPages?.pages[0]?.total ?? workspaceMembers.length;\n const reachedProjectMemberAutoloadCap =\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES;\n const isAutoloadingProjectMembers =\n !isErrorProjectMembers && hasMoreProjectMembers && !reachedProjectMemberAutoloadCap;\n\n const actors = useMemo<ActorInput[]>(\n () => workspaceMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [workspaceMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const eligibleMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n\n return workspaceMembers.filter((member) => {\n if (existingUserIdSet.has(member.userId)) {\n return false;\n }\n\n if (!needle) {\n return true;\n }\n\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n const fallback = getFallbackDisplay(member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [existingUserIdSet, profileMap, searchValue, workspaceMembers]);\n\n const handleAddMember = (userId: string) => {\n void nativeImpact('medium');\n setAddingUserId(userId);\n setError(null);\n\n addProjectMember(\n { projectId, userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setAddingUserId(null);\n onOpenChange(false);\n },\n onError: (mutationError) => {\n void nativeNotify('error');\n setAddingUserId(null);\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : 'Failed to add member to project'\n );\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(85vh,760px)] flex-col overflow-hidden sm:max-w-[720px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add member to project</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add an existing workspace member to {projectName || 'this project'}. Only current\n workspace members are eligible.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 overflow-hidden\">\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 shrink-0\" />\n <span>{error}</span>\n </div>\n ) : null}\n\n {projectMembersError instanceof 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 shrink-0\" />\n <span>{projectMembersError.message}</span>\n </div>\n ) : null}\n\n {workspaceMembersError instanceof Error ? (\n <div className=\"space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <div className=\"flex items-center gap-2\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{workspaceMembersError.message}</span>\n </div>\n <Button variant=\"outline\" onClick={() => void refetchWorkspaceMembers()}>\n Retry Loading Members\n </Button>\n </div>\n ) : null}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder=\"Search workspace members\"\n className=\"pl-9\"\n disabled={isAdding}\n />\n </div>\n <Badge variant=\"secondary\">\n {hasMoreWorkspaceMembers ? `${eligibleMembers.length}+` : eligibleMembers.length}{' '}\n eligible\n </Badge>\n </div>\n\n <div className=\"max-h-[480px] space-y-3 overflow-y-auto pr-1\">\n {isErrorWorkspaceMembers ? null : isLoadingMembers ||\n isLoadingProjectMembers ||\n isAutoloadingProjectMembers ||\n isFetchingNextProjectMembersPage ? (\n Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-bg-surface p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))\n ) : projectMembersError instanceof Error ? null : eligibleMembers.length === 0 ? (\n <div className=\"rounded-lg border bg-bg-surface p-8 text-center\">\n <UserPlus className=\"mx-auto mb-3 size-10 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold\">No eligible members found</h3>\n <p className=\"mt-2 text-sm text-text-secondary\">\n All workspace members are already part of this project, or nothing matched your\n search.\n </p>\n {reachedProjectMemberAutoloadCap ? (\n <p className=\"mt-3 text-xs text-text-secondary\">\n Loaded the first {MAX_AUTOLOADED_PROJECT_MEMBER_PAGES * MEMBER_PAGE_SIZE}{' '}\n project members to avoid an excessively long blocking load.\n </p>\n ) : null}\n {hasMoreWorkspaceMembers ? (\n <div className=\"mt-4\">\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <>\n {eligibleMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getFallbackDisplay(member);\n const isSubmitting = isAdding && addingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-text-secondary\">\n {fallback.email}\n </p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">\n {member.userId}\n </p>\n )}\n </div>\n )}\n </div>\n\n <Button\n size=\"sm\"\n onClick={() => handleAddMember(member.userId)}\n disabled={isAdding}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {isSubmitting ? 'Adding...' : 'Add'}\n </Button>\n </div>\n );\n })}\n\n {hasMoreWorkspaceMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\n Showing {workspaceMembers.length} of {totalWorkspaceMembers} workspace\n members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n </div>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectMemberPickerDialog.js","names":[],"sources":["../../src/components/ProjectMemberPickerDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Search, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useAddProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useInfiniteWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useTr } from '../hooks/useTr';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport type { WorkspaceMember } from '../types';\n\nconst MEMBER_PAGE_SIZE = 100;\nconst MAX_AUTOLOADED_PROJECT_MEMBER_PAGES = 10;\n\nfunction getFallbackDisplay(member: WorkspaceMember): { name: string; email?: string } {\n const meta = member.context?.metadata;\n const name =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : member.userId;\n const email = typeof meta?.email === 'string' ? meta.email : undefined;\n return { name, email };\n}\n\nexport interface ProjectMemberPickerDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n projectId: string;\n projectName?: string;\n}\n\nexport function ProjectMemberPickerDialog({\n open,\n onOpenChange,\n workspaceId,\n projectId,\n projectName,\n}: ProjectMemberPickerDialogProps) {\n const tr = useTr();\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const { mutate: addProjectMember, isPending: isAdding } = useAddProjectMember(workspaceId);\n const [searchValue, setSearchValue] = useState('');\n const [addingUserId, setAddingUserId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const {\n data: workspaceMembersPages,\n isLoading: isLoadingMembers,\n isError: isErrorWorkspaceMembers,\n error: workspaceMembersError,\n hasNextPage: hasMoreWorkspaceMembers,\n fetchNextPage: fetchNextWorkspaceMembersPage,\n isFetchingNextPage: isFetchingNextWorkspaceMembersPage,\n refetch: refetchWorkspaceMembers,\n } = useInfiniteWorkspaceMembers(workspaceId, MEMBER_PAGE_SIZE, open);\n\n const {\n data: projectMembersPages,\n isLoading: isLoadingProjectMembers,\n isError: isErrorProjectMembers,\n error: projectMembersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId, workspaceId, MEMBER_PAGE_SIZE, open);\n\n useEffect(() => {\n if (\n !open ||\n !hasMoreProjectMembers ||\n isFetchingNextProjectMembersPage ||\n isErrorProjectMembers ||\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES\n ) {\n return;\n }\n\n void fetchNextProjectMembersPage();\n }, [\n fetchNextProjectMembersPage,\n hasMoreProjectMembers,\n isErrorProjectMembers,\n isFetchingNextProjectMembersPage,\n open,\n projectMembersPages?.pages.length,\n ]);\n\n const workspaceMembers = useMemo(\n () => workspaceMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [workspaceMembersPages?.pages]\n );\n const existingUserIdSet = useMemo(\n () =>\n new Set(\n projectMembersPages?.pages.flatMap((page) => page.items.map((member) => member.userId)) ??\n []\n ),\n [projectMembersPages?.pages]\n );\n const totalWorkspaceMembers = workspaceMembersPages?.pages[0]?.total ?? workspaceMembers.length;\n const reachedProjectMemberAutoloadCap =\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES;\n const isAutoloadingProjectMembers =\n !isErrorProjectMembers && hasMoreProjectMembers && !reachedProjectMemberAutoloadCap;\n\n const actors = useMemo<ActorInput[]>(\n () => workspaceMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [workspaceMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const eligibleMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n\n return workspaceMembers.filter((member) => {\n if (existingUserIdSet.has(member.userId)) {\n return false;\n }\n\n if (!needle) {\n return true;\n }\n\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n const fallback = getFallbackDisplay(member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [existingUserIdSet, profileMap, searchValue, workspaceMembers]);\n\n const handleAddMember = (userId: string) => {\n void nativeImpact('medium');\n setAddingUserId(userId);\n setError(null);\n\n addProjectMember(\n { projectId, userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setAddingUserId(null);\n onOpenChange(false);\n },\n onError: (mutationError) => {\n void nativeNotify('error');\n setAddingUserId(null);\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : tr('projectMemberPickerDialog.addError', 'Failed to add member to project')\n );\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(85vh,760px)] flex-col overflow-hidden sm:max-w-[720px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('projectMemberPickerDialog.title', 'Add member to project')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'projectMemberPickerDialog.description',\n 'Add an existing workspace member to {{projectName}}. Only current workspace members are eligible.',\n {\n projectName:\n projectName ?? tr('projectMemberPickerDialog.thisProject', 'this project'),\n }\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 overflow-hidden\">\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 shrink-0\" />\n <span>{error}</span>\n </div>\n ) : null}\n\n {projectMembersError instanceof 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 shrink-0\" />\n <span>{projectMembersError.message}</span>\n </div>\n ) : null}\n\n {workspaceMembersError instanceof Error ? (\n <div className=\"space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <div className=\"flex items-center gap-2\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{workspaceMembersError.message}</span>\n </div>\n <Button variant=\"outline\" onClick={() => void refetchWorkspaceMembers()}>\n {tr('projectMemberPickerDialog.retryLoadingMembers', 'Retry Loading Members')}\n </Button>\n </div>\n ) : null}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder={tr(\n 'projectMemberPickerDialog.searchPlaceholder',\n 'Search workspace members'\n )}\n className=\"pl-9\"\n disabled={isAdding}\n />\n </div>\n <Badge variant=\"secondary\">\n {tr('projectMemberPickerDialog.eligibleCount', '{{count}} eligible', {\n count: hasMoreWorkspaceMembers\n ? `${eligibleMembers.length}+`\n : eligibleMembers.length,\n })}\n </Badge>\n </div>\n\n <div className=\"max-h-[480px] space-y-3 overflow-y-auto pr-1\">\n {isErrorWorkspaceMembers ? null : isLoadingMembers ||\n isLoadingProjectMembers ||\n isAutoloadingProjectMembers ||\n isFetchingNextProjectMembersPage ? (\n Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-bg-surface p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))\n ) : projectMembersError instanceof Error ? null : eligibleMembers.length === 0 ? (\n <div className=\"rounded-lg border bg-bg-surface p-8 text-center\">\n <UserPlus className=\"mx-auto mb-3 size-10 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold\">\n {tr('projectMemberPickerDialog.emptyTitle', 'No eligible members found')}\n </h3>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {tr(\n 'projectMemberPickerDialog.emptyDescription',\n 'All workspace members are already part of this project, or nothing matched your search.'\n )}\n </p>\n {reachedProjectMemberAutoloadCap ? (\n <p className=\"mt-3 text-xs text-text-secondary\">\n {tr(\n 'projectMemberPickerDialog.autoloadCapNote',\n 'Loaded the first {{count}} project members to avoid an excessively long blocking load.',\n { count: MAX_AUTOLOADED_PROJECT_MEMBER_PAGES * MEMBER_PAGE_SIZE }\n )}\n </p>\n ) : null}\n {hasMoreWorkspaceMembers ? (\n <div className=\"mt-4\">\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage\n ? tr('projectMemberPickerDialog.loadingMore', 'Loading more…')\n : tr('projectMemberPickerDialog.loadMore', 'Load More Members')}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <>\n {eligibleMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getFallbackDisplay(member);\n const isSubmitting = isAdding && addingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-text-secondary\">\n {fallback.email}\n </p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">\n {member.userId}\n </p>\n )}\n </div>\n )}\n </div>\n\n <Button\n size=\"sm\"\n onClick={() => handleAddMember(member.userId)}\n disabled={isAdding}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {isSubmitting\n ? tr('projectMemberPickerDialog.adding', 'Adding...')\n : tr('projectMemberPickerDialog.add', 'Add')}\n </Button>\n </div>\n );\n })}\n\n {hasMoreWorkspaceMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'projectMemberPickerDialog.showingCount',\n 'Showing {{shown}} of {{total}} workspace members.',\n {\n shown: workspaceMembers.length,\n total: totalWorkspaceMembers,\n }\n )}\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage\n ? tr('projectMemberPickerDialog.loadingMore', 'Loading more…')\n : tr('projectMemberPickerDialog.loadMore', 'Load More Members')}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n </div>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -1,73 +1,76 @@
1
- import { useUpdateProject as e } from "../hooks/useProjectMutations.js";
2
- import { useState as t } from "react";
3
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
- import { Button as i, ResponsiveDialog as a, ResponsiveDialogContent as o, ResponsiveDialogDescription as s, ResponsiveDialogHeader as c, ResponsiveDialogTitle as l } from "@burdenoff/fe-libs/ui";
5
- import { FormControl as u, FormDescription as d, FormField as f, FormItem as p, FormLabel as m, FormMessage as h, FormProvider as g, Input as _, useForm as v, z as y } from "@burdenoff/fe-libs/form";
1
+ import { useTr as e } from "../hooks/useTr.js";
2
+ import { useUpdateProject as t } from "../hooks/useProjectMutations.js";
3
+ import { useMemo as n, useState as r } from "react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { Button as o, ResponsiveDialog as s, ResponsiveDialogContent as c, ResponsiveDialogDescription as l, ResponsiveDialogHeader as u, ResponsiveDialogTitle as d } from "@burdenoff/fe-libs/ui";
6
+ import { FormControl as f, FormDescription as p, FormField as m, FormItem as h, FormLabel as g, FormMessage as _, FormProvider as v, Input as y, useForm as b, z as x } from "@burdenoff/fe-libs/form";
6
7
  //#region src/components/RenameProjectDialog.tsx
7
- var b = y.object({ name: y.string().trim().min(2, "Name must be at least 2 characters").max(100, "Name must be less than 100 characters") });
8
- function x({ project: y, open: x, onOpenChange: S }) {
9
- let { mutate: C, isPending: w } = e(y.workspaceId), [T, E] = t(null), D = v({
10
- schema: b,
11
- defaultValues: { name: y.name }
12
- }), O = D.watch("name").trim() === y.name.trim(), k = (e) => {
8
+ function S(e) {
9
+ return x.object({ name: x.string().trim().min(2, e("renameProjectDialog.validation.nameMin", "Name must be at least 2 characters")).max(100, e("renameProjectDialog.validation.nameMax", "Name must be less than 100 characters")) });
10
+ }
11
+ function C({ project: x, open: C, onOpenChange: w }) {
12
+ let T = e(), { mutate: E, isPending: D } = t(x.workspaceId), [O, k] = r(null), A = b({
13
+ schema: n(() => S(T), [T]),
14
+ defaultValues: { name: x.name }
15
+ }), j = A.watch("name").trim() === x.name.trim(), M = (e) => {
13
16
  let t = e.name.trim();
14
- if (t === y.name.trim()) {
15
- S(!1);
17
+ if (t === x.name.trim()) {
18
+ w(!1);
16
19
  return;
17
20
  }
18
- E(null), C({
19
- id: y.id,
21
+ k(null), E({
22
+ id: x.id,
20
23
  input: { name: t }
21
24
  }, {
22
25
  onSuccess: () => {
23
- S(!1);
26
+ w(!1);
24
27
  },
25
28
  onError: (e) => {
26
- E(e instanceof Error ? e.message : "Failed to rename project");
29
+ k(e instanceof Error ? e.message : T("renameProjectDialog.submitError", "Failed to rename project"));
27
30
  }
28
31
  });
29
32
  };
30
- return /* @__PURE__ */ n(a, {
31
- open: x,
32
- onOpenChange: S,
33
- children: /* @__PURE__ */ r(o, {
33
+ return /* @__PURE__ */ i(s, {
34
+ open: C,
35
+ onOpenChange: w,
36
+ children: /* @__PURE__ */ a(c, {
34
37
  className: "sm:max-w-[500px]",
35
- children: [/* @__PURE__ */ r(c, { children: [/* @__PURE__ */ n(l, { children: "Rename Project" }), /* @__PURE__ */ n(s, { children: "Give this project a new name. The project key stays the same." })] }), /* @__PURE__ */ n(g, {
36
- ...D,
37
- children: /* @__PURE__ */ r("form", {
38
- onSubmit: D.handleSubmit(k),
38
+ children: [/* @__PURE__ */ a(u, { children: [/* @__PURE__ */ i(d, { children: T("renameProjectDialog.title", "Rename Project") }), /* @__PURE__ */ i(l, { children: T("renameProjectDialog.description", "Give this project a new name. The project key stays the same.") })] }), /* @__PURE__ */ i(v, {
39
+ ...A,
40
+ children: /* @__PURE__ */ a("form", {
41
+ onSubmit: A.handleSubmit(M),
39
42
  className: "space-y-4",
40
43
  children: [
41
- /* @__PURE__ */ n(f, {
42
- control: D.control,
44
+ /* @__PURE__ */ i(m, {
45
+ control: A.control,
43
46
  name: "name",
44
- render: ({ field: e }) => /* @__PURE__ */ r(p, { children: [
45
- /* @__PURE__ */ n(m, { children: "Project Name" }),
46
- /* @__PURE__ */ n(u, { children: /* @__PURE__ */ n(_, {
47
+ render: ({ field: e }) => /* @__PURE__ */ a(h, { children: [
48
+ /* @__PURE__ */ i(g, { children: T("renameProjectDialog.nameLabel", "Project Name") }),
49
+ /* @__PURE__ */ i(f, { children: /* @__PURE__ */ i(y, {
47
50
  ...e,
48
- placeholder: "My Project"
51
+ placeholder: T("renameProjectDialog.namePlaceholder", "My Project")
49
52
  }) }),
50
- /* @__PURE__ */ n(d, { children: "A descriptive name for your project" }),
51
- /* @__PURE__ */ n(h, {})
53
+ /* @__PURE__ */ i(p, { children: T("renameProjectDialog.nameHint", "A descriptive name for your project") }),
54
+ /* @__PURE__ */ i(_, {})
52
55
  ] })
53
56
  }),
54
- T && /* @__PURE__ */ n("p", {
57
+ O && /* @__PURE__ */ i("p", {
55
58
  className: "text-sm text-status-error-text",
56
59
  role: "alert",
57
- children: T
60
+ children: O
58
61
  }),
59
- /* @__PURE__ */ r("div", {
62
+ /* @__PURE__ */ a("div", {
60
63
  className: "flex justify-end gap-3 pt-4",
61
- children: [/* @__PURE__ */ n(i, {
64
+ children: [/* @__PURE__ */ i(o, {
62
65
  type: "button",
63
66
  variant: "outline",
64
- onClick: () => S(!1),
65
- disabled: w,
66
- children: "Cancel"
67
- }), /* @__PURE__ */ n(i, {
67
+ onClick: () => w(!1),
68
+ disabled: D,
69
+ children: T("common.cancel", "Cancel")
70
+ }), /* @__PURE__ */ i(o, {
68
71
  type: "submit",
69
- disabled: w || O,
70
- children: w ? "Saving..." : "Save"
72
+ disabled: D || j,
73
+ children: D ? T("renameProjectDialog.saving", "Saving...") : T("renameProjectDialog.save", "Save")
71
74
  })]
72
75
  })
73
76
  ]
@@ -77,6 +80,6 @@ function x({ project: y, open: x, onOpenChange: S }) {
77
80
  });
78
81
  }
79
82
  //#endregion
80
- export { x as RenameProjectDialog };
83
+ export { C as RenameProjectDialog };
81
84
 
82
85
  //# sourceMappingURL=RenameProjectDialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RenameProjectDialog.js","names":[],"sources":["../../src/components/RenameProjectDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\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';\n\nimport { useUpdateProject } from '../hooks/useProjectMutations';\n\nconst renameProjectSchema = z.object({\n // `.trim()` first so length validation runs on the trimmed value — otherwise a\n // padded name like \" a \" passes min(2) but submits as the 1-char \"a\".\n name: z\n .string()\n .trim()\n .min(2, 'Name must be at least 2 characters')\n .max(100, 'Name must be less than 100 characters'),\n});\n\ntype RenameProjectFormData = z.infer<typeof renameProjectSchema>;\n\ninterface RenameProjectDialogProps {\n project: { id: string; name: string; workspaceId: string };\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nexport function RenameProjectDialog({ project, open, onOpenChange }: RenameProjectDialogProps) {\n const { mutate: updateProject, isPending } = useUpdateProject(project.workspaceId);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n // This component is mounted only while open (the parent gates it with\n // `{open && <RenameProjectDialog .../>}`), so `defaultValues` always reflects\n // the current name on each open — no prop-syncing effect needed.\n const form = useForm({\n schema: renameProjectSchema,\n defaultValues: { name: project.name },\n });\n\n const currentName = form.watch('name');\n const isUnchanged = currentName.trim() === project.name.trim();\n\n const onSubmit = (data: RenameProjectFormData) => {\n const trimmed = data.name.trim();\n if (trimmed === project.name.trim()) {\n onOpenChange(false);\n return;\n }\n\n setSubmitError(null);\n updateProject(\n { id: project.id, input: { name: trimmed } },\n {\n onSuccess: () => {\n onOpenChange(false);\n },\n onError: (error) => {\n setSubmitError(error instanceof Error ? error.message : 'Failed to rename project');\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Rename Project</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Give this project a new name. The project key stays the same.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"name\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Project Name</FormLabel>\n <FormControl>\n <Input {...field} placeholder=\"My Project\" />\n </FormControl>\n <FormDescription>A descriptive name for your project</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {submitError && (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {submitError}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isPending || isUnchanged}>\n {isPending ? 'Saving...' : 'Save'}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;AAwBA,IAAM,IAAsB,EAAE,OAAO,EAGnC,MAAM,EACH,QAAQ,CACR,MAAM,CACN,IAAI,GAAG,qCAAqC,CAC5C,IAAI,KAAK,wCAAwC,EACrD,CAAC;AAUF,SAAgB,EAAoB,EAAE,YAAS,SAAM,mBAA0C;CAC7F,IAAM,EAAE,QAAQ,GAAe,iBAAc,EAAiB,EAAQ,YAAY,EAC5E,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAK7D,IAAO,EAAQ;EACnB,QAAQ;EACR,eAAe,EAAE,MAAM,EAAQ,MAAM;EACtC,CAAC,EAGI,IADc,EAAK,MAAM,OAAO,CACN,MAAM,KAAK,EAAQ,KAAK,MAAM,EAExD,KAAY,MAAgC;EAChD,IAAM,IAAU,EAAK,KAAK,MAAM;AAChC,MAAI,MAAY,EAAQ,KAAK,MAAM,EAAE;AACnC,KAAa,GAAM;AACnB;;AAIF,EADA,EAAe,KAAK,EACpB,EACE;GAAE,IAAI,EAAQ;GAAI,OAAO,EAAE,MAAM,GAAS;GAAE,EAC5C;GACE,iBAAiB;AACf,MAAa,GAAM;;GAErB,UAAU,MAAU;AAClB,MAAe,aAAiB,QAAQ,EAAM,UAAU,2BAA2B;;GAEtF,CACF;;AAGH,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,kBAAsC,CAAA,EAC7D,kBAAC,GAAD,EAAA,UAA6B,iEAEC,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,GAAD;IAAc,GAAI;cAChB,kBAAC,QAAD;KAAM,UAAU,EAAK,aAAa,EAAS;KAAE,WAAU;eAAvD;MACE,kBAAC,GAAD;OACE,SAAS,EAAK;OACd,MAAK;OACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;QACE,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA;QACnC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SAAO,GAAI;SAAO,aAAY;SAAe,CAAA,EACjC,CAAA;QACd,kBAAC,GAAD,EAAA,UAAiB,uCAAqD,CAAA;QACtE,kBAAC,GAAD,EAAe,CAAA;QACN,EAAA,CAAA;OAEb,CAAA;MAED,KACC,kBAAC,KAAD;OAAG,WAAU;OAAiC,MAAK;iBAChD;OACC,CAAA;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,eAAe,EAAa,GAAM;QAClC,UAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU,KAAa;kBAC1C,IAAY,cAAc;QACpB,CAAA,CACL;;MACD;;IACM,CAAA,CACS;;EACT,CAAA"}
1
+ {"version":3,"file":"RenameProjectDialog.js","names":[],"sources":["../../src/components/RenameProjectDialog.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\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';\n\nimport { useUpdateProject } from '../hooks/useProjectMutations';\nimport { useTr } from '../hooks/useTr';\n\nfunction renameProjectSchema(tr: ReturnType<typeof useTr>) {\n return z.object({\n // `.trim()` first so length validation runs on the trimmed value — otherwise a\n // padded name like \" a \" passes min(2) but submits as the 1-char \"a\".\n name: z\n .string()\n .trim()\n .min(2, tr('renameProjectDialog.validation.nameMin', 'Name must be at least 2 characters'))\n .max(\n 100,\n tr('renameProjectDialog.validation.nameMax', 'Name must be less than 100 characters')\n ),\n });\n}\n\ntype RenameProjectFormData = z.infer<ReturnType<typeof renameProjectSchema>>;\n\ninterface RenameProjectDialogProps {\n project: { id: string; name: string; workspaceId: string };\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nexport function RenameProjectDialog({ project, open, onOpenChange }: RenameProjectDialogProps) {\n const tr = useTr();\n const { mutate: updateProject, isPending } = useUpdateProject(project.workspaceId);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n const schema = useMemo(() => renameProjectSchema(tr), [tr]);\n\n // This component is mounted only while open (the parent gates it with\n // `{open && <RenameProjectDialog .../>}`), so `defaultValues` always reflects\n // the current name on each open — no prop-syncing effect needed.\n const form = useForm({\n schema,\n defaultValues: { name: project.name },\n });\n\n const currentName = form.watch('name');\n const isUnchanged = currentName.trim() === project.name.trim();\n\n const onSubmit = (data: RenameProjectFormData) => {\n const trimmed = data.name.trim();\n if (trimmed === project.name.trim()) {\n onOpenChange(false);\n return;\n }\n\n setSubmitError(null);\n updateProject(\n { id: project.id, input: { name: trimmed } },\n {\n onSuccess: () => {\n onOpenChange(false);\n },\n onError: (error) => {\n setSubmitError(\n error instanceof Error\n ? error.message\n : tr('renameProjectDialog.submitError', 'Failed to rename project')\n );\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('renameProjectDialog.title', 'Rename Project')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'renameProjectDialog.description',\n 'Give this project a new name. The project key stays the same.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"name\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{tr('renameProjectDialog.nameLabel', 'Project Name')}</FormLabel>\n <FormControl>\n <Input\n {...field}\n placeholder={tr('renameProjectDialog.namePlaceholder', 'My Project')}\n />\n </FormControl>\n <FormDescription>\n {tr('renameProjectDialog.nameHint', 'A descriptive name for your project')}\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {submitError && (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {submitError}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={isPending}\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isPending || isUnchanged}>\n {isPending\n ? tr('renameProjectDialog.saving', 'Saving...')\n : tr('renameProjectDialog.save', 'Save')}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;AAyBA,SAAS,EAAoB,GAA8B;AACzD,QAAO,EAAE,OAAO,EAGd,MAAM,EACH,QAAQ,CACR,MAAM,CACN,IAAI,GAAG,EAAG,0CAA0C,qCAAqC,CAAC,CAC1F,IACC,KACA,EAAG,0CAA0C,wCAAwC,CACtF,EACJ,CAAC;;AAWJ,SAAgB,EAAoB,EAAE,YAAS,SAAM,mBAA0C;CAC7F,IAAM,IAAK,GAAO,EACZ,EAAE,QAAQ,GAAe,iBAAc,EAAiB,EAAQ,YAAY,EAC5E,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAO7D,IAAO,EAAQ;EACnB,QANa,QAAc,EAAoB,EAAG,EAAE,CAAC,EAAG,CAAC;EAOzD,eAAe,EAAE,MAAM,EAAQ,MAAM;EACtC,CAAC,EAGI,IADc,EAAK,MAAM,OAAO,CACN,MAAM,KAAK,EAAQ,KAAK,MAAM,EAExD,KAAY,MAAgC;EAChD,IAAM,IAAU,EAAK,KAAK,MAAM;AAChC,MAAI,MAAY,EAAQ,KAAK,MAAM,EAAE;AACnC,KAAa,GAAM;AACnB;;AAIF,EADA,EAAe,KAAK,EACpB,EACE;GAAE,IAAI,EAAQ;GAAI,OAAO,EAAE,MAAM,GAAS;GAAE,EAC5C;GACE,iBAAiB;AACf,MAAa,GAAM;;GAErB,UAAU,MAAU;AAClB,MACE,aAAiB,QACb,EAAM,UACN,EAAG,mCAAmC,2BAA2B,CACtE;;GAEJ,CACF;;AAGH,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACG,EAAG,6BAA6B,iBAAiB,EAC5B,CAAA,EACxB,kBAAC,GAAD,EAAA,UACG,EACC,mCACA,gEACD,EAC2B,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,GAAD;IAAc,GAAI;cAChB,kBAAC,QAAD;KAAM,UAAU,EAAK,aAAa,EAAS;KAAE,WAAU;eAAvD;MACE,kBAAC,GAAD;OACE,SAAS,EAAK;OACd,MAAK;OACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;QACE,kBAAC,GAAD,EAAA,UAAY,EAAG,iCAAiC,eAAe,EAAa,CAAA;QAC5E,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SACE,GAAI;SACJ,aAAa,EAAG,uCAAuC,aAAa;SACpE,CAAA,EACU,CAAA;QACd,kBAAC,GAAD,EAAA,UACG,EAAG,gCAAgC,sCAAsC,EAC1D,CAAA;QAClB,kBAAC,GAAD,EAAe,CAAA;QACN,EAAA,CAAA;OAEb,CAAA;MAED,KACC,kBAAC,KAAD;OAAG,WAAU;OAAiC,MAAK;iBAChD;OACC,CAAA;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,eAAe,EAAa,GAAM;QAClC,UAAU;kBAET,EAAG,iBAAiB,SAAS;QACvB,CAAA,EACT,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU,KAAa;kBAC1C,IACG,EAAG,8BAA8B,YAAY,GAC7C,EAAG,4BAA4B,OAAO;QACnC,CAAA,CACL;;MACD;;IACM,CAAA,CACS;;EACT,CAAA"}