@burdenoff/microfe-store 2026.614.1 → 2026.623.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.
@@ -1,17 +1,17 @@
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, User as u, X as d } from "lucide-react";
4
- import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
5
- import { graphqlFetch as h } from "@burdenoff/fe-libs/shared/graphql";
3
+ import { AlertCircle as a, Building2 as o, CheckCircle as s, Info as c, Loader2 as l, X as u } from "lucide-react";
4
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
5
+ import { graphqlFetch as m } from "@burdenoff/fe-libs/shared/graphql";
6
6
  //#region src/components/InstallAppModal.tsx
7
- var g = "query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }", _ = "mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }", v = "mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }", y = "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 } }", b = "mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }", x = new Set([
7
+ var h = "query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }", g = "mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }", _ = "mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }", v = "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 } }", y = "mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }", b = new Set([
8
8
  "VIBEMODULE",
9
9
  "NODE",
10
10
  "CONSOLE",
11
11
  "DASHBOARD",
12
12
  "PARSER",
13
13
  "DATASINK"
14
- ]), S = {
14
+ ]), x = {
15
15
  ALREADY_INSTALLED: "This app is already installed in the selected workspace.",
16
16
  QUOTA_EXCEEDED: "Installation limit reached. Please upgrade your plan or contact support.",
17
17
  PERMISSION_DENIED: "You do not have permission to install apps in this workspace.",
@@ -20,19 +20,19 @@ var g = "query MyWorkspaces { myWorkspaces { items { id name slug type status or
20
20
  ENTITLEMENT_EXPIRED: "Your license for this app has expired.",
21
21
  UNSUPPORTED_TYPE: "This product type cannot be installed in the selected workspace."
22
22
  };
23
- function C(e) {
24
- for (let [t, n] of Object.entries(S)) if (e.toUpperCase().includes(t)) return n;
23
+ function S(e) {
24
+ for (let [t, n] of Object.entries(x)) if (e.toUpperCase().includes(t)) return n;
25
25
  return "Installation failed. Please try again or contact support.";
26
26
  }
27
- var w = {
27
+ var C = {
28
28
  selectedWorkspace: null,
29
29
  installing: !1,
30
30
  installError: null,
31
31
  installInfo: null
32
32
  };
33
- function T(e, t) {
33
+ function w(e, t) {
34
34
  switch (t.type) {
35
- case "reset": return w;
35
+ case "reset": return C;
36
36
  case "selectWorkspace": return {
37
37
  ...e,
38
38
  selectedWorkspace: t.workspaceId
@@ -58,14 +58,14 @@ function T(e, t) {
58
58
  default: return e;
59
59
  }
60
60
  }
61
- function E(r) {
61
+ function T(r) {
62
62
  let { authToken: a } = e(), [o, s] = i([]), [c, l] = i(!1), [u, d] = i(null), f = t(async () => {
63
63
  if (!r) {
64
64
  l(!0), d(null);
65
65
  try {
66
- let e = await h({
66
+ let e = await m({
67
67
  gateway: "global",
68
- query: g,
68
+ query: h,
69
69
  authToken: a || void 0
70
70
  });
71
71
  if (e.errors?.length) throw Error(e.errors[0]?.message ?? "GraphQL error");
@@ -86,19 +86,19 @@ function E(r) {
86
86
  refetch: f
87
87
  };
88
88
  }
89
- var D = ({ isOpen: t, onClose: n, app: i, orderId: g, purchaseState: S = "free", onInstallSuccess: D, onInstallError: O }) => {
90
- let { authToken: k, user: A } = e(), [j, M] = r(T, w), { selectedWorkspace: N, installing: P, installError: F, installInfo: I } = j, { workspaces: L, loading: R, error: z } = E(!t), B = L.filter((e) => e.status === "ACTIVE"), V = async () => {
91
- if (!N) return;
92
- let e = B.find((e) => e.id === N);
89
+ var E = ({ isOpen: t, onClose: n, app: i, orderId: h, purchaseState: x = "free", onInstallSuccess: E, onInstallError: D }) => {
90
+ let { authToken: O, user: k } = e(), [A, j] = r(w, C), { selectedWorkspace: M, installing: N, installError: P, installInfo: F } = A, { workspaces: I, loading: L, error: R } = T(!t), z = I.filter((e) => e.status === "ACTIVE"), B = async () => {
91
+ if (!M) return;
92
+ let e = z.find((e) => e.id === M);
93
93
  if (!e) return;
94
- let t = A?.id;
95
- M({ type: "startInstall" });
94
+ let t = k?.id;
95
+ j({ type: "startInstall" });
96
96
  try {
97
- if (!g && S !== "purchased") {
97
+ if (!h && x !== "purchased") {
98
98
  if (!t) throw Error("Unable to identify the current user for license creation");
99
- await h({
99
+ await m({
100
100
  gateway: "global",
101
- query: b,
101
+ query: y,
102
102
  variables: { input: {
103
103
  productId: i.id,
104
104
  purchasedBy: t,
@@ -108,50 +108,50 @@ var D = ({ isOpen: t, onClose: n, app: i, orderId: g, purchaseState: S = "free",
108
108
  productType: i.type
109
109
  }
110
110
  } },
111
- authToken: k || void 0
111
+ authToken: O || void 0
112
112
  });
113
113
  }
114
- let r = await h({
114
+ let r = await m({
115
115
  gateway: "global",
116
- query: y,
116
+ query: v,
117
117
  variables: {
118
- orderId: g ?? null,
118
+ orderId: h ?? null,
119
119
  productId: i.id,
120
120
  workspaceId: e.id
121
121
  },
122
- authToken: k || void 0
122
+ authToken: O || void 0
123
123
  });
124
124
  if (r.errors?.length) throw Error(r.errors[0]?.message ?? "Installation failed");
125
125
  let a = r.data?.installPurchasedApp;
126
- if (a?.success) a.alreadyInstalled ? M({
126
+ if (a?.success) a.alreadyInstalled ? j({
127
127
  type: "setInfo",
128
128
  info: a.message || "Already installed on this workspace"
129
- }) : (D?.(e.id, e.name), n());
129
+ }) : (E?.(e.id, e.name), n());
130
130
  else {
131
131
  let e = a?.error || "Installation failed";
132
- M({
132
+ j({
133
133
  type: "setError",
134
- error: C(e)
135
- }), O?.(Error(e));
134
+ error: S(e)
135
+ }), D?.(Error(e));
136
136
  }
137
137
  } catch (e) {
138
138
  let t = e instanceof Error ? e.message : "Installation failed";
139
- M({
139
+ j({
140
140
  type: "setError",
141
- error: C(t)
142
- }), O?.(Error(t));
141
+ error: S(t)
142
+ }), D?.(Error(t));
143
143
  } finally {
144
- M({ type: "finishInstall" });
144
+ j({ type: "finishInstall" });
145
145
  }
146
- }, H = async () => {
147
- if (!N) return;
148
- let e = B.find((e) => e.id === N);
146
+ }, V = async () => {
147
+ if (!M) return;
148
+ let e = z.find((e) => e.id === M);
149
149
  if (e) {
150
- M({ type: "startInstall" });
150
+ j({ type: "startInstall" });
151
151
  try {
152
- let t = await h({
152
+ let t = await m({
153
153
  gateway: "workspace",
154
- query: _,
154
+ query: g,
155
155
  variables: {
156
156
  workspaceIds: [e.id],
157
157
  input: {
@@ -165,15 +165,17 @@ var D = ({ isOpen: t, onClose: n, app: i, orderId: g, purchaseState: S = "free",
165
165
  }
166
166
  }
167
167
  },
168
- authToken: k || void 0
168
+ authToken: O || void 0,
169
+ workspaceId: e.id,
170
+ workspaceToken: !0
169
171
  });
170
172
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Installation failed");
171
173
  let r = t.data?.installWorkspaceApps;
172
174
  if (r?.successCount && r.successCount > 0) {
173
175
  try {
174
- await h({
176
+ await m({
175
177
  gateway: "global",
176
- query: v,
178
+ query: _,
177
179
  variables: { input: {
178
180
  productId: i.id,
179
181
  workspaceId: e.id,
@@ -188,155 +190,155 @@ var D = ({ isOpen: t, onClose: n, app: i, orderId: g, purchaseState: S = "free",
188
190
  type: i.type
189
191
  }
190
192
  } },
191
- authToken: k || void 0
193
+ authToken: O || void 0
192
194
  });
193
195
  } catch {}
194
- D?.(e.id, e.name), n();
196
+ E?.(e.id, e.name), n();
195
197
  } else {
196
198
  let e = r?.results?.[0]?.error || "Installation failed";
197
- M({
199
+ j({
198
200
  type: "setError",
199
- error: C(e)
200
- }), O?.(Error(e));
201
+ error: S(e)
202
+ }), D?.(Error(e));
201
203
  }
202
204
  } catch (e) {
203
205
  let t = e instanceof Error ? e.message : "Installation failed";
204
- M({
206
+ j({
205
207
  type: "setError",
206
- error: C(t)
207
- }), O?.(Error(t));
208
+ error: S(t)
209
+ }), D?.(Error(t));
208
210
  } finally {
209
- M({ type: "finishInstall" });
211
+ j({ type: "finishInstall" });
210
212
  }
211
213
  }
212
214
  };
213
- return t ? /* @__PURE__ */ m("div", {
215
+ return t ? /* @__PURE__ */ p("div", {
214
216
  className: "fixed inset-0 z-50 flex items-center justify-center",
215
- children: [/* @__PURE__ */ p("button", {
217
+ children: [/* @__PURE__ */ f("button", {
216
218
  type: "button",
217
219
  "aria-label": "Close modal",
218
220
  className: "absolute inset-0 bg-bg-overlay/50 backdrop-blur-sm",
219
221
  onClick: n
220
- }), /* @__PURE__ */ m("dialog", {
222
+ }), /* @__PURE__ */ p("dialog", {
221
223
  open: !0,
222
224
  "aria-modal": "true",
223
225
  "aria-labelledby": "install-app-modal-title",
224
226
  className: "relative z-10 mx-4 w-full max-w-lg rounded-2xl border border-border-default bg-bg-surface shadow-2xl",
225
227
  children: [
226
- /* @__PURE__ */ m("div", {
228
+ /* @__PURE__ */ p("div", {
227
229
  className: "flex items-center justify-between border-b border-border-default px-6 py-4",
228
- children: [/* @__PURE__ */ m("div", {
230
+ children: [/* @__PURE__ */ p("div", {
229
231
  className: "flex items-center gap-3",
230
- children: [i.icon ? /* @__PURE__ */ p("img", {
232
+ children: [i.icon ? /* @__PURE__ */ f("img", {
231
233
  src: i.icon,
232
234
  alt: i.name,
233
235
  className: "size-10 rounded-lg object-cover"
234
- }) : /* @__PURE__ */ p("div", {
236
+ }) : /* @__PURE__ */ f("div", {
235
237
  className: "flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted",
236
238
  children: i.name.charAt(0)
237
- }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h2", {
239
+ }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h2", {
238
240
  id: "install-app-modal-title",
239
241
  className: "text-lg font-semibold text-text-primary",
240
242
  children: "Install App"
241
- }), /* @__PURE__ */ p("p", {
243
+ }), /* @__PURE__ */ f("p", {
242
244
  className: "text-sm text-text-muted",
243
245
  children: i.name
244
246
  })] })]
245
- }), /* @__PURE__ */ p("button", {
247
+ }), /* @__PURE__ */ f("button", {
246
248
  type: "button",
247
249
  onClick: n,
248
250
  "aria-label": "Close install dialog",
249
251
  className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
250
- children: /* @__PURE__ */ p(d, { className: "size-5" })
252
+ children: /* @__PURE__ */ f(u, { className: "size-5" })
251
253
  })]
252
254
  }),
253
- /* @__PURE__ */ m("div", {
255
+ /* @__PURE__ */ p("div", {
254
256
  className: "px-6 py-4",
255
- children: [/* @__PURE__ */ p("p", {
257
+ children: [/* @__PURE__ */ f("p", {
256
258
  className: "mb-4 text-text-secondary",
257
259
  children: "Select the workspace where you want to install this app:"
258
- }), /* @__PURE__ */ p("div", {
260
+ }), /* @__PURE__ */ f("div", {
259
261
  className: "max-h-72 overflow-y-auto rounded-lg border border-border-default",
260
- children: R ? /* @__PURE__ */ p("div", {
262
+ children: L ? /* @__PURE__ */ f("div", {
261
263
  className: "flex items-center justify-center py-12",
262
- children: /* @__PURE__ */ p(l, { className: "size-8 animate-spin text-text-muted" })
263
- }) : z ? /* @__PURE__ */ m("div", {
264
+ children: /* @__PURE__ */ f(l, { className: "size-8 animate-spin text-text-muted" })
265
+ }) : R ? /* @__PURE__ */ p("div", {
264
266
  className: "flex flex-col items-center justify-center py-12 text-center",
265
267
  children: [
266
- /* @__PURE__ */ p(a, { className: "mb-2 size-8 text-status-error-text" }),
267
- /* @__PURE__ */ p("p", {
268
+ /* @__PURE__ */ f(a, { className: "mb-2 size-8 text-status-error-text" }),
269
+ /* @__PURE__ */ f("p", {
268
270
  className: "text-sm text-status-error-text",
269
271
  children: "Failed to load workspaces"
270
272
  }),
271
- /* @__PURE__ */ p("p", {
273
+ /* @__PURE__ */ f("p", {
272
274
  className: "text-xs text-text-muted mt-1",
273
- children: z.message
275
+ children: R.message
274
276
  })
275
277
  ]
276
- }) : B.length === 0 ? /* @__PURE__ */ m("div", {
278
+ }) : z.length === 0 ? /* @__PURE__ */ p("div", {
277
279
  className: "flex flex-col items-center justify-center py-12 text-center",
278
- children: [/* @__PURE__ */ p(o, { className: "mb-2 size-8 text-text-muted" }), /* @__PURE__ */ p("p", {
280
+ children: [/* @__PURE__ */ f(o, { className: "mb-2 size-8 text-text-muted" }), /* @__PURE__ */ f("p", {
279
281
  className: "text-sm text-text-muted",
280
282
  children: "No workspaces available"
281
283
  })]
282
- }) : /* @__PURE__ */ p("div", {
284
+ }) : /* @__PURE__ */ f("div", {
283
285
  className: "divide-y divide-border-default",
284
- children: B.map((e) => /* @__PURE__ */ m("button", {
286
+ children: z.map((e) => /* @__PURE__ */ p("button", {
285
287
  type: "button",
286
- onClick: () => M({
288
+ onClick: () => j({
287
289
  type: "selectWorkspace",
288
290
  workspaceId: e.id
289
291
  }),
290
- className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${N === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
292
+ className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${M === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
291
293
  children: [
292
- /* @__PURE__ */ p("div", {
293
- className: `flex size-10 items-center justify-center rounded-lg ${e.type === "user" ? "bg-status-info-bg-subtle text-status-info-text" : "bg-action-primary-bg/10 text-text-link"}`,
294
- children: e.type === "user" ? /* @__PURE__ */ p(u, { className: "size-5" }) : /* @__PURE__ */ p(o, { className: "size-5" })
294
+ /* @__PURE__ */ f("div", {
295
+ className: "flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link",
296
+ children: /* @__PURE__ */ f(o, { className: "size-5" })
295
297
  }),
296
- /* @__PURE__ */ m("div", {
298
+ /* @__PURE__ */ p("div", {
297
299
  className: "flex-1 min-w-0",
298
- children: [/* @__PURE__ */ p("p", {
300
+ children: [/* @__PURE__ */ f("p", {
299
301
  className: "font-medium text-text-primary truncate",
300
302
  children: e.name
301
- }), /* @__PURE__ */ m("p", {
302
- className: "text-xs text-text-muted",
303
- children: [e.type === "user" ? "Personal" : "Team", " workspace"]
304
- })]
303
+ }), e.slug ? /* @__PURE__ */ p("p", {
304
+ className: "text-xs text-text-muted truncate",
305
+ children: ["/", e.slug]
306
+ }) : null]
305
307
  }),
306
- N === e.id && /* @__PURE__ */ p(s, { className: "size-5 flex-shrink-0 text-action-primary-bg" })
308
+ M === e.id && /* @__PURE__ */ f(s, { className: "size-5 flex-shrink-0 text-action-primary-bg" })
307
309
  ]
308
310
  }, e.id))
309
311
  })
310
312
  })]
311
313
  }),
312
- /* @__PURE__ */ m("div", {
314
+ /* @__PURE__ */ p("div", {
313
315
  className: "border-t border-border-default px-6 py-4",
314
316
  children: [
315
- I && /* @__PURE__ */ m("div", {
317
+ F && /* @__PURE__ */ p("div", {
316
318
  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",
317
- children: [/* @__PURE__ */ p(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: I })]
319
+ children: [/* @__PURE__ */ f(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: F })]
318
320
  }),
319
- F && /* @__PURE__ */ m("div", {
321
+ P && /* @__PURE__ */ p("div", {
320
322
  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",
321
- children: [/* @__PURE__ */ p(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: F })]
323
+ children: [/* @__PURE__ */ f(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: P })]
322
324
  }),
323
- /* @__PURE__ */ m("div", {
325
+ /* @__PURE__ */ p("div", {
324
326
  className: "flex items-center justify-end gap-3",
325
- children: [/* @__PURE__ */ p("button", {
327
+ children: [/* @__PURE__ */ f("button", {
326
328
  type: "button",
327
329
  onClick: n,
328
- disabled: P,
330
+ disabled: N,
329
331
  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",
330
332
  children: "Cancel"
331
- }), /* @__PURE__ */ p("button", {
333
+ }), /* @__PURE__ */ f("button", {
332
334
  type: "button",
333
335
  onClick: async () => {
334
336
  let e = i.type?.toUpperCase() ?? "";
335
- S === "purchased" || g || x.has(e) ? await V() : await H();
337
+ x === "purchased" || h || b.has(e) ? await B() : await V();
336
338
  },
337
- disabled: !N || P,
339
+ disabled: !M || N,
338
340
  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",
339
- children: P ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(l, { className: "size-4 animate-spin" }), "Installing…"] }) : "Install App"
341
+ children: N ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(l, { className: "size-4 animate-spin" }), "Installing…"] }) : "Install App"
340
342
  })]
341
343
  })
342
344
  ]
@@ -346,6 +348,6 @@ var D = ({ isOpen: t, onClose: n, app: i, orderId: g, purchaseState: S = "free",
346
348
  }) : null;
347
349
  };
348
350
  //#endregion
349
- export { D as InstallAppModal };
351
+ export { E as InstallAppModal };
350
352
 
351
353
  //# 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 { X, Loader2, Building2, User, CheckCircle, AlertCircle, Info } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\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 });\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 if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close modal\"\n className=\"absolute inset-0 bg-bg-overlay/50 backdrop-blur-sm\"\n onClick={onClose}\n />\n\n {/* Modal */}\n <dialog\n open\n aria-modal=\"true\"\n aria-labelledby=\"install-app-modal-title\"\n className=\"relative z-10 mx-4 w-full max-w-lg rounded-2xl border border-border-default bg-bg-surface shadow-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-border-default px-6 py-4\">\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 <h2 id=\"install-app-modal-title\" className=\"text-lg font-semibold text-text-primary\">\n Install App\n </h2>\n <p className=\"text-sm text-text-muted\">{app.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close install dialog\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\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-default\">\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\n className={`flex size-10 items-center justify-center rounded-lg ${\n workspace.type === 'user'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-action-primary-bg/10 text-text-link'\n }`}\n >\n {workspace.type === 'user' ? (\n <User className=\"size-5\" />\n ) : (\n <Building2 className=\"size-5\" />\n )}\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 <p className=\"text-xs text-text-muted\">\n {workspace.type === 'user' ? 'Personal' : 'Team'} workspace\n </p>\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 <div className=\"border-t border-border-default px-6 py-4\">\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 </div>\n </dialog>\n </div>\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 { X, Loader2, Building2, CheckCircle, AlertCircle, Info } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\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 if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close modal\"\n className=\"absolute inset-0 bg-bg-overlay/50 backdrop-blur-sm\"\n onClick={onClose}\n />\n\n {/* Modal */}\n <dialog\n open\n aria-modal=\"true\"\n aria-labelledby=\"install-app-modal-title\"\n className=\"relative z-10 mx-4 w-full max-w-lg rounded-2xl border border-border-default bg-bg-surface shadow-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-border-default px-6 py-4\">\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 <h2 id=\"install-app-modal-title\" className=\"text-lg font-semibold text-text-primary\">\n Install App\n </h2>\n <p className=\"text-sm text-text-muted\">{app.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close install dialog\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\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-default\">\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 <div className=\"border-t border-border-default px-6 py-4\">\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 </div>\n </dialog>\n </div>\n );\n};\n"],"mappings":"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"}