@burdenoff/microfe-store 2026.703.1 → 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.
- package/dist/components/InstallAppModal.js +153 -127
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/generated/global-operations.js +6 -0
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/hooks/useInstallations.js.map +1 -1
- package/dist/hooks/useStoreGraphQL.js +42 -34
- package/dist/hooks/useStoreGraphQL.js.map +1 -1
- package/dist/hooks/useStoreGraphQLWithContext.js +10 -7
- package/dist/hooks/useStoreGraphQLWithContext.js.map +1 -1
- package/dist/pages/AdminDashboardPage.js +451 -378
- package/dist/pages/AdminDashboardPage.js.map +1 -1
- package/dist/pages/AdminReviewModerationPage.js +182 -150
- package/dist/pages/AdminReviewModerationPage.js.map +1 -1
- package/dist/pages/AdminStoresPage.js.map +1 -1
- package/dist/pages/AdminSubmissionsQueuePage.js +642 -305
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/AppDetailPage.js.map +1 -1
- package/dist/pages/AppSettingsPage.js.map +1 -1
- package/dist/pages/CartPage.js.map +1 -1
- package/dist/pages/MyLicensesPage.js.map +1 -1
- package/dist/pages/OrdersPage.js +5 -5
- package/dist/pages/ProductDetailPage.js +504 -451
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 { graphqlFetch as
|
|
5
|
-
import { Fragment as
|
|
6
|
-
import { Dialog as
|
|
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
|
|
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
|
-
]),
|
|
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
|
|
25
|
-
for (let [t, n] of Object.entries(
|
|
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
|
|
28
|
+
var k = {
|
|
29
29
|
selectedWorkspace: null,
|
|
30
30
|
installing: !1,
|
|
31
31
|
installError: null,
|
|
32
32
|
installInfo: null
|
|
33
33
|
};
|
|
34
|
-
function
|
|
34
|
+
function A(e, t) {
|
|
35
35
|
switch (t.type) {
|
|
36
|
-
case "reset": return
|
|
36
|
+
case "reset": return k;
|
|
37
37
|
case "selectWorkspace": return {
|
|
38
38
|
...e,
|
|
39
39
|
selectedWorkspace: t.workspaceId
|
|
@@ -59,14 +59,14 @@ function k(e, t) {
|
|
|
59
59
|
default: return e;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
let { authToken: a } = e(), [o, s] = i([]), [c, l] = i(!1), [
|
|
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
65
|
l(!0), f(null);
|
|
66
66
|
try {
|
|
67
|
-
let e = await
|
|
67
|
+
let e = await d({
|
|
68
68
|
gateway: "global",
|
|
69
|
-
query:
|
|
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");
|
|
@@ -83,23 +83,23 @@ function A(r) {
|
|
|
83
83
|
}, [p]), {
|
|
84
84
|
workspaces: o,
|
|
85
85
|
loading: c,
|
|
86
|
-
error:
|
|
86
|
+
error: u,
|
|
87
87
|
refetch: p
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
var
|
|
91
|
-
let { authToken:
|
|
92
|
-
if (!
|
|
93
|
-
let e =
|
|
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 =
|
|
96
|
-
|
|
95
|
+
let t = F?.id;
|
|
96
|
+
L({ type: "startInstall" });
|
|
97
97
|
try {
|
|
98
|
-
if (!
|
|
98
|
+
if (!x && D !== "purchased") {
|
|
99
99
|
if (!t) throw Error("Unable to identify the current user for license creation");
|
|
100
|
-
await
|
|
100
|
+
await d({
|
|
101
101
|
gateway: "global",
|
|
102
|
-
query:
|
|
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:
|
|
112
|
+
authToken: P || void 0
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
let r = await
|
|
115
|
+
let r = await d({
|
|
116
116
|
gateway: "global",
|
|
117
|
-
query:
|
|
117
|
+
query: w,
|
|
118
118
|
variables: {
|
|
119
|
-
orderId:
|
|
119
|
+
orderId: x ?? null,
|
|
120
120
|
productId: i.id,
|
|
121
121
|
workspaceId: e.id
|
|
122
122
|
},
|
|
123
|
-
authToken:
|
|
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 ?
|
|
127
|
+
if (a?.success) a.alreadyInstalled ? L({
|
|
128
128
|
type: "setInfo",
|
|
129
129
|
info: a.message || "Already installed on this workspace"
|
|
130
|
-
}) : (
|
|
130
|
+
}) : (M?.(e.id, e.name), n());
|
|
131
131
|
else {
|
|
132
132
|
let e = a?.error || "Installation failed";
|
|
133
|
-
|
|
133
|
+
L({
|
|
134
134
|
type: "setError",
|
|
135
|
-
error:
|
|
136
|
-
}),
|
|
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
|
-
|
|
140
|
+
L({
|
|
141
141
|
type: "setError",
|
|
142
|
-
error:
|
|
143
|
-
}),
|
|
142
|
+
error: O(t)
|
|
143
|
+
}), N?.(Error(t));
|
|
144
144
|
} finally {
|
|
145
|
-
|
|
145
|
+
L({ type: "finishInstall" });
|
|
146
146
|
}
|
|
147
|
-
},
|
|
148
|
-
if (!
|
|
149
|
-
let e =
|
|
147
|
+
}, q = async () => {
|
|
148
|
+
if (!R) return;
|
|
149
|
+
let e = G.find((e) => e.id === R);
|
|
150
150
|
if (e) {
|
|
151
|
-
|
|
151
|
+
L({ type: "startInstall" });
|
|
152
152
|
try {
|
|
153
|
-
let t = await
|
|
153
|
+
let t = await d({
|
|
154
154
|
gateway: "workspace",
|
|
155
|
-
query:
|
|
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:
|
|
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
|
|
177
|
+
await d({
|
|
178
178
|
gateway: "global",
|
|
179
|
-
query:
|
|
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:
|
|
194
|
+
authToken: P || void 0
|
|
195
195
|
});
|
|
196
196
|
} catch {}
|
|
197
|
-
|
|
197
|
+
M?.(e.id, e.name), n();
|
|
198
198
|
} else {
|
|
199
199
|
let e = r?.results?.[0]?.error || "Installation failed";
|
|
200
|
-
|
|
200
|
+
L({
|
|
201
201
|
type: "setError",
|
|
202
|
-
error:
|
|
203
|
-
}),
|
|
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
|
-
|
|
207
|
+
L({
|
|
208
208
|
type: "setError",
|
|
209
|
-
error:
|
|
210
|
-
}),
|
|
209
|
+
error: O(t)
|
|
210
|
+
}), N?.(Error(t));
|
|
211
211
|
} finally {
|
|
212
|
-
|
|
212
|
+
L({ type: "finishInstall" });
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
};
|
|
216
|
-
return /* @__PURE__ */
|
|
216
|
+
return /* @__PURE__ */ p(h, {
|
|
217
217
|
open: t,
|
|
218
218
|
onOpenChange: (e) => !e && n(),
|
|
219
|
-
children: /* @__PURE__ */
|
|
219
|
+
children: /* @__PURE__ */ m(g, {
|
|
220
220
|
className: "mx-4 w-full max-w-lg gap-0 p-0",
|
|
221
221
|
children: [
|
|
222
|
-
/* @__PURE__ */
|
|
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__ */
|
|
224
|
+
children: /* @__PURE__ */ m("div", {
|
|
225
225
|
className: "flex items-center gap-3",
|
|
226
|
-
children: [i.icon ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
236
|
+
}), /* @__PURE__ */ p(_, {
|
|
237
237
|
className: "text-sm text-text-muted",
|
|
238
238
|
children: i.name
|
|
239
239
|
})] })]
|
|
240
240
|
})
|
|
241
241
|
}),
|
|
242
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ m("div", {
|
|
243
243
|
className: "px-6 py-4",
|
|
244
|
-
children: [
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}), /* @__PURE__ */ f("div", {
|
|
248
|
-
className: "max-h-72 overflow-y-auto rounded-lg border border-border-seam",
|
|
249
|
-
children: H ? /* @__PURE__ */ f("div", {
|
|
250
|
-
className: "flex items-center justify-center py-12",
|
|
251
|
-
children: /* @__PURE__ */ f(l, { className: "size-8 animate-spin text-text-muted" })
|
|
252
|
-
}) : U ? /* @__PURE__ */ p("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__ */
|
|
256
|
-
|
|
257
|
-
className: "
|
|
258
|
-
|
|
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
|
+
})]
|
|
254
|
+
}),
|
|
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:"
|
|
259
258
|
}),
|
|
260
|
-
/* @__PURE__ */
|
|
261
|
-
className: "
|
|
262
|
-
children:
|
|
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
|
-
})
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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__ */
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
/* @__PURE__ */ p("div", {
|
|
286
|
-
className: "flex-1 min-w-0",
|
|
287
|
-
children: [/* @__PURE__ */ f("p", {
|
|
288
|
-
className: "font-medium text-text-primary truncate",
|
|
289
|
-
children: e.name
|
|
290
|
-
}), e.slug ? /* @__PURE__ */ p("p", {
|
|
291
|
-
className: "text-xs text-text-muted truncate",
|
|
292
|
-
children: ["/", e.slug]
|
|
293
|
-
}) : null]
|
|
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"
|
|
294
284
|
}),
|
|
295
|
-
|
|
285
|
+
/* @__PURE__ */ p("p", {
|
|
286
|
+
className: "text-xs text-text-muted mt-1",
|
|
287
|
+
children: W.message
|
|
288
|
+
})
|
|
296
289
|
]
|
|
297
|
-
},
|
|
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__ */
|
|
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
|
-
|
|
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__ */
|
|
332
|
+
children: [/* @__PURE__ */ p(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: V })]
|
|
307
333
|
}),
|
|
308
|
-
|
|
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__ */
|
|
336
|
+
children: [/* @__PURE__ */ p(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("span", { children: B })]
|
|
311
337
|
}),
|
|
312
|
-
/* @__PURE__ */
|
|
338
|
+
/* @__PURE__ */ m("div", {
|
|
313
339
|
className: "flex items-center justify-end gap-3",
|
|
314
|
-
children: [/* @__PURE__ */
|
|
340
|
+
children: [/* @__PURE__ */ p("button", {
|
|
315
341
|
type: "button",
|
|
316
342
|
onClick: n,
|
|
317
|
-
disabled:
|
|
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__ */
|
|
346
|
+
}), /* @__PURE__ */ p("button", {
|
|
321
347
|
type: "button",
|
|
322
348
|
onClick: async () => {
|
|
323
349
|
let e = i.type?.toUpperCase() ?? "";
|
|
324
|
-
|
|
350
|
+
D === "purchased" || x || E.has(e) ? await K() : await q();
|
|
325
351
|
},
|
|
326
|
-
disabled: !
|
|
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:
|
|
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 {
|
|
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
|