@burdenoff/microfe-store 2026.518.1 → 2026.518.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.
@@ -11,8 +11,20 @@ var h = "query MyWorkspaces { myWorkspaces { items { id name slug type status or
11
11
  "DASHBOARD",
12
12
  "PARSER",
13
13
  "DATASINK"
14
- ]);
15
- function x(i) {
14
+ ]), x = {
15
+ ALREADY_INSTALLED: "This app is already installed in the selected workspace.",
16
+ QUOTA_EXCEEDED: "Installation limit reached. Please upgrade your plan or contact support.",
17
+ PERMISSION_DENIED: "You do not have permission to install apps in this workspace.",
18
+ NOT_FOUND: "The app or workspace could not be found.",
19
+ WORKSPACE_INACTIVE: "The selected workspace is inactive.",
20
+ ENTITLEMENT_EXPIRED: "Your license for this app has expired.",
21
+ UNSUPPORTED_TYPE: "This product type cannot be installed in the selected workspace."
22
+ };
23
+ function S(e) {
24
+ for (let [t, n] of Object.entries(x)) if (e.toUpperCase().includes(t)) return n;
25
+ return "Installation failed. Please try again or contact support.";
26
+ }
27
+ function C(i) {
16
28
  let { authToken: a } = e(), [o, s] = r([]), [c, l] = r(!1), [u, d] = r(null), f = t(async () => {
17
29
  if (!i) {
18
30
  l(!0), d(null);
@@ -40,63 +52,63 @@ function x(i) {
40
52
  refetch: f
41
53
  };
42
54
  }
43
- var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free", onInstallSuccess: T, onInstallError: E }) => {
44
- let { authToken: D, user: O } = e(), [k, A] = r(null), [j, M] = r(!1), [N, P] = r(null), [F, I] = r(null), { workspaces: L, loading: R, error: z } = x(!t);
55
+ var w = ({ isOpen: t, onClose: h, app: x, orderId: w, purchaseState: T = "free", onInstallSuccess: E, onInstallError: D }) => {
56
+ let { authToken: O, user: k } = e(), [A, j] = r(null), [M, N] = r(!1), [P, F] = r(null), [I, L] = r(null), { workspaces: R, loading: z, error: B } = C(!t);
45
57
  n(() => {
46
- t && (A(null), P(null), I(null));
58
+ t && (j(null), F(null), L(null));
47
59
  }, [t]);
48
- let B = L.filter((e) => e.status === "ACTIVE"), V = async () => {
49
- if (!k) return;
50
- let e = B.find((e) => e.id === k);
60
+ let V = R.filter((e) => e.status === "ACTIVE"), H = async () => {
61
+ if (!A) return;
62
+ let e = V.find((e) => e.id === A);
51
63
  if (!e) return;
52
- let t = O?.id;
53
- M(!0), P(null), I(null);
64
+ let t = k?.id;
65
+ N(!0), F(null), L(null);
54
66
  try {
55
- if (!C && w !== "purchased") {
67
+ if (!w && T !== "purchased") {
56
68
  if (!t) throw Error("Unable to identify the current user for license creation");
57
69
  await m({
58
70
  gateway: "global",
59
71
  query: y,
60
72
  variables: { input: {
61
- productId: S.id,
73
+ productId: x.id,
62
74
  purchasedBy: t,
63
75
  workspaceId: e.id,
64
76
  metadata: {
65
77
  source: "store-ui",
66
- productType: S.type
78
+ productType: x.type
67
79
  }
68
80
  } },
69
- authToken: D || void 0
81
+ authToken: O || void 0
70
82
  });
71
83
  }
72
84
  let n = await m({
73
85
  gateway: "global",
74
86
  query: v,
75
87
  variables: {
76
- orderId: C ?? null,
77
- productId: S.id,
88
+ orderId: w ?? null,
89
+ productId: x.id,
78
90
  workspaceId: e.id
79
91
  },
80
- authToken: D || void 0
92
+ authToken: O || void 0
81
93
  });
82
94
  if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Installation failed");
83
95
  let r = n.data?.installPurchasedApp;
84
- if (r?.success) r.alreadyInstalled ? I(r.message || "Already installed on this workspace") : (T?.(e.id, e.name), h());
96
+ if (r?.success) r.alreadyInstalled ? L(r.message || "Already installed on this workspace") : (E?.(e.id, e.name), h());
85
97
  else {
86
98
  let e = r?.error || "Installation failed";
87
- P(e), E?.(Error(e));
99
+ F(S(e)), D?.(Error(e));
88
100
  }
89
101
  } catch (e) {
90
102
  let t = e instanceof Error ? e.message : "Installation failed";
91
- P(t), E?.(Error(t));
103
+ F(S(t)), D?.(Error(t));
92
104
  } finally {
93
- M(!1);
105
+ N(!1);
94
106
  }
95
- }, H = async () => {
96
- if (!k) return;
97
- let e = B.find((e) => e.id === k);
107
+ }, U = async () => {
108
+ if (!A) return;
109
+ let e = V.find((e) => e.id === A);
98
110
  if (e) {
99
- M(!0), P(null), I(null);
111
+ N(!0), F(null), L(null);
100
112
  try {
101
113
  let t = await m({
102
114
  gateway: "workspace",
@@ -104,17 +116,17 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
104
116
  variables: {
105
117
  workspaceIds: [e.id],
106
118
  input: {
107
- appName: S.slug || S.name.toLowerCase().replace(/\s+/g, "-"),
108
- displayName: S.name,
109
- version: S.version || "1.0.0",
110
- type: S.type || "app",
119
+ appName: x.slug || x.name.toLowerCase().replace(/\s+/g, "-"),
120
+ displayName: x.name,
121
+ version: x.version || "1.0.0",
122
+ type: x.type || "app",
111
123
  manifest: {
112
- productId: S.id,
113
- icon: S.icon
124
+ productId: x.id,
125
+ icon: x.icon
114
126
  }
115
127
  }
116
128
  },
117
- authToken: D || void 0
129
+ authToken: O || void 0
118
130
  });
119
131
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Installation failed");
120
132
  let n = t.data?.installWorkspaceApps;
@@ -124,34 +136,32 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
124
136
  gateway: "global",
125
137
  query: _,
126
138
  variables: { input: {
127
- productId: S.id,
139
+ productId: x.id,
128
140
  workspaceId: e.id,
129
141
  workspaceName: e.name,
130
142
  workspaceType: e.type,
131
143
  organizationId: e.organizationId,
132
- version: S.version || "1.0.0",
144
+ version: x.version || "1.0.0",
133
145
  manifest: {
134
- productId: S.id,
135
- icon: S.icon,
136
- slug: S.slug,
137
- type: S.type
146
+ productId: x.id,
147
+ icon: x.icon,
148
+ slug: x.slug,
149
+ type: x.type
138
150
  }
139
151
  } },
140
- authToken: D || void 0
152
+ authToken: O || void 0
141
153
  });
142
- } catch (e) {
143
- console.warn("Failed to record installation in store:", e);
144
- }
145
- T?.(e.id, e.name), h();
154
+ } catch {}
155
+ E?.(e.id, e.name), h();
146
156
  } else {
147
157
  let e = n?.results?.[0]?.error || "Installation failed";
148
- P(e), E?.(Error(e));
158
+ F(S(e)), D?.(Error(e));
149
159
  }
150
160
  } catch (e) {
151
161
  let t = e instanceof Error ? e.message : "Installation failed";
152
- P(t), E?.(Error(t));
162
+ F(S(t)), D?.(Error(t));
153
163
  } finally {
154
- M(!1);
164
+ N(!1);
155
165
  }
156
166
  }
157
167
  };
@@ -170,20 +180,20 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
170
180
  className: "flex items-center justify-between border-b border-border-default px-6 py-4",
171
181
  children: [/* @__PURE__ */ p("div", {
172
182
  className: "flex items-center gap-3",
173
- children: [S.icon ? /* @__PURE__ */ f("img", {
174
- src: S.icon,
175
- alt: S.name,
183
+ children: [x.icon ? /* @__PURE__ */ f("img", {
184
+ src: x.icon,
185
+ alt: x.name,
176
186
  className: "h-10 w-10 rounded-lg object-cover"
177
187
  }) : /* @__PURE__ */ f("div", {
178
188
  className: "flex h-10 w-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted",
179
- children: S.name.charAt(0)
189
+ children: x.name.charAt(0)
180
190
  }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h2", {
181
191
  id: "install-app-modal-title",
182
192
  className: "text-lg font-semibold text-text-primary",
183
193
  children: "Install App"
184
194
  }), /* @__PURE__ */ f("p", {
185
195
  className: "text-sm text-text-muted",
186
- children: S.name
196
+ children: x.name
187
197
  })] })]
188
198
  }), /* @__PURE__ */ f("button", {
189
199
  onClick: h,
@@ -199,10 +209,10 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
199
209
  children: "Select the workspace where you want to install this app:"
200
210
  }), /* @__PURE__ */ f("div", {
201
211
  className: "max-h-72 overflow-y-auto rounded-lg border border-border-default",
202
- children: R ? /* @__PURE__ */ f("div", {
212
+ children: z ? /* @__PURE__ */ f("div", {
203
213
  className: "flex items-center justify-center py-12",
204
214
  children: /* @__PURE__ */ f(c, { className: "h-8 w-8 animate-spin text-text-muted" })
205
- }) : z ? /* @__PURE__ */ p("div", {
215
+ }) : B ? /* @__PURE__ */ p("div", {
206
216
  className: "flex flex-col items-center justify-center py-12 text-center",
207
217
  children: [
208
218
  /* @__PURE__ */ f(i, { className: "mb-2 h-8 w-8 text-status-error-text" }),
@@ -212,10 +222,10 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
212
222
  }),
213
223
  /* @__PURE__ */ f("p", {
214
224
  className: "text-xs text-text-muted mt-1",
215
- children: z.message
225
+ children: B.message
216
226
  })
217
227
  ]
218
- }) : B.length === 0 ? /* @__PURE__ */ p("div", {
228
+ }) : V.length === 0 ? /* @__PURE__ */ p("div", {
219
229
  className: "flex flex-col items-center justify-center py-12 text-center",
220
230
  children: [/* @__PURE__ */ f(a, { className: "mb-2 h-8 w-8 text-text-muted" }), /* @__PURE__ */ f("p", {
221
231
  className: "text-sm text-text-muted",
@@ -223,9 +233,9 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
223
233
  })]
224
234
  }) : /* @__PURE__ */ f("div", {
225
235
  className: "divide-y divide-border-default",
226
- children: B.map((e) => /* @__PURE__ */ p("button", {
227
- onClick: () => A(e.id),
228
- className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${k === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
236
+ children: V.map((e) => /* @__PURE__ */ p("button", {
237
+ onClick: () => j(e.id),
238
+ className: `w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${A === e.id ? "bg-action-primary-bg/10" : "transition-colors hover:bg-bg-sunken"}`,
229
239
  children: [
230
240
  /* @__PURE__ */ f("div", {
231
241
  className: `flex h-10 w-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"}`,
@@ -241,7 +251,7 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
241
251
  children: [e.type === "user" ? "Personal" : "Team", " workspace"]
242
252
  })]
243
253
  }),
244
- k === e.id && /* @__PURE__ */ f(o, { className: "h-5 w-5 flex-shrink-0 text-action-primary-bg" })
254
+ A === e.id && /* @__PURE__ */ f(o, { className: "h-5 w-5 flex-shrink-0 text-action-primary-bg" })
245
255
  ]
246
256
  }, e.id))
247
257
  })
@@ -250,29 +260,29 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
250
260
  /* @__PURE__ */ p("div", {
251
261
  className: "border-t border-border-default px-6 py-4",
252
262
  children: [
253
- F && /* @__PURE__ */ p("div", {
263
+ I && /* @__PURE__ */ p("div", {
254
264
  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",
255
- children: [/* @__PURE__ */ f(s, { className: "h-4 w-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: F })]
265
+ children: [/* @__PURE__ */ f(s, { className: "h-4 w-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: I })]
256
266
  }),
257
- N && /* @__PURE__ */ p("div", {
267
+ P && /* @__PURE__ */ p("div", {
258
268
  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",
259
- children: [/* @__PURE__ */ f(i, { className: "h-4 w-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: N })]
269
+ children: [/* @__PURE__ */ f(i, { className: "h-4 w-4 flex-shrink-0" }), /* @__PURE__ */ f("span", { children: P })]
260
270
  }),
261
271
  /* @__PURE__ */ p("div", {
262
272
  className: "flex items-center justify-end gap-3",
263
273
  children: [/* @__PURE__ */ f("button", {
264
274
  onClick: h,
265
- disabled: j,
275
+ disabled: M,
266
276
  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",
267
277
  children: "Cancel"
268
278
  }), /* @__PURE__ */ f("button", {
269
279
  onClick: async () => {
270
- let e = S.type?.toUpperCase() ?? "";
271
- w === "purchased" || C || b.has(e) ? await V() : await H();
280
+ let e = x.type?.toUpperCase() ?? "";
281
+ T === "purchased" || w || b.has(e) ? await H() : await U();
272
282
  },
273
- disabled: !k || j,
283
+ disabled: !A || M,
274
284
  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",
275
- children: j ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(c, { className: "h-4 w-4 animate-spin" }), "Installing..."] }) : "Install App"
285
+ children: M ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(c, { className: "h-4 w-4 animate-spin" }), "Installing..."] }) : "Install App"
276
286
  })]
277
287
  })
278
288
  ]
@@ -282,6 +292,6 @@ var S = ({ isOpen: t, onClose: h, app: S, orderId: C, purchaseState: w = "free",
282
292
  }) : null;
283
293
  };
284
294
  //#endregion
285
- export { S as InstallAppModal };
295
+ export { w as InstallAppModal };
286
296
 
287
297
  //# 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 } 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\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 [selectedWorkspace, setSelectedWorkspace] = useState<string | null>(null);\n const [installing, setInstalling] = useState(false);\n const [installError, setInstallError] = useState<string | null>(null);\n const [installInfo, setInstallInfo] = useState<string | null>(null);\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n\n // Reset selection when modal opens\n useEffect(() => {\n if (isOpen) {\n setSelectedWorkspace(null);\n setInstallError(null);\n setInstallInfo(null);\n }\n }, [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 setInstalling(true);\n setInstallError(null);\n setInstallInfo(null);\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 setInstallInfo(installResult.message || 'Already installed on this workspace');\n } else {\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n }\n } else {\n const errorMsg = installResult?.error || 'Installation failed';\n setInstallError(errorMsg);\n onInstallError?.(new Error(errorMsg));\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : 'Installation failed';\n setInstallError(errorMsg);\n onInstallError?.(new Error(errorMsg));\n } finally {\n setInstalling(false);\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 setInstalling(true);\n setInstallError(null);\n setInstallInfo(null);\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 (recordErr) {\n // Log but don't fail - workspace installation succeeded\n console.warn('Failed to record installation in store:', recordErr);\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } else {\n const errorMsg = installResult?.results?.[0]?.error || 'Installation failed';\n setInstallError(errorMsg);\n onInstallError?.(new Error(errorMsg));\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : 'Installation failed';\n setInstallError(errorMsg);\n onInstallError?.(new Error(errorMsg));\n } finally {\n setInstalling(false);\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 <div className=\"absolute inset-0 bg-bg-overlay/50 backdrop-blur-sm\" onClick={onClose} />\n\n {/* Modal */}\n <div\n role=\"dialog\"\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=\"h-10 w-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex h-10 w-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 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=\"h-5 w-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=\"h-8 w-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 h-8 w-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 h-8 w-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 key={workspace.id}\n onClick={() => setSelectedWorkspace(workspace.id)}\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 h-10 w-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=\"h-5 w-5\" />\n ) : (\n <Building2 className=\"h-5 w-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=\"h-5 w-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=\"h-4 w-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=\"h-4 w-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n <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 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=\"h-4 w-4 animate-spin\" />\n Installing...\n </>\n ) : (\n 'Install App'\n )}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;AAiCA,IAAM,IAAuB,mGACvB,IAAgC,mOAChC,IAAiC,kJACjC,IAA+B,2RAC/B,IAAoC,6JACpC,IAA0B,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAkCF,SAAS,EAAc,GAAoC;CACzD,IAAM,EAAE,iBAAc,GAAU,EAC1B,CAAC,GAAY,KAAiB,EAAsB,EAAE,CAAC,EACvD,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAuB,KAAK,EAEhD,IAAkB,EAAY,YAAY;AAC1C,UAGJ;GADA,EAAW,GAAK,EAChB,EAAS,KAAK;AAEd,OAAI;IACF,IAAM,IAAS,MAAM,EAA8C;KACjE,SAAS;KACT,OAAO;KACP,WAAW,KAAa,KAAA;KACzB,CAAC;AAEF,QAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,gBAAgB;AAG/D,MAAc,EAAO,MAAM,cAAc,SAAS,EAAE,CAAC;YAC9C,GAAK;AACZ,MAAS,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CAAC;aACtE;AACR,MAAW,GAAM;;;IAElB,CAAC,GAAW,EAAK,CAAC;AAMrB,QAJA,QAAgB;AACd,KAAiB;IAChB,CAAC,EAAgB,CAAC,EAEd;EACL;EACA;EACA;EACA,SAAS;EACV;;AAQH,IAAa,KAA6C,EACxD,WACA,YACA,QACA,YACA,mBAAgB,QAChB,qBACA,wBACI;CACJ,IAAM,EAAE,cAAW,YAAS,GAAU,EAChC,CAAC,GAAmB,KAAwB,EAAwB,KAAK,EACzE,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAC/D,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAE7D,EAAE,eAAY,YAAS,aAAU,EAAc,CAAC,EAAO;AAG7D,SAAgB;AACd,EAAI,MACF,EAAqB,KAAK,EAC1B,EAAgB,KAAK,EACrB,EAAe,KAAK;IAErB,CAAC,EAAO,CAAC;CAGZ,IAAM,IAAqB,EAAW,QAAQ,MAAO,EAAG,WAAW,SAAS,EAMtE,IAAyB,YAAY;AACzC,MAAI,CAAC,EAAmB;EAExB,IAAM,IAAY,EAAmB,MAAM,MAAO,EAAG,OAAO,EAAkB;AAC9E,MAAI,CAAC,EAAW;EAChB,IAAM,IAAc,GAAM;AAI1B,EAFA,EAAc,GAAK,EACnB,EAAgB,KAAK,EACrB,EAAe,KAAK;AAEpB,MAAI;AACF,OAAI,CAAC,KAAW,MAAkB,aAAa;AAC7C,QAAI,CAAC,EACH,OAAU,MAAM,2DAA2D;AAG7E,UAAM,EAAa;KACjB,SAAS;KACT,OAAO;KACP,WAAW,EACT,OAAO;MACL,WAAW,EAAI;MACf;MACA,aAAa,EAAU;MACvB,UAAU;OACR,QAAQ;OACR,aAAa,EAAI;OAClB;MACF,EACF;KACD,WAAW,KAAa,KAAA;KACzB,CAAC;;GAIJ,IAAM,IAAS,MAAM,EAclB;IACD,SAAS;IACT,OAAO;IACP,WAAW;KACT,SAAS,KAAW;KACpB,WAAW,EAAI;KACf,aAAa,EAAU;KACxB;IACD,WAAW,KAAa,KAAA;IACzB,CAAC;AAEF,OAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,sBAAsB;GAGrE,IAAM,IAAgB,EAAO,MAAM;AAEnC,OAAI,GAAe,QACjB,CAAI,EAAc,mBAChB,EAAe,EAAc,WAAW,sCAAsC,IAE9E,IAAmB,EAAU,IAAI,EAAU,KAAK,EAChD,GAAS;QAEN;IACL,IAAM,IAAW,GAAe,SAAS;AAEzC,IADA,EAAgB,EAAS,EACzB,IAAqB,MAAM,EAAS,CAAC;;WAEhC,GAAK;GACZ,IAAM,IAAW,aAAe,QAAQ,EAAI,UAAU;AAEtD,GADA,EAAgB,EAAS,EACzB,IAAqB,MAAM,EAAS,CAAC;YAC7B;AACR,KAAc,GAAM;;IAQlB,IAA4B,YAAY;AAC5C,MAAI,CAAC,EAAmB;EAExB,IAAM,IAAY,EAAmB,MAAM,MAAO,EAAG,OAAO,EAAkB;AACzE,SAIL;GAFA,EAAc,GAAK,EACnB,EAAgB,KAAK,EACrB,EAAe,KAAK;AAEpB,OAAI;IAgBF,IAAM,IAAS,MAAM,EAElB;KACD,SAAS;KACT,OAAO;KACP,WAnBkB;MAClB,cAAc,CAAC,EAAU,GAAG;MAC5B,OAAO;OACL,SAAS,EAAI,QAAQ,EAAI,KAAK,aAAa,CAAC,QAAQ,QAAQ,IAAI;OAChE,aAAa,EAAI;OACjB,SAAS,EAAI,WAAW;OACxB,MAAM,EAAI,QAAQ;OAClB,UAAU;QACR,WAAW,EAAI;QACf,MAAM,EAAI;QACX;OACF;MACF;KAQC,WAAW,KAAa,KAAA;KACzB,CAAC;AAEF,QAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,sBAAsB;IAGrE,IAAM,IAAgB,EAAO,MAAM;AACnC,QAAI,GAAe,gBAAgB,EAAc,eAAe,GAAG;AAEjE,SAAI;AACF,YAAM,EAAa;OACjB,SAAS;OACT,OAAO;OACP,WAAW,EACT,OAAO;QACL,WAAW,EAAI;QACf,aAAa,EAAU;QACvB,eAAe,EAAU;QACzB,eAAe,EAAU;QACzB,gBAAgB,EAAU;QAC1B,SAAS,EAAI,WAAW;QACxB,UAAU;SACR,WAAW,EAAI;SACf,MAAM,EAAI;SACV,MAAM,EAAI;SACV,MAAM,EAAI;SACX;QACF,EACF;OACD,WAAW,KAAa,KAAA;OACzB,CAAC;cACK,GAAW;AAElB,cAAQ,KAAK,2CAA2C,EAAU;;AAIpE,KADA,IAAmB,EAAU,IAAI,EAAU,KAAK,EAChD,GAAS;WACJ;KACL,IAAM,IAAW,GAAe,UAAU,IAAI,SAAS;AAEvD,KADA,EAAgB,EAAS,EACzB,IAAqB,MAAM,EAAS,CAAC;;YAEhC,GAAK;IACZ,IAAM,IAAW,aAAe,QAAQ,EAAI,UAAU;AAEtD,IADA,EAAgB,EAAS,EACzB,IAAqB,MAAM,EAAS,CAAC;aAC7B;AACR,MAAc,GAAM;;;;AAqBxB,QAFK,IAGH,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GAAK,WAAU;GAAqD,SAAS;GAAW,CAAA,EAGxF,kBAAC,OAAD;GACE,MAAK;GACL,cAAW;GACX,mBAAgB;GAChB,WAAU;aAJZ;IAOE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAI,OACH,kBAAC,OAAD;OAAK,KAAK,EAAI;OAAM,KAAK,EAAI;OAAM,WAAU;OAAsC,CAAA,GAEnF,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAI,KAAK,OAAO,EAAE;OACf,CAAA,EAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,IAAG;OAA0B,WAAU;iBAA0C;OAEhF,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAA2B,EAAI;OAAS,CAAA,CACjD,EAAA,CAAA,CACF;SACN,kBAAC,UAAD;MACE,SAAS;MACT,cAAW;MACX,WAAU;gBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;MAClB,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA2B;MAEpC,CAAA,EAGJ,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,wCAAyC,CAAA;OACxD,CAAA,GACJ,IACF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAa,WAAU,uCAAwC,CAAA;QAC/D,kBAAC,KAAD;SAAG,WAAU;mBAAiC;SAA6B,CAAA;QAC3E,kBAAC,KAAD;SAAG,WAAU;mBAAgC,EAAM;SAAY,CAAA;QAC3D;WACJ,EAAmB,WAAW,IAChC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,gCAAiC,CAAA,EACtD,kBAAC,KAAD;QAAG,WAAU;kBAA0B;QAA2B,CAAA,CAC9D;WAEN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAmB,KAAK,MACvB,kBAAC,UAAD;QAEE,eAAe,EAAqB,EAAU,GAAG;QACjD,WAAW,uFACT,MAAsB,EAAU,KAC5B,4BACA;kBANR;SAUE,kBAAC,OAAD;UACE,WAAW,yDACT,EAAU,SAAS,SACf,mDACA;oBAGL,EAAU,SAAS,SAClB,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,GAE5B,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;UAE/B,CAAA;SAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAA0C,EAAU;WAAS,CAAA,EAC1E,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACG,EAAU,SAAS,SAAS,aAAa,QAAO,aAC/C;aACA;;SAGL,MAAsB,EAAU,MAC/B,kBAAC,GAAD,EAAa,WAAU,gDAAiD,CAAA;SAEnE;UAnCF,EAAU,GAmCR,CACT;OACE,CAAA;MAEJ,CAAA,CACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,yBAA0B,CAAA,EAC1C,kBAAC,QAAD,EAAA,UAAO,GAAmB,CAAA,CACtB;;MAGP,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,yBAA0B,CAAA,EACjD,kBAAC,QAAD,EAAA,UAAO,GAAoB,CAAA,CACvB;;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,SAAS;QACT,UAAU;QACV,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,SAlJU,YAAY;SAChC,IAAM,IAAU,EAAI,MAAM,aAAa,IAAI;AAE3C,SAAI,MAAkB,eAAe,KAAW,EAAwB,IAAI,EAAQ,GAElF,MAAM,GAAwB,GAG9B,MAAM,GAA2B;;QA2IzB,UAAU,CAAC,KAAqB;QAChC,WAAU;kBAET,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,gBAE3C,EAAA,CAAA,GAEH;QAEK,CAAA,CACL;;MACF;;IACF;KACF;MAtJY"}
1
+ {"version":3,"file":"InstallAppModal.js","names":[],"sources":["../../src/components/InstallAppModal.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useCallback } 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\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 [selectedWorkspace, setSelectedWorkspace] = useState<string | null>(null);\n const [installing, setInstalling] = useState(false);\n const [installError, setInstallError] = useState<string | null>(null);\n const [installInfo, setInstallInfo] = useState<string | null>(null);\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n\n // Reset selection when modal opens\n useEffect(() => {\n if (isOpen) {\n setSelectedWorkspace(null);\n setInstallError(null);\n setInstallInfo(null);\n }\n }, [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 setInstalling(true);\n setInstallError(null);\n setInstallInfo(null);\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 setInstallInfo(installResult.message || 'Already installed on this workspace');\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 setInstallError(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 setInstallError(errorMsg);\n onInstallError?.(new Error(rawMsg));\n } finally {\n setInstalling(false);\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 setInstalling(true);\n setInstallError(null);\n setInstallInfo(null);\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 setInstallError(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 setInstallError(errorMsg);\n onInstallError?.(new Error(rawMsg));\n } finally {\n setInstalling(false);\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 <div className=\"absolute inset-0 bg-bg-overlay/50 backdrop-blur-sm\" onClick={onClose} />\n\n {/* Modal */}\n <div\n role=\"dialog\"\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=\"h-10 w-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex h-10 w-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 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=\"h-5 w-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=\"h-8 w-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 h-8 w-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 h-8 w-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 key={workspace.id}\n onClick={() => setSelectedWorkspace(workspace.id)}\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 h-10 w-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=\"h-5 w-5\" />\n ) : (\n <Building2 className=\"h-5 w-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=\"h-5 w-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=\"h-4 w-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=\"h-4 w-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n <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 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=\"h-4 w-4 animate-spin\" />\n Installing...\n </>\n ) : (\n 'Install App'\n )}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -6,12 +6,12 @@ var l = ({ isOpen: l, onClose: u, onCheckout: d, cartItems: f, itemCount: p, sub
6
6
  className: "fixed inset-0 z-40 bg-bg-overlay/50 transition-opacity",
7
7
  onClick: u
8
8
  }), /* @__PURE__ */ s("div", {
9
- className: "fixed right-0 top-0 z-50 h-full w-full max-w-md bg-bg-surface shadow-xl",
9
+ className: "fixed right-0 top-0 z-50 h-full w-full sm:max-w-md bg-bg-surface shadow-xl",
10
10
  children: /* @__PURE__ */ c("div", {
11
11
  className: "flex h-full flex-col",
12
12
  children: [
13
13
  /* @__PURE__ */ c("div", {
14
- className: "flex items-center justify-between border-b border-border-default px-6 py-4",
14
+ className: "flex items-center justify-between border-b border-border-default px-4 sm:px-6 py-4",
15
15
  children: [/* @__PURE__ */ c("div", {
16
16
  className: "flex items-center gap-2",
17
17
  children: [
@@ -33,11 +33,16 @@ var l = ({ isOpen: l, onClose: u, onCheckout: d, cartItems: f, itemCount: p, sub
33
33
  })]
34
34
  }),
35
35
  /* @__PURE__ */ s("div", {
36
- className: "flex-1 overflow-y-auto px-6 py-4",
36
+ className: "flex-1 overflow-y-auto px-4 sm:px-6 py-4",
37
37
  children: f.length === 0 ? /* @__PURE__ */ c("div", {
38
+ role: "status",
39
+ "aria-label": "Your cart is empty",
38
40
  className: "flex h-full flex-col items-center justify-center text-center",
39
41
  children: [
40
- /* @__PURE__ */ s(r, { className: "mb-4 h-16 w-16 text-text-muted" }),
42
+ /* @__PURE__ */ s(r, {
43
+ className: "mb-4 h-16 w-16 text-text-muted",
44
+ "aria-hidden": "true"
45
+ }),
41
46
  /* @__PURE__ */ s("h3", {
42
47
  className: "mb-2 text-lg font-semibold text-text-primary",
43
48
  children: "Your cart is empty"
@@ -79,24 +84,37 @@ var l = ({ isOpen: l, onClose: u, onCheckout: d, cartItems: f, itemCount: p, sub
79
84
  children: [
80
85
  /* @__PURE__ */ s("button", {
81
86
  onClick: () => _(a.id, a.quantity - 1),
87
+ "aria-label": `Decrease quantity of ${a.productName || a.product?.name || "item"}`,
82
88
  className: "cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50",
83
89
  disabled: a.quantity <= 1,
84
- children: /* @__PURE__ */ s(t, { className: "h-4 w-4" })
90
+ children: /* @__PURE__ */ s(t, {
91
+ className: "h-4 w-4",
92
+ "aria-hidden": "true"
93
+ })
85
94
  }),
86
95
  /* @__PURE__ */ s("span", {
87
96
  className: "min-w-[2rem] text-center text-sm font-medium",
97
+ "aria-label": `Quantity: ${a.quantity}`,
88
98
  children: a.quantity
89
99
  }),
90
100
  /* @__PURE__ */ s("button", {
91
101
  onClick: () => _(a.id, a.quantity + 1),
102
+ "aria-label": `Increase quantity of ${a.productName || a.product?.name || "item"}`,
92
103
  className: "cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken",
93
- children: /* @__PURE__ */ s(n, { className: "h-4 w-4" })
104
+ children: /* @__PURE__ */ s(n, {
105
+ className: "h-4 w-4",
106
+ "aria-hidden": "true"
107
+ })
94
108
  })
95
109
  ]
96
110
  }), /* @__PURE__ */ s("button", {
97
111
  onClick: () => v(a.id),
112
+ "aria-label": `Remove ${a.productName || a.product?.name || "item"} from cart`,
98
113
  className: "cursor-pointer ml-auto text-status-error-text transition-colors hover:text-status-error-text-emphasis",
99
- children: /* @__PURE__ */ s(i, { className: "h-4 w-4" })
114
+ children: /* @__PURE__ */ s(i, {
115
+ className: "h-4 w-4",
116
+ "aria-hidden": "true"
117
+ })
100
118
  })]
101
119
  }),
102
120
  /* @__PURE__ */ s("div", {
@@ -112,7 +130,7 @@ var l = ({ isOpen: l, onClose: u, onCheckout: d, cartItems: f, itemCount: p, sub
112
130
  })
113
131
  }),
114
132
  f.length > 0 && /* @__PURE__ */ c("div", {
115
- className: "border-t border-border-default bg-bg-sunken px-6 py-4",
133
+ className: "border-t border-border-default bg-bg-sunken px-4 sm:px-6 py-4",
116
134
  children: [/* @__PURE__ */ c("div", {
117
135
  className: "space-y-2",
118
136
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"CartDrawer.js","names":[],"sources":["../../../src/components/cart/CartDrawer.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { X, ShoppingCart, Trash2, Plus, Minus } from 'lucide-react';\nimport { formatPrice } from '../../utils';\n\n/**\n * Props for CartDrawer\n */\ninterface CartDrawerProps {\n isOpen: boolean;\n onClose: () => void;\n onCheckout: () => void;\n cartItems: Array<{\n id: string;\n quantity: number;\n subtotal: number;\n productName?: string;\n productIcon?: string;\n variantName?: string;\n product?: { icon?: string; name?: string };\n }>;\n itemCount: number;\n subtotal: number;\n tax: number;\n total: number;\n updateQuantity: (cartItemId: string, quantity: number) => Promise<void>;\n removeProduct: (cartItemId: string) => Promise<void>;\n}\n\n/**\n * CartDrawer component\n *\n * Sliding drawer for cart management\n */\nexport const CartDrawer: FC<CartDrawerProps> = ({\n isOpen,\n onClose,\n onCheckout,\n cartItems,\n itemCount,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n}) => {\n if (!isOpen) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-40 bg-bg-overlay/50 transition-opacity\" onClick={onClose} />\n\n {/* Drawer */}\n <div className=\"fixed right-0 top-0 z-50 h-full w-full max-w-md bg-bg-surface shadow-xl\">\n <div className=\"flex h-full flex-col\">\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-2\">\n <ShoppingCart className=\"h-5 w-5 text-text-primary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Shopping Cart</h2>\n {itemCount > 0 && (\n <span className=\"rounded-full bg-bg-accent px-2 py-0.5 text-xs font-medium text-text-link\">\n {itemCount}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n aria-label=\"Close cart\"\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=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Cart Items */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">\n {cartItems.length === 0 ? (\n <div className=\"flex h-full flex-col items-center justify-center text-center\">\n <ShoppingCart className=\"mb-4 h-16 w-16 text-text-muted\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">Your cart is empty</h3>\n <p className=\"text-sm text-text-muted\">Start shopping to add items to your cart</p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {cartItems.map((item) => (\n <div\n key={item.id}\n className=\"flex gap-4 rounded-lg border border-border-default p-4\"\n >\n {/* Product Image */}\n <div className=\"h-20 w-20 flex-shrink-0 overflow-hidden rounded bg-bg-sunken\">\n {item.productIcon || item.product?.icon ? (\n <img\n src={item.productIcon || item.product?.icon}\n alt={item.productName || item.product?.name}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <ShoppingCart className=\"h-8 w-8 text-text-muted\" />\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <h4 className=\"mb-1 font-semibold text-text-primary\">\n {item.productName || item.product?.name || 'Product'}\n </h4>\n {item.variantName && (\n <p className=\"mb-2 text-xs text-text-muted\">{item.variantName}</p>\n )}\n\n {/* Quantity Controls */}\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center rounded border border-border-default\">\n <button\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n className=\"cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"h-4 w-4\" />\n </button>\n <span className=\"min-w-[2rem] text-center text-sm font-medium\">\n {item.quantity}\n </span>\n <button\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n className=\"cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" />\n </button>\n </div>\n\n <button\n onClick={() => removeProduct(item.id)}\n className=\"cursor-pointer ml-auto text-status-error-text transition-colors hover:text-status-error-text-emphasis\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n\n {/* Price */}\n <div className=\"mt-2 text-right\">\n <span className=\"font-semibold text-text-primary\">\n {formatPrice(item.subtotal)}\n </span>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer with Summary */}\n {cartItems.length > 0 && (\n <div className=\"border-t border-border-default bg-bg-sunken px-6 py-4\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Subtotal</span>\n <span className=\"font-medium text-text-primary\">{formatPrice(subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Tax</span>\n <span className=\"font-medium text-text-primary\">{formatPrice(tax)}</span>\n </div>\n <div className=\"flex justify-between border-t border-border-default pt-2\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatPrice(total)}</span>\n </div>\n </div>\n\n <button\n onClick={onCheckout}\n className=\"cursor-pointer mt-4 w-full rounded-lg bg-action-primary-bg py-3 font-bold text-action-primary-text shadow-lg transition-all hover:opacity-90 hover:shadow-xl active:scale-[0.98]\"\n >\n Proceed to Checkout\n </button>\n </div>\n )}\n </div>\n </div>\n </>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CartDrawer.js","names":[],"sources":["../../../src/components/cart/CartDrawer.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { X, ShoppingCart, Trash2, Plus, Minus } from 'lucide-react';\nimport { formatPrice } from '../../utils';\n\n/**\n * Props for CartDrawer\n */\ninterface CartDrawerProps {\n isOpen: boolean;\n onClose: () => void;\n onCheckout: () => void;\n cartItems: Array<{\n id: string;\n quantity: number;\n subtotal: number;\n productName?: string;\n productIcon?: string;\n variantName?: string;\n product?: { icon?: string; name?: string };\n }>;\n itemCount: number;\n subtotal: number;\n tax: number;\n total: number;\n updateQuantity: (cartItemId: string, quantity: number) => Promise<void>;\n removeProduct: (cartItemId: string) => Promise<void>;\n}\n\n/**\n * CartDrawer component\n *\n * Sliding drawer for cart management\n */\nexport const CartDrawer: FC<CartDrawerProps> = ({\n isOpen,\n onClose,\n onCheckout,\n cartItems,\n itemCount,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n}) => {\n if (!isOpen) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-40 bg-bg-overlay/50 transition-opacity\" onClick={onClose} />\n\n {/* Drawer */}\n <div className=\"fixed right-0 top-0 z-50 h-full w-full sm:max-w-md bg-bg-surface shadow-xl\">\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-border-default px-4 sm:px-6 py-4\">\n <div className=\"flex items-center gap-2\">\n <ShoppingCart className=\"h-5 w-5 text-text-primary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Shopping Cart</h2>\n {itemCount > 0 && (\n <span className=\"rounded-full bg-bg-accent px-2 py-0.5 text-xs font-medium text-text-link\">\n {itemCount}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n aria-label=\"Close cart\"\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=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Cart Items */}\n <div className=\"flex-1 overflow-y-auto px-4 sm:px-6 py-4\">\n {cartItems.length === 0 ? (\n <div\n role=\"status\"\n aria-label=\"Your cart is empty\"\n className=\"flex h-full flex-col items-center justify-center text-center\"\n >\n <ShoppingCart className=\"mb-4 h-16 w-16 text-text-muted\" aria-hidden=\"true\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">Your cart is empty</h3>\n <p className=\"text-sm text-text-muted\">Start shopping to add items to your cart</p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {cartItems.map((item) => (\n <div\n key={item.id}\n className=\"flex gap-4 rounded-lg border border-border-default p-4\"\n >\n {/* Product Image */}\n <div className=\"h-20 w-20 flex-shrink-0 overflow-hidden rounded bg-bg-sunken\">\n {item.productIcon || item.product?.icon ? (\n <img\n src={item.productIcon || item.product?.icon}\n alt={item.productName || item.product?.name}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <ShoppingCart className=\"h-8 w-8 text-text-muted\" />\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <h4 className=\"mb-1 font-semibold text-text-primary\">\n {item.productName || item.product?.name || 'Product'}\n </h4>\n {item.variantName && (\n <p className=\"mb-2 text-xs text-text-muted\">{item.variantName}</p>\n )}\n\n {/* Quantity Controls */}\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center rounded border border-border-default\">\n <button\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n aria-label={`Decrease quantity of ${item.productName || item.product?.name || 'item'}`}\n className=\"cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n <span\n className=\"min-w-[2rem] text-center text-sm font-medium\"\n aria-label={`Quantity: ${item.quantity}`}\n >\n {item.quantity}\n </span>\n <button\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n aria-label={`Increase quantity of ${item.productName || item.product?.name || 'item'}`}\n className=\"cursor-pointer p-1.5 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <button\n onClick={() => removeProduct(item.id)}\n aria-label={`Remove ${item.productName || item.product?.name || 'item'} from cart`}\n className=\"cursor-pointer ml-auto text-status-error-text transition-colors hover:text-status-error-text-emphasis\"\n >\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n {/* Price */}\n <div className=\"mt-2 text-right\">\n <span className=\"font-semibold text-text-primary\">\n {formatPrice(item.subtotal)}\n </span>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer with Summary */}\n {cartItems.length > 0 && (\n <div className=\"border-t border-border-default bg-bg-sunken px-4 sm:px-6 py-4\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Subtotal</span>\n <span className=\"font-medium text-text-primary\">{formatPrice(subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Tax</span>\n <span className=\"font-medium text-text-primary\">{formatPrice(tax)}</span>\n </div>\n <div className=\"flex justify-between border-t border-border-default pt-2\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatPrice(total)}</span>\n </div>\n </div>\n\n <button\n onClick={onCheckout}\n className=\"cursor-pointer mt-4 w-full rounded-lg bg-action-primary-bg py-3 font-bold text-action-primary-text shadow-lg transition-all hover:opacity-90 hover:shadow-xl active:scale-[0.98]\"\n >\n Proceed to Checkout\n </button>\n </div>\n )}\n </div>\n </div>\n </>\n );\n};\n"],"mappings":"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"}
@@ -15,7 +15,7 @@ function d() {
15
15
  fetchPolicy: e ? "network-only" : "cache-first"
16
16
  })).data?.myCart || null);
17
17
  } catch (e) {
18
- b(e), console.error("Failed to fetch cart:", e);
18
+ b(e);
19
19
  } finally {
20
20
  v(!1);
21
21
  }
@@ -40,8 +40,6 @@ function d() {
40
40
  variables: { input: e }
41
41
  }), n.data?.addToCart.errors && n.data.addToCart.errors.length > 0) throw Error(n.data.addToCart.errors[0].message);
42
42
  return n.data?.addToCart.cart && g(n.data.addToCart.cart), await j(!0), n.data?.addToCart.cart;
43
- } catch (e) {
44
- throw console.error("Failed to add product to cart:", e), e;
45
43
  } finally {
46
44
  S(!1);
47
45
  }
@@ -69,8 +67,6 @@ function d() {
69
67
  } }
70
68
  }), n.data?.updateCartItem.errors && n.data.updateCartItem.errors.length > 0) throw Error(n.data.updateCartItem.errors[0].message);
71
69
  return n.data?.updateCartItem.cart && g(n.data.updateCartItem.cart), await j(!0), n.data?.updateCartItem.cart;
72
- } catch (e) {
73
- throw console.error("Failed to update cart item:", e), e;
74
70
  } finally {
75
71
  w(!1);
76
72
  }
@@ -92,8 +88,6 @@ function d() {
92
88
  variables: { cartItemId: e }
93
89
  }), t.data?.removeFromCart.errors && t.data.removeFromCart.errors.length > 0) throw Error(t.data.removeFromCart.errors[0].message);
94
90
  return t.data?.removeFromCart.cart && g(t.data.removeFromCart.cart), await j(!0), t.data?.removeFromCart.cart;
95
- } catch (e) {
96
- throw console.error("Failed to remove product from cart:", e), e;
97
91
  } finally {
98
92
  E(!1);
99
93
  }
@@ -108,8 +102,6 @@ function d() {
108
102
  let e;
109
103
  if (e = m ? await p({ mutation: n }) : await d.mutate({ mutation: n }), e.data?.clearCart.errors && e.data.clearCart.errors.length > 0) throw Error(e.data.clearCart.errors[0].message);
110
104
  return e.data?.clearCart.cart && g(e.data.clearCart.cart), await j(!0), e.data?.clearCart.cart;
111
- } catch (e) {
112
- throw console.error("Failed to clear cart:", e), e;
113
105
  } finally {
114
106
  O(!1);
115
107
  }
@@ -131,8 +123,6 @@ function d() {
131
123
  variables: { input: e }
132
124
  }), !t.data?.createStoreOrder?.id) throw Error("Failed to create order");
133
125
  return await j(!0), t.data.createStoreOrder;
134
- } catch (e) {
135
- throw console.error("Failed to create order:", e), e;
136
126
  } finally {
137
127
  A(!1);
138
128
  }