@burdenoff/microfe-store 2026.626.2 → 2026.703.2

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.
@@ -1,18 +1,18 @@
1
1
  import { useStore as e } from "../providers/StoreProvider.js";
2
2
  import { useCallback as t, useEffect as n, useReducer as r, useState as i } from "react";
3
- import { AlertCircle as a, Building2 as o, CheckCircle as s, Info as c, Loader2 as l } from "lucide-react";
4
- import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
5
- import { graphqlFetch as p } from "@burdenoff/fe-libs/shared/graphql";
6
- import { Dialog as m, DialogContent as h, DialogDescription as g, DialogFooter as _, DialogHeader as v, DialogTitle as y } from "@burdenoff/fe-libs/ui";
3
+ import { AlertCircle as a, Building2 as o, CheckCircle as s, Info as c, Loader2 as l, ShieldCheck as u } from "lucide-react";
4
+ import { graphqlFetch as d } from "@burdenoff/fe-libs/shared/graphql";
5
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
6
+ import { Dialog as h, DialogContent as g, DialogDescription as _, DialogFooter as v, DialogHeader as y, DialogTitle as b } from "@burdenoff/fe-libs/ui";
7
7
  //#region src/components/InstallAppModal.tsx
8
- var b = "query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }", x = "mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }", S = "mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }", C = "mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId) { success alreadyInstalled installation { id productId workspaceId status version } error message } }", w = "mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }", T = new Set([
8
+ var x = "query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }", S = "mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }", C = "mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }", w = "mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId) { success alreadyInstalled installation { id productId workspaceId status version } error message } }", T = "mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }", E = new Set([
9
9
  "VIBEMODULE",
10
10
  "NODE",
11
11
  "CONSOLE",
12
12
  "DASHBOARD",
13
13
  "PARSER",
14
14
  "DATASINK"
15
- ]), E = {
15
+ ]), D = {
16
16
  ALREADY_INSTALLED: "This app is already installed in the selected workspace.",
17
17
  QUOTA_EXCEEDED: "Installation limit reached. Please upgrade your plan or contact support.",
18
18
  PERMISSION_DENIED: "You do not have permission to install apps in this workspace.",
@@ -21,19 +21,19 @@ var b = "query MyWorkspaces { myWorkspaces { items { id name slug type status or
21
21
  ENTITLEMENT_EXPIRED: "Your license for this app has expired.",
22
22
  UNSUPPORTED_TYPE: "This product type cannot be installed in the selected workspace."
23
23
  };
24
- function D(e) {
25
- for (let [t, n] of Object.entries(E)) if (e.toUpperCase().includes(t)) return n;
24
+ function O(e) {
25
+ for (let [t, n] of Object.entries(D)) if (e.toUpperCase().includes(t)) return n;
26
26
  return "Installation failed. Please try again or contact support.";
27
27
  }
28
- var O = {
28
+ var k = {
29
29
  selectedWorkspace: null,
30
30
  installing: !1,
31
31
  installError: null,
32
32
  installInfo: null
33
33
  };
34
- function k(e, t) {
34
+ function A(e, t) {
35
35
  switch (t.type) {
36
- case "reset": return O;
36
+ case "reset": return k;
37
37
  case "selectWorkspace": return {
38
38
  ...e,
39
39
  selectedWorkspace: t.workspaceId
@@ -59,47 +59,47 @@ function k(e, t) {
59
59
  default: return e;
60
60
  }
61
61
  }
62
- function A(r) {
63
- let { authToken: a } = e(), [o, s] = i([]), [c, l] = i(!1), [u, d] = i(null), f = t(async () => {
62
+ function j(r) {
63
+ let { authToken: a } = e(), [o, s] = i([]), [c, l] = i(!1), [u, f] = i(null), p = t(async () => {
64
64
  if (!r) {
65
- l(!0), d(null);
65
+ l(!0), f(null);
66
66
  try {
67
- let e = await p({
67
+ let e = await d({
68
68
  gateway: "global",
69
- query: b,
69
+ query: x,
70
70
  authToken: a || void 0
71
71
  });
72
72
  if (e.errors?.length) throw Error(e.errors[0]?.message ?? "GraphQL error");
73
73
  s(e.data?.myWorkspaces?.items || []);
74
74
  } catch (e) {
75
- d(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch workspaces"));
75
+ f(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch workspaces"));
76
76
  } finally {
77
77
  l(!1);
78
78
  }
79
79
  }
80
80
  }, [a, r]);
81
81
  return n(() => {
82
- f();
83
- }, [f]), {
82
+ p();
83
+ }, [p]), {
84
84
  workspaces: o,
85
85
  loading: c,
86
86
  error: u,
87
- refetch: f
87
+ refetch: p
88
88
  };
89
89
  }
90
- var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free", onInstallSuccess: j, onInstallError: M }) => {
91
- let { authToken: N, user: P } = e(), [F, I] = r(k, O), { selectedWorkspace: L, installing: R, installError: z, installInfo: B } = F, { workspaces: V, loading: H, error: U } = A(!t), W = V.filter((e) => e.status === "ACTIVE"), G = async () => {
92
- if (!L) return;
93
- let e = W.find((e) => e.id === L);
90
+ var M = ({ isOpen: t, onClose: n, app: i, orderId: x, purchaseState: D = "free", onInstallSuccess: M, onInstallError: N }) => {
91
+ let { authToken: P, user: F } = e(), [I, L] = r(A, k), { selectedWorkspace: R, installing: z, installError: B, installInfo: V } = I, { workspaces: H, loading: U, error: W } = j(!t), G = H.filter((e) => e.status === "ACTIVE"), K = async () => {
92
+ if (!R) return;
93
+ let e = G.find((e) => e.id === R);
94
94
  if (!e) return;
95
- let t = P?.id;
96
- I({ type: "startInstall" });
95
+ let t = F?.id;
96
+ L({ type: "startInstall" });
97
97
  try {
98
- if (!b && E !== "purchased") {
98
+ if (!x && D !== "purchased") {
99
99
  if (!t) throw Error("Unable to identify the current user for license creation");
100
- await p({
100
+ await d({
101
101
  gateway: "global",
102
- query: w,
102
+ query: T,
103
103
  variables: { input: {
104
104
  productId: i.id,
105
105
  purchasedBy: t,
@@ -109,50 +109,50 @@ var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free",
109
109
  productType: i.type
110
110
  }
111
111
  } },
112
- authToken: N || void 0
112
+ authToken: P || void 0
113
113
  });
114
114
  }
115
- let r = await p({
115
+ let r = await d({
116
116
  gateway: "global",
117
- query: C,
117
+ query: w,
118
118
  variables: {
119
- orderId: b ?? null,
119
+ orderId: x ?? null,
120
120
  productId: i.id,
121
121
  workspaceId: e.id
122
122
  },
123
- authToken: N || void 0
123
+ authToken: P || void 0
124
124
  });
125
125
  if (r.errors?.length) throw Error(r.errors[0]?.message ?? "Installation failed");
126
126
  let a = r.data?.installPurchasedApp;
127
- if (a?.success) a.alreadyInstalled ? I({
127
+ if (a?.success) a.alreadyInstalled ? L({
128
128
  type: "setInfo",
129
129
  info: a.message || "Already installed on this workspace"
130
- }) : (j?.(e.id, e.name), n());
130
+ }) : (M?.(e.id, e.name), n());
131
131
  else {
132
132
  let e = a?.error || "Installation failed";
133
- I({
133
+ L({
134
134
  type: "setError",
135
- error: D(e)
136
- }), M?.(Error(e));
135
+ error: O(e)
136
+ }), N?.(Error(e));
137
137
  }
138
138
  } catch (e) {
139
139
  let t = e instanceof Error ? e.message : "Installation failed";
140
- I({
140
+ L({
141
141
  type: "setError",
142
- error: D(t)
143
- }), M?.(Error(t));
142
+ error: O(t)
143
+ }), N?.(Error(t));
144
144
  } finally {
145
- I({ type: "finishInstall" });
145
+ L({ type: "finishInstall" });
146
146
  }
147
- }, K = async () => {
148
- if (!L) return;
149
- let e = W.find((e) => e.id === L);
147
+ }, q = async () => {
148
+ if (!R) return;
149
+ let e = G.find((e) => e.id === R);
150
150
  if (e) {
151
- I({ type: "startInstall" });
151
+ L({ type: "startInstall" });
152
152
  try {
153
- let t = await p({
153
+ let t = await d({
154
154
  gateway: "workspace",
155
- query: x,
155
+ query: S,
156
156
  variables: {
157
157
  workspaceIds: [e.id],
158
158
  input: {
@@ -166,7 +166,7 @@ var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free",
166
166
  }
167
167
  }
168
168
  },
169
- authToken: N || void 0,
169
+ authToken: P || void 0,
170
170
  workspaceId: e.id,
171
171
  workspaceToken: !0
172
172
  });
@@ -174,9 +174,9 @@ var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free",
174
174
  let r = t.data?.installWorkspaceApps;
175
175
  if (r?.successCount && r.successCount > 0) {
176
176
  try {
177
- await p({
177
+ await d({
178
178
  gateway: "global",
179
- query: S,
179
+ query: C,
180
180
  variables: { input: {
181
181
  productId: i.id,
182
182
  workspaceId: e.id,
@@ -191,141 +191,167 @@ var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free",
191
191
  type: i.type
192
192
  }
193
193
  } },
194
- authToken: N || void 0
194
+ authToken: P || void 0
195
195
  });
196
196
  } catch {}
197
- j?.(e.id, e.name), n();
197
+ M?.(e.id, e.name), n();
198
198
  } else {
199
199
  let e = r?.results?.[0]?.error || "Installation failed";
200
- I({
200
+ L({
201
201
  type: "setError",
202
- error: D(e)
203
- }), M?.(Error(e));
202
+ error: O(e)
203
+ }), N?.(Error(e));
204
204
  }
205
205
  } catch (e) {
206
206
  let t = e instanceof Error ? e.message : "Installation failed";
207
- I({
207
+ L({
208
208
  type: "setError",
209
- error: D(t)
210
- }), M?.(Error(t));
209
+ error: O(t)
210
+ }), N?.(Error(t));
211
211
  } finally {
212
- I({ type: "finishInstall" });
212
+ L({ type: "finishInstall" });
213
213
  }
214
214
  }
215
215
  };
216
- return /* @__PURE__ */ d(m, {
216
+ return /* @__PURE__ */ p(h, {
217
217
  open: t,
218
218
  onOpenChange: (e) => !e && n(),
219
- children: /* @__PURE__ */ f(h, {
219
+ children: /* @__PURE__ */ m(g, {
220
220
  className: "mx-4 w-full max-w-lg gap-0 p-0",
221
221
  children: [
222
- /* @__PURE__ */ d(v, {
222
+ /* @__PURE__ */ p(y, {
223
223
  className: "flex flex-row items-center justify-between space-y-0 border-b border-border-default px-6 py-4 text-left",
224
- children: /* @__PURE__ */ f("div", {
224
+ children: /* @__PURE__ */ m("div", {
225
225
  className: "flex items-center gap-3",
226
- children: [i.icon ? /* @__PURE__ */ d("img", {
226
+ children: [i.icon ? /* @__PURE__ */ p("img", {
227
227
  src: i.icon,
228
228
  alt: i.name,
229
229
  className: "size-10 rounded-lg object-cover"
230
- }) : /* @__PURE__ */ d("div", {
230
+ }) : /* @__PURE__ */ p("div", {
231
231
  className: "flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted",
232
232
  children: i.name.charAt(0)
233
- }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d(y, {
233
+ }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p(b, {
234
234
  className: "text-lg font-semibold text-text-primary",
235
235
  children: "Install App"
236
- }), /* @__PURE__ */ d(g, {
236
+ }), /* @__PURE__ */ p(_, {
237
237
  className: "text-sm text-text-muted",
238
238
  children: i.name
239
239
  })] })]
240
240
  })
241
241
  }),
242
- /* @__PURE__ */ f("div", {
242
+ /* @__PURE__ */ m("div", {
243
243
  className: "px-6 py-4",
244
- children: [/* @__PURE__ */ d("p", {
245
- className: "mb-4 text-text-secondary",
246
- children: "Select the workspace where you want to install this app:"
247
- }), /* @__PURE__ */ d("div", {
248
- className: "max-h-72 overflow-y-auto rounded-lg border border-border-seam",
249
- children: H ? /* @__PURE__ */ d("div", {
250
- className: "flex items-center justify-center py-12",
251
- children: /* @__PURE__ */ d(l, { className: "size-8 animate-spin text-text-muted" })
252
- }) : U ? /* @__PURE__ */ f("div", {
253
- className: "flex flex-col items-center justify-center py-12 text-center",
244
+ children: [
245
+ i.permissions && i.permissions.length > 0 && /* @__PURE__ */ m("div", {
246
+ className: "mb-5 rounded-lg border border-border-seam bg-bg-surface px-4 py-3",
254
247
  children: [
255
- /* @__PURE__ */ d(a, { className: "mb-2 size-8 text-status-error-text" }),
256
- /* @__PURE__ */ d("p", {
257
- className: "text-sm text-status-error-text",
258
- children: "Failed to load workspaces"
248
+ /* @__PURE__ */ m("div", {
249
+ className: "mb-2 flex items-center gap-2",
250
+ children: [/* @__PURE__ */ p(u, { className: "size-4 text-status-success-text" }), /* @__PURE__ */ p("p", {
251
+ className: "text-sm font-medium text-text-primary",
252
+ children: "Permissions requested"
253
+ })]
259
254
  }),
260
- /* @__PURE__ */ d("p", {
261
- className: "text-xs text-text-muted mt-1",
262
- children: U.message
255
+ /* @__PURE__ */ p("p", {
256
+ className: "mb-3 text-xs text-text-secondary",
257
+ children: "By installing, you grant this app access to the following in the selected workspace:"
258
+ }),
259
+ /* @__PURE__ */ p("ul", {
260
+ className: "space-y-1.5",
261
+ children: i.permissions.map((e) => /* @__PURE__ */ m("li", {
262
+ className: "flex items-center gap-2 text-sm text-text-primary",
263
+ children: [/* @__PURE__ */ p("span", { className: "size-1.5 rounded-full bg-action-primary-bg flex-shrink-0" }), e]
264
+ }, e))
263
265
  })
264
266
  ]
265
- }) : W.length === 0 ? /* @__PURE__ */ f("div", {
266
- className: "flex flex-col items-center justify-center py-12 text-center",
267
- children: [/* @__PURE__ */ d(o, { className: "mb-2 size-8 text-text-muted" }), /* @__PURE__ */ d("p", {
268
- className: "text-sm text-text-muted",
269
- children: "No workspaces available"
270
- })]
271
- }) : /* @__PURE__ */ d("div", {
272
- className: "divide-y divide-border-default",
273
- children: W.map((e) => /* @__PURE__ */ f("button", {
274
- type: "button",
275
- onClick: () => I({
276
- type: "selectWorkspace",
277
- workspaceId: e.id
278
- }),
279
- className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${L === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
267
+ }),
268
+ /* @__PURE__ */ p("p", {
269
+ className: "mb-4 text-text-secondary",
270
+ children: "Select the workspace where you want to install this app:"
271
+ }),
272
+ /* @__PURE__ */ p("div", {
273
+ className: "max-h-72 overflow-y-auto rounded-lg border border-border-seam",
274
+ children: U ? /* @__PURE__ */ p("div", {
275
+ className: "flex items-center justify-center py-12",
276
+ children: /* @__PURE__ */ p(l, { className: "size-8 animate-spin text-text-muted" })
277
+ }) : W ? /* @__PURE__ */ m("div", {
278
+ className: "flex flex-col items-center justify-center py-12 text-center",
280
279
  children: [
281
- /* @__PURE__ */ d("div", {
282
- className: "flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link",
283
- children: /* @__PURE__ */ d(o, { className: "size-5" })
280
+ /* @__PURE__ */ p(a, { className: "mb-2 size-8 text-status-error-text" }),
281
+ /* @__PURE__ */ p("p", {
282
+ className: "text-sm text-status-error-text",
283
+ children: "Failed to load workspaces"
284
284
  }),
285
- /* @__PURE__ */ f("div", {
286
- className: "flex-1 min-w-0",
287
- children: [/* @__PURE__ */ d("p", {
288
- className: "font-medium text-text-primary truncate",
289
- children: e.name
290
- }), e.slug ? /* @__PURE__ */ f("p", {
291
- className: "text-xs text-text-muted truncate",
292
- children: ["/", e.slug]
293
- }) : null]
294
- }),
295
- L === e.id && /* @__PURE__ */ d(s, { className: "size-5 flex-shrink-0 text-action-primary-bg" })
285
+ /* @__PURE__ */ p("p", {
286
+ className: "text-xs text-text-muted mt-1",
287
+ children: W.message
288
+ })
296
289
  ]
297
- }, e.id))
290
+ }) : G.length === 0 ? /* @__PURE__ */ m("div", {
291
+ className: "flex flex-col items-center justify-center py-12 text-center",
292
+ children: [/* @__PURE__ */ p(o, { className: "mb-2 size-8 text-text-muted" }), /* @__PURE__ */ p("p", {
293
+ className: "text-sm text-text-muted",
294
+ children: "No workspaces available"
295
+ })]
296
+ }) : /* @__PURE__ */ p("div", {
297
+ className: "divide-y divide-border-default",
298
+ children: G.map((e) => /* @__PURE__ */ m("button", {
299
+ type: "button",
300
+ onClick: () => L({
301
+ type: "selectWorkspace",
302
+ workspaceId: e.id
303
+ }),
304
+ className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${R === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
305
+ children: [
306
+ /* @__PURE__ */ p("div", {
307
+ className: "flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link",
308
+ children: /* @__PURE__ */ p(o, { className: "size-5" })
309
+ }),
310
+ /* @__PURE__ */ m("div", {
311
+ className: "flex-1 min-w-0",
312
+ children: [/* @__PURE__ */ p("p", {
313
+ className: "font-medium text-text-primary truncate",
314
+ children: e.name
315
+ }), e.slug ? /* @__PURE__ */ m("p", {
316
+ className: "text-xs text-text-muted truncate",
317
+ children: ["/", e.slug]
318
+ }) : null]
319
+ }),
320
+ R === e.id && /* @__PURE__ */ p(s, { className: "size-5 flex-shrink-0 text-action-primary-bg" })
321
+ ]
322
+ }, e.id))
323
+ })
298
324
  })
299
- })]
325
+ ]
300
326
  }),
301
- /* @__PURE__ */ f(_, {
327
+ /* @__PURE__ */ m(v, {
302
328
  className: "block border-t border-border-default px-6 py-4 sm:flex-col sm:items-stretch sm:space-x-0",
303
329
  children: [
304
- B && /* @__PURE__ */ f("div", {
330
+ V && /* @__PURE__ */ m("div", {
305
331
  className: "mb-3 flex items-center gap-2 rounded-lg bg-status-info-bg-subtle px-3 py-2 text-sm text-status-info-text",
306
- children: [/* @__PURE__ */ d(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ d("span", { children: B })]
332
+ children: [/* @__PURE__ */ p(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: V })]
307
333
  }),
308
- z && /* @__PURE__ */ f("div", {
334
+ B && /* @__PURE__ */ m("div", {
309
335
  className: "mb-3 flex items-center gap-2 rounded-lg bg-status-error-bg-subtle/10 px-3 py-2 text-sm text-status-error-text",
310
- children: [/* @__PURE__ */ d(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ d("span", { children: z })]
336
+ children: [/* @__PURE__ */ p(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: B })]
311
337
  }),
312
- /* @__PURE__ */ f("div", {
338
+ /* @__PURE__ */ m("div", {
313
339
  className: "flex items-center justify-end gap-3",
314
- children: [/* @__PURE__ */ d("button", {
340
+ children: [/* @__PURE__ */ p("button", {
315
341
  type: "button",
316
342
  onClick: n,
317
- disabled: R,
343
+ disabled: z,
318
344
  className: "cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50",
319
345
  children: "Cancel"
320
- }), /* @__PURE__ */ d("button", {
346
+ }), /* @__PURE__ */ p("button", {
321
347
  type: "button",
322
348
  onClick: async () => {
323
349
  let e = i.type?.toUpperCase() ?? "";
324
- E === "purchased" || b || T.has(e) ? await G() : await K();
350
+ D === "purchased" || x || E.has(e) ? await K() : await q();
325
351
  },
326
- disabled: !L || R,
352
+ disabled: !R || z,
327
353
  className: "cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2",
328
- children: R ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(l, { className: "size-4 animate-spin" }), "Installing…"] }) : "Install App"
354
+ children: z ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(l, { className: "size-4 animate-spin" }), "Installing…"] }) : "Install App"
329
355
  })]
330
356
  })
331
357
  ]
@@ -335,6 +361,6 @@ var j = ({ isOpen: t, onClose: n, app: i, orderId: b, purchaseState: E = "free",
335
361
  });
336
362
  };
337
363
  //#endregion
338
- export { j as InstallAppModal };
364
+ export { M as InstallAppModal };
339
365
 
340
366
  //# sourceMappingURL=InstallAppModal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InstallAppModal.js","names":[],"sources":["../../src/components/InstallAppModal.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useCallback, useReducer } from 'react';\nimport { Loader2, Building2, CheckCircle, AlertCircle, Info } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\n\n// Local type definitions for workspace operations\ninterface Workspace {\n id: string;\n name: string;\n slug: string;\n type?: string;\n status?: string;\n organizationId?: string;\n}\n\ninterface WorkspaceList {\n items: Workspace[];\n total: number;\n}\n\ninterface InstallWorkspaceAppsResult {\n successCount: number;\n failedCount: number;\n results: Array<{\n success: boolean;\n workspaceId: string;\n app?: unknown;\n error?: string;\n }>;\n}\n\n// String queries for graphqlFetch\nconst MY_WORKSPACES_STRING = `query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }`;\nconst INSTALL_WORKSPACE_APPS_STRING = `mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }`;\nconst RECORD_APP_INSTALLATION_STRING = `mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }`;\nconst INSTALL_PURCHASED_APP_STRING = `mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId) { success alreadyInstalled installation { id productId workspaceId status version } error message } }`;\nconst CREATE_PRODUCT_ENTITLEMENT_STRING = `mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }`;\nconst STORE_DISTRIBUTED_TYPES = new Set([\n 'VIBEMODULE',\n 'NODE',\n 'CONSOLE',\n 'DASHBOARD',\n 'PARSER',\n 'DATASINK',\n]);\n\nconst INSTALL_ERROR_MESSAGES: Record<string, string> = {\n ALREADY_INSTALLED: 'This app is already installed in the selected workspace.',\n QUOTA_EXCEEDED: 'Installation limit reached. Please upgrade your plan or contact support.',\n PERMISSION_DENIED: 'You do not have permission to install apps in this workspace.',\n NOT_FOUND: 'The app or workspace could not be found.',\n WORKSPACE_INACTIVE: 'The selected workspace is inactive.',\n ENTITLEMENT_EXPIRED: 'Your license for this app has expired.',\n UNSUPPORTED_TYPE: 'This product type cannot be installed in the selected workspace.',\n};\n\nfunction mapInstallError(raw: string): string {\n for (const [code, message] of Object.entries(INSTALL_ERROR_MESSAGES)) {\n if (raw.toUpperCase().includes(code)) return message;\n }\n return 'Installation failed. Please try again or contact support.';\n}\n\ninterface InstallState {\n selectedWorkspace: string | null;\n installing: boolean;\n installError: string | null;\n installInfo: string | null;\n}\n\ntype InstallAction =\n | { type: 'reset' }\n | { type: 'selectWorkspace'; workspaceId: string }\n | { type: 'startInstall' }\n | { type: 'finishInstall' }\n | { type: 'setError'; error: string }\n | { type: 'setInfo'; info: string };\n\nconst initialInstallState: InstallState = {\n selectedWorkspace: null,\n installing: false,\n installError: null,\n installInfo: null,\n};\n\nfunction installReducer(state: InstallState, action: InstallAction): InstallState {\n switch (action.type) {\n case 'reset':\n return initialInstallState;\n case 'selectWorkspace':\n return { ...state, selectedWorkspace: action.workspaceId };\n case 'startInstall':\n return { ...state, installing: true, installError: null, installInfo: null };\n case 'finishInstall':\n return { ...state, installing: false };\n case 'setError':\n return { ...state, installError: action.error };\n case 'setInfo':\n return { ...state, installInfo: action.info };\n default:\n return state;\n }\n}\n\nexport interface AppInfo {\n id: string;\n name: string;\n slug?: string;\n icon?: string;\n type?: string;\n version?: string;\n}\n\ninterface InstallAppModalProps {\n isOpen: boolean;\n onClose: () => void;\n app: AppInfo;\n orderId?: string | null; // For purchased apps from orders page\n purchaseState?: 'free' | 'purchased';\n onInstallSuccess?: (workspaceId: string, workspaceName: string) => void;\n onInstallError?: (error: Error) => void;\n}\n\ninterface UseWorkspacesResult {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// NOTE: Inline Apollo clients removed. All GraphQL calls now use graphqlFetch\n// from fe-libs which automatically resolves gateway URLs and injects auth + locale headers.\n\n/**\n * Hook to fetch user's workspaces using graphqlFetch from fe-libs\n */\nfunction useWorkspaces(skip: boolean): UseWorkspacesResult {\n const { authToken } = useStore();\n const [workspaces, setWorkspaces] = useState<Workspace[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchWorkspaces = useCallback(async () => {\n if (skip) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<{ myWorkspaces: WorkspaceList }>({\n gateway: 'global',\n query: MY_WORKSPACES_STRING,\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'GraphQL error');\n }\n\n setWorkspaces(result.data?.myWorkspaces?.items || []);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch workspaces'));\n } finally {\n setLoading(false);\n }\n }, [authToken, skip]);\n\n useEffect(() => {\n fetchWorkspaces();\n }, [fetchWorkspaces]);\n\n return {\n workspaces,\n loading,\n error,\n refetch: fetchWorkspaces,\n };\n}\n\n/**\n * InstallAppModal component\n *\n * Modal for selecting a workspace to install an app into\n */\nexport const InstallAppModal: FC<InstallAppModalProps> = ({\n isOpen,\n onClose,\n app,\n orderId,\n purchaseState = 'free',\n onInstallSuccess,\n onInstallError,\n}) => {\n const { authToken, user } = useStore();\n const [installState, dispatchInstall] = useReducer(installReducer, initialInstallState);\n const { selectedWorkspace, installing, installError, installInfo } = installState;\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n\n // Filter workspaces by status (only show active)\n const filteredWorkspaces = workspaces.filter((ws) => ws.status === 'ACTIVE');\n\n /**\n * Install purchased app via Store Service (NEW FLOW)\n * Used when installing from orders page or any already-purchased product\n */\n const handleInstallFromStore = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n const purchasedBy = user?.id;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n if (!orderId && purchaseState !== 'purchased') {\n if (!purchasedBy) {\n throw new Error('Unable to identify the current user for license creation');\n }\n\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_PRODUCT_ENTITLEMENT_STRING,\n variables: {\n input: {\n productId: app.id,\n purchasedBy,\n workspaceId: workspace.id,\n metadata: {\n source: 'store-ui',\n productType: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n }\n\n // Call Store Service to install the purchased app\n const result = await graphqlFetch<{\n installPurchasedApp: {\n success: boolean;\n alreadyInstalled?: boolean;\n installation?: {\n id: string;\n productId: string;\n workspaceId: string;\n status: string;\n version: string;\n };\n error?: string;\n message?: string;\n };\n }>({\n gateway: 'global', // Store Service\n query: INSTALL_PURCHASED_APP_STRING,\n variables: {\n orderId: orderId ?? null,\n productId: app.id,\n workspaceId: workspace.id,\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installPurchasedApp;\n\n if (installResult?.success) {\n if (installResult.alreadyInstalled) {\n dispatchInstall({\n type: 'setInfo',\n info: installResult.message || 'Already installed on this workspace',\n });\n } else {\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n }\n } else {\n const rawMsg = installResult?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Install app via Workspace Service (LEGACY FLOW)\n * Used for free apps or when installing without order\n */\n const handleInstallViaWorkspace = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n // Step 1: Install to workspace service via graphqlFetch\n const installVars = {\n workspaceIds: [workspace.id],\n input: {\n appName: app.slug || app.name.toLowerCase().replace(/\\s+/g, '-'),\n displayName: app.name,\n version: app.version || '1.0.0',\n type: app.type || 'app',\n manifest: {\n productId: app.id,\n icon: app.icon,\n },\n },\n };\n\n const result = await graphqlFetch<{\n installWorkspaceApps: InstallWorkspaceAppsResult;\n }>({\n gateway: 'workspace',\n query: INSTALL_WORKSPACE_APPS_STRING,\n variables: installVars,\n authToken: authToken || undefined,\n workspaceId: workspace.id,\n workspaceToken: true,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installWorkspaceApps;\n if (installResult?.successCount && installResult.successCount > 0) {\n // Step 2: Record installation in store service (for tracking)\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal: workspace installation succeeded; store record will sync later.\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } else {\n const rawMsg = installResult?.results?.[0]?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Main install handler - routes to appropriate install function\n */\n const handleInstall = async () => {\n const appType = app.type?.toUpperCase() ?? '';\n\n if (purchaseState === 'purchased' || orderId || STORE_DISTRIBUTED_TYPES.has(appType)) {\n // Purchased products install through the store service.\n await handleInstallFromStore();\n } else {\n // Free products install through the workspace service.\n await handleInstallViaWorkspace();\n }\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"mx-4 w-full max-w-lg gap-0 p-0\">\n {/* Header */}\n <DialogHeader className=\"flex flex-row items-center justify-between space-y-0 border-b border-border-default px-6 py-4 text-left\">\n <div className=\"flex items-center gap-3\">\n {app.icon ? (\n <img src={app.icon} alt={app.name} className=\"size-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted\">\n {app.name.charAt(0)}\n </div>\n )}\n <div>\n <DialogTitle className=\"text-lg font-semibold text-text-primary\">\n Install App\n </DialogTitle>\n <DialogDescription className=\"text-sm text-text-muted\">{app.name}</DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n {/* Content */}\n <div className=\"px-6 py-4\">\n <p className=\"mb-4 text-text-secondary\">\n Select the workspace where you want to install this app:\n </p>\n\n {/* Workspace List */}\n <div className=\"max-h-72 overflow-y-auto rounded-lg border border-border-seam\">\n {loading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 animate-spin text-text-muted\" />\n </div>\n ) : error ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle className=\"mb-2 size-8 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">Failed to load workspaces</p>\n <p className=\"text-xs text-text-muted mt-1\">{error.message}</p>\n </div>\n ) : filteredWorkspaces.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Building2 className=\"mb-2 size-8 text-text-muted\" />\n <p className=\"text-sm text-text-muted\">No workspaces available</p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredWorkspaces.map((workspace) => (\n <button\n type=\"button\"\n key={workspace.id}\n onClick={() =>\n dispatchInstall({ type: 'selectWorkspace', workspaceId: workspace.id })\n }\n className={`w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${\n selectedWorkspace === workspace.id\n ? 'bg-action-primary-bg/10'\n : 'transition-colors hover:bg-bg-sunken'\n }`}\n >\n {/* Workspace Icon */}\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link\">\n <Building2 className=\"size-5\" />\n </div>\n\n {/* Workspace Info */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{workspace.name}</p>\n {workspace.slug ? (\n <p className=\"text-xs text-text-muted truncate\">/{workspace.slug}</p>\n ) : null}\n </div>\n\n {/* Selection Indicator */}\n {selectedWorkspace === workspace.id && (\n <CheckCircle className=\"size-5 flex-shrink-0 text-action-primary-bg\" />\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter className=\"block border-t border-border-default px-6 py-4 sm:flex-col sm:items-stretch sm:space-x-0\">\n {/* Already-installed info */}\n {installInfo && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-info-bg-subtle px-3 py-2 text-sm text-status-info-text\">\n <Info className=\"size-4 flex-shrink-0\" />\n <span>{installInfo}</span>\n </div>\n )}\n {/* Error message */}\n {installError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-error-bg-subtle/10 px-3 py-2 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={installing}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleInstall}\n disabled={!selectedWorkspace || installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Installing…\n </>\n ) : (\n 'Install App'\n )}\n </button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"InstallAppModal.js","names":[],"sources":["../../src/components/InstallAppModal.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useCallback, useReducer } from 'react';\nimport { Loader2, Building2, CheckCircle, AlertCircle, Info, ShieldCheck } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\n\n// Local type definitions for workspace operations\ninterface Workspace {\n id: string;\n name: string;\n slug: string;\n type?: string;\n status?: string;\n organizationId?: string;\n}\n\ninterface WorkspaceList {\n items: Workspace[];\n total: number;\n}\n\ninterface InstallWorkspaceAppsResult {\n successCount: number;\n failedCount: number;\n results: Array<{\n success: boolean;\n workspaceId: string;\n app?: unknown;\n error?: string;\n }>;\n}\n\n// String queries for graphqlFetch\nconst MY_WORKSPACES_STRING = `query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }`;\nconst INSTALL_WORKSPACE_APPS_STRING = `mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }`;\nconst RECORD_APP_INSTALLATION_STRING = `mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }`;\nconst INSTALL_PURCHASED_APP_STRING = `mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId) { success alreadyInstalled installation { id productId workspaceId status version } error message } }`;\nconst CREATE_PRODUCT_ENTITLEMENT_STRING = `mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }`;\nconst STORE_DISTRIBUTED_TYPES = new Set([\n 'VIBEMODULE',\n 'NODE',\n 'CONSOLE',\n 'DASHBOARD',\n 'PARSER',\n 'DATASINK',\n]);\n\nconst INSTALL_ERROR_MESSAGES: Record<string, string> = {\n ALREADY_INSTALLED: 'This app is already installed in the selected workspace.',\n QUOTA_EXCEEDED: 'Installation limit reached. Please upgrade your plan or contact support.',\n PERMISSION_DENIED: 'You do not have permission to install apps in this workspace.',\n NOT_FOUND: 'The app or workspace could not be found.',\n WORKSPACE_INACTIVE: 'The selected workspace is inactive.',\n ENTITLEMENT_EXPIRED: 'Your license for this app has expired.',\n UNSUPPORTED_TYPE: 'This product type cannot be installed in the selected workspace.',\n};\n\nfunction mapInstallError(raw: string): string {\n for (const [code, message] of Object.entries(INSTALL_ERROR_MESSAGES)) {\n if (raw.toUpperCase().includes(code)) return message;\n }\n return 'Installation failed. Please try again or contact support.';\n}\n\ninterface InstallState {\n selectedWorkspace: string | null;\n installing: boolean;\n installError: string | null;\n installInfo: string | null;\n}\n\ntype InstallAction =\n | { type: 'reset' }\n | { type: 'selectWorkspace'; workspaceId: string }\n | { type: 'startInstall' }\n | { type: 'finishInstall' }\n | { type: 'setError'; error: string }\n | { type: 'setInfo'; info: string };\n\nconst initialInstallState: InstallState = {\n selectedWorkspace: null,\n installing: false,\n installError: null,\n installInfo: null,\n};\n\nfunction installReducer(state: InstallState, action: InstallAction): InstallState {\n switch (action.type) {\n case 'reset':\n return initialInstallState;\n case 'selectWorkspace':\n return { ...state, selectedWorkspace: action.workspaceId };\n case 'startInstall':\n return { ...state, installing: true, installError: null, installInfo: null };\n case 'finishInstall':\n return { ...state, installing: false };\n case 'setError':\n return { ...state, installError: action.error };\n case 'setInfo':\n return { ...state, installInfo: action.info };\n default:\n return state;\n }\n}\n\nexport interface AppInfo {\n id: string;\n name: string;\n slug?: string;\n icon?: string;\n type?: string;\n version?: string;\n permissions?: string[];\n}\n\ninterface InstallAppModalProps {\n isOpen: boolean;\n onClose: () => void;\n app: AppInfo;\n orderId?: string | null; // For purchased apps from orders page\n purchaseState?: 'free' | 'purchased';\n onInstallSuccess?: (workspaceId: string, workspaceName: string) => void;\n onInstallError?: (error: Error) => void;\n}\n\ninterface UseWorkspacesResult {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// NOTE: Inline Apollo clients removed. All GraphQL calls now use graphqlFetch\n// from fe-libs which automatically resolves gateway URLs and injects auth + locale headers.\n\n/**\n * Hook to fetch user's workspaces using graphqlFetch from fe-libs\n */\nfunction useWorkspaces(skip: boolean): UseWorkspacesResult {\n const { authToken } = useStore();\n const [workspaces, setWorkspaces] = useState<Workspace[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchWorkspaces = useCallback(async () => {\n if (skip) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<{ myWorkspaces: WorkspaceList }>({\n gateway: 'global',\n query: MY_WORKSPACES_STRING,\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'GraphQL error');\n }\n\n setWorkspaces(result.data?.myWorkspaces?.items || []);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch workspaces'));\n } finally {\n setLoading(false);\n }\n }, [authToken, skip]);\n\n useEffect(() => {\n fetchWorkspaces();\n }, [fetchWorkspaces]);\n\n return {\n workspaces,\n loading,\n error,\n refetch: fetchWorkspaces,\n };\n}\n\n/**\n * InstallAppModal component\n *\n * Modal for selecting a workspace to install an app into\n */\nexport const InstallAppModal: FC<InstallAppModalProps> = ({\n isOpen,\n onClose,\n app,\n orderId,\n purchaseState = 'free',\n onInstallSuccess,\n onInstallError,\n}) => {\n const { authToken, user } = useStore();\n const [installState, dispatchInstall] = useReducer(installReducer, initialInstallState);\n const { selectedWorkspace, installing, installError, installInfo } = installState;\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n\n // Filter workspaces by status (only show active)\n const filteredWorkspaces = workspaces.filter((ws) => ws.status === 'ACTIVE');\n\n /**\n * Install purchased app via Store Service (NEW FLOW)\n * Used when installing from orders page or any already-purchased product\n */\n const handleInstallFromStore = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n const purchasedBy = user?.id;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n if (!orderId && purchaseState !== 'purchased') {\n if (!purchasedBy) {\n throw new Error('Unable to identify the current user for license creation');\n }\n\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_PRODUCT_ENTITLEMENT_STRING,\n variables: {\n input: {\n productId: app.id,\n purchasedBy,\n workspaceId: workspace.id,\n metadata: {\n source: 'store-ui',\n productType: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n }\n\n // Call Store Service to install the purchased app\n const result = await graphqlFetch<{\n installPurchasedApp: {\n success: boolean;\n alreadyInstalled?: boolean;\n installation?: {\n id: string;\n productId: string;\n workspaceId: string;\n status: string;\n version: string;\n };\n error?: string;\n message?: string;\n };\n }>({\n gateway: 'global', // Store Service\n query: INSTALL_PURCHASED_APP_STRING,\n variables: {\n orderId: orderId ?? null,\n productId: app.id,\n workspaceId: workspace.id,\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installPurchasedApp;\n\n if (installResult?.success) {\n if (installResult.alreadyInstalled) {\n dispatchInstall({\n type: 'setInfo',\n info: installResult.message || 'Already installed on this workspace',\n });\n } else {\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n }\n } else {\n const rawMsg = installResult?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Install app via Workspace Service (LEGACY FLOW)\n * Used for free apps or when installing without order\n */\n const handleInstallViaWorkspace = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n // Step 1: Install to workspace service via graphqlFetch\n const installVars = {\n workspaceIds: [workspace.id],\n input: {\n appName: app.slug || app.name.toLowerCase().replace(/\\s+/g, '-'),\n displayName: app.name,\n version: app.version || '1.0.0',\n type: app.type || 'app',\n manifest: {\n productId: app.id,\n icon: app.icon,\n },\n },\n };\n\n const result = await graphqlFetch<{\n installWorkspaceApps: InstallWorkspaceAppsResult;\n }>({\n gateway: 'workspace',\n query: INSTALL_WORKSPACE_APPS_STRING,\n variables: installVars,\n authToken: authToken || undefined,\n workspaceId: workspace.id,\n workspaceToken: true,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installWorkspaceApps;\n if (installResult?.successCount && installResult.successCount > 0) {\n // Step 2: Record installation in store service (for tracking)\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal: workspace installation succeeded; store record will sync later.\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } else {\n const rawMsg = installResult?.results?.[0]?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Main install handler - routes to appropriate install function\n */\n const handleInstall = async () => {\n const appType = app.type?.toUpperCase() ?? '';\n\n if (purchaseState === 'purchased' || orderId || STORE_DISTRIBUTED_TYPES.has(appType)) {\n // Purchased products install through the store service.\n await handleInstallFromStore();\n } else {\n // Free products install through the workspace service.\n await handleInstallViaWorkspace();\n }\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"mx-4 w-full max-w-lg gap-0 p-0\">\n {/* Header */}\n <DialogHeader className=\"flex flex-row items-center justify-between space-y-0 border-b border-border-default px-6 py-4 text-left\">\n <div className=\"flex items-center gap-3\">\n {app.icon ? (\n <img src={app.icon} alt={app.name} className=\"size-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted\">\n {app.name.charAt(0)}\n </div>\n )}\n <div>\n <DialogTitle className=\"text-lg font-semibold text-text-primary\">\n Install App\n </DialogTitle>\n <DialogDescription className=\"text-sm text-text-muted\">{app.name}</DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n {/* Content */}\n <div className=\"px-6 py-4\">\n {/* Permissions consent */}\n {app.permissions && app.permissions.length > 0 && (\n <div className=\"mb-5 rounded-lg border border-border-seam bg-bg-surface px-4 py-3\">\n <div className=\"mb-2 flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" />\n <p className=\"text-sm font-medium text-text-primary\">Permissions requested</p>\n </div>\n <p className=\"mb-3 text-xs text-text-secondary\">\n By installing, you grant this app access to the following in the selected workspace:\n </p>\n <ul className=\"space-y-1.5\">\n {app.permissions.map((perm) => (\n <li key={perm} className=\"flex items-center gap-2 text-sm text-text-primary\">\n <span className=\"size-1.5 rounded-full bg-action-primary-bg flex-shrink-0\" />\n {perm}\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <p className=\"mb-4 text-text-secondary\">\n Select the workspace where you want to install this app:\n </p>\n\n {/* Workspace List */}\n <div className=\"max-h-72 overflow-y-auto rounded-lg border border-border-seam\">\n {loading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 animate-spin text-text-muted\" />\n </div>\n ) : error ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle className=\"mb-2 size-8 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">Failed to load workspaces</p>\n <p className=\"text-xs text-text-muted mt-1\">{error.message}</p>\n </div>\n ) : filteredWorkspaces.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Building2 className=\"mb-2 size-8 text-text-muted\" />\n <p className=\"text-sm text-text-muted\">No workspaces available</p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredWorkspaces.map((workspace) => (\n <button\n type=\"button\"\n key={workspace.id}\n onClick={() =>\n dispatchInstall({ type: 'selectWorkspace', workspaceId: workspace.id })\n }\n className={`w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${\n selectedWorkspace === workspace.id\n ? 'bg-action-primary-bg/10'\n : 'transition-colors hover:bg-bg-sunken'\n }`}\n >\n {/* Workspace Icon */}\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link\">\n <Building2 className=\"size-5\" />\n </div>\n\n {/* Workspace Info */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{workspace.name}</p>\n {workspace.slug ? (\n <p className=\"text-xs text-text-muted truncate\">/{workspace.slug}</p>\n ) : null}\n </div>\n\n {/* Selection Indicator */}\n {selectedWorkspace === workspace.id && (\n <CheckCircle className=\"size-5 flex-shrink-0 text-action-primary-bg\" />\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter className=\"block border-t border-border-default px-6 py-4 sm:flex-col sm:items-stretch sm:space-x-0\">\n {/* Already-installed info */}\n {installInfo && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-info-bg-subtle px-3 py-2 text-sm text-status-info-text\">\n <Info className=\"size-4 flex-shrink-0\" />\n <span>{installInfo}</span>\n </div>\n )}\n {/* Error message */}\n {installError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-error-bg-subtle/10 px-3 py-2 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={installing}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleInstall}\n disabled={!selectedWorkspace || installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Installing…\n </>\n ) : (\n 'Install App'\n )}\n </button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
@@ -837,6 +837,10 @@ var H = t`
837
837
  workspaceName
838
838
  workspaceType
839
839
  installedById
840
+ manifest
841
+ metadata
842
+ context
843
+ grantedPermissions
840
844
  }
841
845
  }
842
846
  }
@@ -1059,6 +1063,7 @@ var K = t`
1059
1063
  manifest
1060
1064
  metadata
1061
1065
  context
1066
+ grantedPermissions
1062
1067
  installedAt
1063
1068
  updatedAt
1064
1069
  uninstalledAt
@@ -1238,6 +1243,7 @@ var Y = t`
1238
1243
  price
1239
1244
  currency
1240
1245
  icon
1246
+ bannerUrl
1241
1247
  screenshots
1242
1248
  videoUrls
1243
1249
  documentationUrl