@burdenoff/microfe-store 2026.624.4 → 2026.624.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,95 +1,322 @@
|
|
|
1
1
|
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
2
|
import { useInstallation as t } from "../hooks/useInstallations.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { StoreInstallationStatus as n } from "../generated/global-types.js";
|
|
4
|
+
import { useState as r } from "react";
|
|
5
|
+
import { useI18n as i } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
+
import { ArrowLeft as a, CheckCircle2 as o, ChevronDown as s, ChevronRight as c, ExternalLink as l, Globe as u, Key as d, Link2 as f, Link2Off as p, RefreshCw as m, ShieldCheck as h, Webhook as g, Zap as _ } from "lucide-react";
|
|
7
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
8
|
+
import { useNavigate as x, useParams as S } from "react-router-dom";
|
|
7
9
|
//#region src/pages/AppSettingsPage.tsx
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
return
|
|
10
|
+
function C(e) {
|
|
11
|
+
let t = e.context;
|
|
12
|
+
if (!t) return { connected: !1 };
|
|
13
|
+
let n = t.connected === !0 || !!t.accessToken || !!t.oauth?.accessToken || t.connection?.status === "connected", r = t.account, i = t.oauth;
|
|
14
|
+
return {
|
|
15
|
+
connected: n,
|
|
16
|
+
accountName: r?.name ?? t.accountName ?? i?.accountName,
|
|
17
|
+
accountEmail: r?.email ?? t.accountEmail ?? i?.email,
|
|
18
|
+
connectedAt: t.connectedAt ?? i?.connectedAt,
|
|
19
|
+
expiresAt: t.expiresAt ?? i?.expiresAt
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function w(e) {
|
|
23
|
+
let t = e.manifest, n = e.context, r = t?.permissions ?? n?.scopes ?? n?.oauth?.scopes;
|
|
24
|
+
return r ? Array.isArray(r) ? r.map(String) : typeof r == "string" ? r.split(/[\s,]+/).filter(Boolean) : [] : [];
|
|
25
|
+
}
|
|
26
|
+
function T(e) {
|
|
27
|
+
let t = e.manifest, n = e.context, r = t?.webhooks ?? n?.webhooks ?? t?.events;
|
|
28
|
+
return !r || !Array.isArray(r) ? [] : r.map((e) => {
|
|
29
|
+
if (typeof e == "string") return { event: e };
|
|
30
|
+
let t = e;
|
|
31
|
+
return {
|
|
32
|
+
event: String(t.event ?? t.name ?? t.type ?? "Unknown event"),
|
|
33
|
+
endpoint: t.endpoint,
|
|
34
|
+
active: t.active !== !1
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function E(e) {
|
|
39
|
+
let t = e.manifest, n = t?.oauth ?? t?.auth;
|
|
40
|
+
return n ? {
|
|
41
|
+
authorizationUrl: n.authorizationUrl,
|
|
42
|
+
tokenUrl: n.tokenUrl,
|
|
43
|
+
redirectUri: n.redirectUri,
|
|
44
|
+
clientId: n.clientId
|
|
45
|
+
} : {};
|
|
46
|
+
}
|
|
47
|
+
function D({ children: e }) {
|
|
48
|
+
return /* @__PURE__ */ y("div", {
|
|
49
|
+
className: "rounded-xl border border-border-default bg-bg-surface p-5",
|
|
50
|
+
children: e
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function O({ icon: e, title: t }) {
|
|
54
|
+
return /* @__PURE__ */ b("div", {
|
|
55
|
+
className: "mb-4 flex items-center gap-2",
|
|
56
|
+
children: [/* @__PURE__ */ y("span", {
|
|
57
|
+
className: "text-text-muted",
|
|
58
|
+
children: e
|
|
59
|
+
}), /* @__PURE__ */ y("h2", {
|
|
60
|
+
className: "text-base font-semibold text-text-primary",
|
|
61
|
+
children: t
|
|
62
|
+
})]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function k({ label: e, value: t }) {
|
|
66
|
+
return /* @__PURE__ */ b("div", {
|
|
67
|
+
className: "flex items-start justify-between gap-4 py-2",
|
|
68
|
+
children: [/* @__PURE__ */ y("span", {
|
|
69
|
+
className: "text-sm text-text-muted",
|
|
70
|
+
children: e
|
|
71
|
+
}), /* @__PURE__ */ y("span", {
|
|
72
|
+
className: "text-right text-sm font-medium text-text-primary",
|
|
73
|
+
children: t
|
|
74
|
+
})]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function A({ installation: e }) {
|
|
78
|
+
let t = C(e), r = E(e), i = e.status === n.Active;
|
|
79
|
+
return /* @__PURE__ */ b(D, { children: [
|
|
80
|
+
/* @__PURE__ */ y(O, {
|
|
81
|
+
icon: /* @__PURE__ */ y(f, { className: "size-5" }),
|
|
82
|
+
title: "Connection"
|
|
83
|
+
}),
|
|
84
|
+
/* @__PURE__ */ y("div", {
|
|
85
|
+
className: "mb-4 flex items-center gap-3 rounded-lg bg-bg-sunken px-4 py-3",
|
|
86
|
+
children: t.connected ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(o, { className: "size-5 shrink-0 text-status-success-text" }), /* @__PURE__ */ b("div", { children: [
|
|
87
|
+
/* @__PURE__ */ y("p", {
|
|
88
|
+
className: "text-sm font-medium text-text-primary",
|
|
89
|
+
children: "Connected"
|
|
90
|
+
}),
|
|
91
|
+
t.accountName && /* @__PURE__ */ b("p", {
|
|
92
|
+
className: "text-xs text-text-muted",
|
|
93
|
+
children: [t.accountName, t.accountEmail ? ` · ${t.accountEmail}` : ""]
|
|
94
|
+
}),
|
|
95
|
+
t.connectedAt && /* @__PURE__ */ b("p", {
|
|
96
|
+
className: "text-xs text-text-muted",
|
|
97
|
+
children: ["Since ", new Date(t.connectedAt).toLocaleDateString()]
|
|
98
|
+
})
|
|
99
|
+
] })] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "size-5 shrink-0 text-text-muted" }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
|
|
100
|
+
className: "text-sm font-medium text-text-primary",
|
|
101
|
+
children: "Not connected"
|
|
102
|
+
}), /* @__PURE__ */ y("p", {
|
|
103
|
+
className: "text-xs text-text-muted",
|
|
104
|
+
children: "Authorize this integration to start using it"
|
|
105
|
+
})] })] })
|
|
106
|
+
}),
|
|
107
|
+
t.expiresAt && /* @__PURE__ */ b("p", {
|
|
108
|
+
className: "mb-3 text-xs text-text-muted",
|
|
109
|
+
children: ["Token expires: ", new Date(t.expiresAt).toLocaleString()]
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ b("div", {
|
|
112
|
+
className: "flex flex-wrap gap-3",
|
|
113
|
+
children: [
|
|
114
|
+
!t.connected && i && r.authorizationUrl && /* @__PURE__ */ b("a", {
|
|
115
|
+
href: r.authorizationUrl,
|
|
116
|
+
target: "_blank",
|
|
117
|
+
rel: "noopener noreferrer",
|
|
118
|
+
className: "inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90",
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ y(_, { className: "size-4" }),
|
|
121
|
+
"Connect",
|
|
122
|
+
/* @__PURE__ */ y(l, { className: "size-3.5" })
|
|
123
|
+
]
|
|
124
|
+
}),
|
|
125
|
+
t.connected && /* @__PURE__ */ b(v, { children: [r.authorizationUrl && /* @__PURE__ */ b("a", {
|
|
126
|
+
href: r.authorizationUrl,
|
|
127
|
+
target: "_blank",
|
|
128
|
+
rel: "noopener noreferrer",
|
|
129
|
+
className: "inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
130
|
+
children: [/* @__PURE__ */ y(m, { className: "size-4" }), "Reconnect"]
|
|
131
|
+
}), /* @__PURE__ */ b("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
className: "inline-flex cursor-pointer items-center gap-2 rounded-lg border border-status-error-border px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle",
|
|
134
|
+
children: [/* @__PURE__ */ y(p, { className: "size-4" }), "Revoke access"]
|
|
135
|
+
})] }),
|
|
136
|
+
!t.connected && !r.authorizationUrl && /* @__PURE__ */ y("p", {
|
|
137
|
+
className: "text-xs text-text-muted",
|
|
138
|
+
children: "OAuth URL not configured. Contact the app developer to complete the setup."
|
|
139
|
+
})
|
|
140
|
+
]
|
|
141
|
+
}),
|
|
142
|
+
r.clientId && /* @__PURE__ */ b("p", {
|
|
143
|
+
className: "mt-3 text-xs text-text-muted",
|
|
144
|
+
children: ["Client ID: ", r.clientId]
|
|
145
|
+
})
|
|
146
|
+
] });
|
|
147
|
+
}
|
|
148
|
+
function j({ installation: e }) {
|
|
149
|
+
let t = w(e);
|
|
150
|
+
return t.length === 0 ? null : /* @__PURE__ */ b(D, { children: [
|
|
151
|
+
/* @__PURE__ */ y(O, {
|
|
152
|
+
icon: /* @__PURE__ */ y(h, { className: "size-5" }),
|
|
153
|
+
title: "Granted Permissions"
|
|
154
|
+
}),
|
|
155
|
+
/* @__PURE__ */ y("div", {
|
|
156
|
+
className: "flex flex-wrap gap-2",
|
|
157
|
+
children: t.map((e) => /* @__PURE__ */ b("span", {
|
|
158
|
+
className: "inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-3 py-1 text-xs font-medium text-text-primary",
|
|
159
|
+
children: [/* @__PURE__ */ y(d, { className: "size-3 text-text-muted" }), e]
|
|
160
|
+
}, e))
|
|
161
|
+
}),
|
|
162
|
+
/* @__PURE__ */ y("p", {
|
|
163
|
+
className: "mt-3 text-xs text-text-muted",
|
|
164
|
+
children: "These are the scopes this integration is allowed to access in your workspace."
|
|
165
|
+
})
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
function M({ installation: e }) {
|
|
169
|
+
let t = T(e);
|
|
170
|
+
return t.length === 0 ? null : /* @__PURE__ */ b(D, { children: [/* @__PURE__ */ y(O, {
|
|
171
|
+
icon: /* @__PURE__ */ y(g, { className: "size-5" }),
|
|
172
|
+
title: "Event Subscriptions"
|
|
173
|
+
}), /* @__PURE__ */ y("ul", {
|
|
174
|
+
className: "space-y-2",
|
|
175
|
+
children: t.map((e, t) => /* @__PURE__ */ b("li", {
|
|
176
|
+
className: "flex items-center justify-between rounded-lg bg-bg-sunken px-4 py-3",
|
|
177
|
+
children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
|
|
178
|
+
className: "text-sm font-medium text-text-primary",
|
|
179
|
+
children: e.event
|
|
180
|
+
}), e.endpoint && /* @__PURE__ */ y("p", {
|
|
181
|
+
className: "mt-0.5 font-mono text-xs text-text-muted",
|
|
182
|
+
children: e.endpoint
|
|
183
|
+
})] }), /* @__PURE__ */ y("span", {
|
|
184
|
+
className: `rounded-full px-2 py-0.5 text-xs font-medium ${e.active === !1 ? "bg-bg-sunken text-text-muted" : "bg-status-success-bg-subtle text-status-success-text"}`,
|
|
185
|
+
children: e.active === !1 ? "Inactive" : "Active"
|
|
186
|
+
})]
|
|
187
|
+
}, t))
|
|
188
|
+
})] });
|
|
189
|
+
}
|
|
190
|
+
function N({ installation: e }) {
|
|
191
|
+
return /* @__PURE__ */ b(D, { children: [/* @__PURE__ */ y(O, {
|
|
192
|
+
icon: /* @__PURE__ */ y(u, { className: "size-5" }),
|
|
193
|
+
title: "Installation Details"
|
|
194
|
+
}), /* @__PURE__ */ b("div", {
|
|
195
|
+
className: "divide-y divide-border-default",
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ y(k, {
|
|
198
|
+
label: "Product ID",
|
|
199
|
+
value: e.productId
|
|
200
|
+
}),
|
|
201
|
+
e.version && /* @__PURE__ */ y(k, {
|
|
202
|
+
label: "Version",
|
|
203
|
+
value: `v${e.version}`
|
|
204
|
+
}),
|
|
205
|
+
/* @__PURE__ */ y(k, {
|
|
206
|
+
label: "Status",
|
|
207
|
+
value: e.status
|
|
208
|
+
}),
|
|
209
|
+
/* @__PURE__ */ y(k, {
|
|
210
|
+
label: "Workspace",
|
|
211
|
+
value: e.workspaceName ?? e.workspaceId
|
|
212
|
+
}),
|
|
213
|
+
/* @__PURE__ */ y(k, {
|
|
214
|
+
label: "Installed",
|
|
215
|
+
value: new Date(e.installedAt).toLocaleString()
|
|
216
|
+
}),
|
|
217
|
+
/* @__PURE__ */ y(k, {
|
|
218
|
+
label: "Last updated",
|
|
219
|
+
value: new Date(e.updatedAt).toLocaleString()
|
|
220
|
+
}),
|
|
221
|
+
e.organizationId && /* @__PURE__ */ y(k, {
|
|
222
|
+
label: "Organization",
|
|
223
|
+
value: e.organizationId
|
|
224
|
+
}),
|
|
225
|
+
/* @__PURE__ */ y(k, {
|
|
226
|
+
label: "Installed by",
|
|
227
|
+
value: e.installedById
|
|
228
|
+
})
|
|
229
|
+
]
|
|
230
|
+
})] });
|
|
231
|
+
}
|
|
232
|
+
function P({ installation: e }) {
|
|
233
|
+
let [t, n] = r(!1);
|
|
234
|
+
return e.manifest && Object.keys(e.manifest).length > 0 || e.metadata && Object.keys(e.metadata).length > 0 || e.context && Object.keys(e.context).length > 0 ? /* @__PURE__ */ b(D, { children: [/* @__PURE__ */ b("button", {
|
|
235
|
+
type: "button",
|
|
236
|
+
onClick: () => n((e) => !e),
|
|
237
|
+
className: "flex w-full cursor-pointer items-center justify-between text-left",
|
|
238
|
+
children: [/* @__PURE__ */ y("span", {
|
|
239
|
+
className: "text-base font-semibold text-text-primary",
|
|
240
|
+
children: "Raw configuration"
|
|
241
|
+
}), y(t ? s : c, { className: "size-4 text-text-muted" })]
|
|
242
|
+
}), t && /* @__PURE__ */ b("div", {
|
|
243
|
+
className: "mt-4 grid gap-4 lg:grid-cols-2",
|
|
244
|
+
children: [e.manifest && Object.keys(e.manifest).length > 0 && /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
|
|
245
|
+
className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-muted",
|
|
246
|
+
children: "Manifest"
|
|
247
|
+
}), /* @__PURE__ */ y("pre", {
|
|
248
|
+
className: "overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted",
|
|
249
|
+
children: JSON.stringify(e.manifest, null, 2)
|
|
250
|
+
})] }), (e.metadata || e.context) && /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
|
|
251
|
+
className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-muted",
|
|
252
|
+
children: "Runtime state"
|
|
253
|
+
}), /* @__PURE__ */ y("pre", {
|
|
254
|
+
className: "overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted",
|
|
255
|
+
children: JSON.stringify({
|
|
256
|
+
metadata: e.metadata ?? {},
|
|
257
|
+
context: e.context ?? {}
|
|
258
|
+
}, null, 2)
|
|
259
|
+
})] })]
|
|
260
|
+
})] }) : null;
|
|
261
|
+
}
|
|
262
|
+
var F = () => {
|
|
263
|
+
let n = x(), { basePath: r } = e(), { installationId: o } = S(), { installation: s, loading: c, error: l } = t(o), { t: u } = i();
|
|
264
|
+
return /* @__PURE__ */ b("div", {
|
|
11
265
|
className: "flex h-full flex-col",
|
|
12
|
-
children: [/* @__PURE__ */
|
|
266
|
+
children: [/* @__PURE__ */ y("header", {
|
|
13
267
|
className: "border-b border-border-default bg-bg-surface px-6 py-4",
|
|
14
|
-
children: /* @__PURE__ */
|
|
268
|
+
children: /* @__PURE__ */ b("div", {
|
|
15
269
|
className: "mx-auto flex max-w-4xl items-center gap-3",
|
|
16
|
-
children: [/* @__PURE__ */
|
|
270
|
+
children: [/* @__PURE__ */ y("button", {
|
|
17
271
|
type: "button",
|
|
18
|
-
onClick: () =>
|
|
272
|
+
onClick: () => n(-1),
|
|
19
273
|
"aria-label": "Go back",
|
|
20
274
|
className: "cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary",
|
|
21
|
-
children: /* @__PURE__ */ a
|
|
22
|
-
}), /* @__PURE__ */
|
|
275
|
+
children: /* @__PURE__ */ y(a, { className: "size-5" })
|
|
276
|
+
}), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
|
|
23
277
|
className: "text-2xl font-bold text-text-primary",
|
|
24
|
-
children:
|
|
25
|
-
}), /* @__PURE__ */
|
|
278
|
+
children: u("pages.appSettings.title", { defaultValue: "Integration Settings" })
|
|
279
|
+
}), /* @__PURE__ */ y("p", {
|
|
26
280
|
className: "text-sm text-text-muted",
|
|
27
|
-
children:
|
|
281
|
+
children: u("pages.appSettings.subtitle", { defaultValue: "Manage your connection, permissions and webhook events" })
|
|
28
282
|
})] })]
|
|
29
283
|
})
|
|
30
|
-
}), /* @__PURE__ */
|
|
284
|
+
}), /* @__PURE__ */ y("main", {
|
|
31
285
|
className: "flex-1 overflow-y-auto p-6",
|
|
32
|
-
children: /* @__PURE__ */
|
|
286
|
+
children: /* @__PURE__ */ y("div", {
|
|
33
287
|
className: "mx-auto max-w-4xl",
|
|
34
|
-
children:
|
|
288
|
+
children: c ? /* @__PURE__ */ y("p", {
|
|
35
289
|
role: "status",
|
|
36
290
|
className: "text-sm text-text-muted",
|
|
37
291
|
children: "Loading settings…"
|
|
38
|
-
}) :
|
|
292
|
+
}) : l ? /* @__PURE__ */ y("p", {
|
|
39
293
|
role: "alert",
|
|
40
294
|
className: "text-sm text-status-error-text",
|
|
41
|
-
children:
|
|
42
|
-
}) :
|
|
43
|
-
className: "space-y-4
|
|
295
|
+
children: l.message
|
|
296
|
+
}) : s ? /* @__PURE__ */ b("div", {
|
|
297
|
+
className: "space-y-4",
|
|
44
298
|
children: [
|
|
45
|
-
/* @__PURE__ */
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
className: "text-sm text-text-muted",
|
|
54
|
-
children: h("pages.appSettings.configDescription", { defaultValue: "Store-level configuration for this installation is read-only here." })
|
|
55
|
-
}),
|
|
56
|
-
/* @__PURE__ */ o("div", {
|
|
57
|
-
className: "grid gap-4 lg:grid-cols-2",
|
|
58
|
-
children: [/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("p", {
|
|
59
|
-
className: "mb-2 text-sm font-medium text-text-primary",
|
|
60
|
-
children: "Manifest"
|
|
61
|
-
}), /* @__PURE__ */ a("pre", {
|
|
62
|
-
className: "overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted",
|
|
63
|
-
children: JSON.stringify(f.manifest ?? {}, null, 2)
|
|
64
|
-
})] }), /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("p", {
|
|
65
|
-
className: "mb-2 text-sm font-medium text-text-primary",
|
|
66
|
-
children: "Metadata & Context"
|
|
67
|
-
}), /* @__PURE__ */ a("pre", {
|
|
68
|
-
className: "overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted",
|
|
69
|
-
children: JSON.stringify({
|
|
70
|
-
metadata: f.metadata ?? {},
|
|
71
|
-
context: f.context ?? {},
|
|
72
|
-
version: f.version,
|
|
73
|
-
status: f.status
|
|
74
|
-
}, null, 2)
|
|
75
|
-
})] })]
|
|
76
|
-
}),
|
|
77
|
-
/* @__PURE__ */ o("div", {
|
|
78
|
-
className: "flex flex-wrap gap-3",
|
|
79
|
-
children: [/* @__PURE__ */ a("button", {
|
|
299
|
+
/* @__PURE__ */ y(A, { installation: s }),
|
|
300
|
+
/* @__PURE__ */ y(j, { installation: s }),
|
|
301
|
+
/* @__PURE__ */ y(M, { installation: s }),
|
|
302
|
+
/* @__PURE__ */ y(N, { installation: s }),
|
|
303
|
+
/* @__PURE__ */ y(P, { installation: s }),
|
|
304
|
+
/* @__PURE__ */ b("div", {
|
|
305
|
+
className: "flex flex-wrap gap-3 pt-2",
|
|
306
|
+
children: [/* @__PURE__ */ y("button", {
|
|
80
307
|
type: "button",
|
|
81
|
-
onClick: () =>
|
|
308
|
+
onClick: () => n(`${r}/installed/${s.id}`),
|
|
82
309
|
className: "inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
83
|
-
children:
|
|
84
|
-
}), /* @__PURE__ */
|
|
310
|
+
children: u("pages.appSettings.viewInstallation", { defaultValue: "View Installation" })
|
|
311
|
+
}), /* @__PURE__ */ y("button", {
|
|
85
312
|
type: "button",
|
|
86
|
-
onClick: () =>
|
|
313
|
+
onClick: () => n(`${r}/marketplace/product/${s.productId}`),
|
|
87
314
|
className: "inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90",
|
|
88
|
-
children:
|
|
315
|
+
children: u("pages.appDetail.viewProduct", { defaultValue: "View Product" })
|
|
89
316
|
})]
|
|
90
317
|
})
|
|
91
318
|
]
|
|
92
|
-
}) : /* @__PURE__ */
|
|
319
|
+
}) : /* @__PURE__ */ y("p", {
|
|
93
320
|
className: "text-sm text-text-muted",
|
|
94
321
|
children: "Installation not found."
|
|
95
322
|
})
|
|
@@ -98,6 +325,6 @@ var l = () => {
|
|
|
98
325
|
});
|
|
99
326
|
};
|
|
100
327
|
//#endregion
|
|
101
|
-
export {
|
|
328
|
+
export { F as AppSettingsPage };
|
|
102
329
|
|
|
103
330
|
//# sourceMappingURL=AppSettingsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSettingsPage.js","names":[],"sources":["../../src/pages/AppSettingsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Settings } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const AppSettingsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appSettings.title', { defaultValue: 'App Settings' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.subtitle', {\n defaultValue: 'Review current installation configuration',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading settings…\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"space-y-4 rounded-xl border border-border-default bg-bg-surface p-5\">\n <div className=\"flex items-center gap-2\">\n <Settings className=\"size-5 text-text-primary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.appSettings.configSnapshot', {\n defaultValue: 'Configuration snapshot',\n })}\n </h2>\n </div>\n\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.configDescription', {\n defaultValue:\n 'Store-level configuration for this installation is read-only here.',\n })}\n </p>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Manifest</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(installation.manifest ?? {}, null, 2)}\n </pre>\n </div>\n\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Metadata & Context</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata ?? {},\n context: installation.context ?? {},\n version: installation.version,\n status: installation.status,\n },\n null,\n 2\n )}\n </pre>\n </div>\n </div>\n\n <div className=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {t('pages.appSettings.viewInstallation', { defaultValue: 'View Installation' })}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,UAA4B;CACvC,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAU,EACzB,EAAE,sBAAmB,GAAuC,EAC5D,EAAE,iBAAc,YAAS,aAAU,EAAgB,EAAe,EAClE,EAAE,SAAM,GAAS;AAEvB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GAAQ,WAAU;aAChB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,GAAG;KAC3B,cAAW;KACX,WAAU;eAEV,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;KACzB,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,2BAA2B,EAAE,cAAc,gBAAgB,CAAC;KAC5D,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,8BAA8B,EAC/B,cAAc,6CACf,CAAC;KACA,CAAA,CACA,EAAA,CAAA,CACF;;GACC,CAAA,EAET,kBAAC,QAAD;GAAM,WAAU;aACd,kBAAC,OAAD;IAAK,WAAU;cACZ,IACC,kBAAC,KAAD;KAAG,MAAK;KAAS,WAAU;eAA0B;KAEjD,CAAA,GACF,IACF,kBAAC,KAAD;KAAG,MAAK;KAAQ,WAAU;eACvB,EAAM;KACL,CAAA,GACD,IAGH,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA,EACjD,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,oCAAoC,EACrC,cAAc,0BACf,CAAC;QACC,CAAA,CACD;;MAEN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,uCAAuC,EACxC,cACE,sEACH,CAAC;OACA,CAAA;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAY,CAAA,EACtE,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UAAU,EAAa,YAAY,EAAE,EAAE,MAAM,EAAE;QACjD,CAAA,CACF,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAsB,CAAA,EAChF,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UACJ;SACE,UAAU,EAAa,YAAY,EAAE;SACrC,SAAS,EAAa,WAAW,EAAE;SACnC,SAAS,EAAa;SACtB,QAAQ,EAAa;SACtB,EACD,MACA,EACD;QACG,CAAA,CACF,EAAA,CAAA,CACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,GAAG,EAAS,aAAa,EAAa,KAAK;QACnE,WAAU;kBAET,EAAE,sCAAsC,EAAE,cAAc,qBAAqB,CAAC;QACxE,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,eACE,EAAS,GAAG,EAAS,uBAAuB,EAAa,YAAY;QAEvE,WAAU;kBAET,EAAE,+BAA+B,EAAE,cAAc,gBAAgB,CAAC;QAC5D,CAAA,CACL;;MACF;SA9DN,kBAAC,KAAD;KAAG,WAAU;eAA0B;KAA2B,CAAA;IAgEhE,CAAA;GACD,CAAA,CACH"}
|
|
1
|
+
{"version":3,"file":"AppSettingsPage.js","names":[],"sources":["../../src/pages/AppSettingsPage.tsx"],"sourcesContent":["import { type FC, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n CheckCircle2,\n ChevronDown,\n ChevronRight,\n ExternalLink,\n Globe,\n Key,\n Link2,\n Link2Off,\n RefreshCw,\n ShieldCheck,\n Webhook,\n Zap,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { StoreInstallationStatus } from '../generated/global-types';\nimport type { InstalledApp } from '../hooks/useInstallations';\n\n// ---------------------------------------------------------------------------\n// Helpers to parse free-form manifest / context JSON\n// ---------------------------------------------------------------------------\n\nfunction getConnectionState(installation: InstalledApp): {\n connected: boolean;\n accountName?: string;\n accountEmail?: string;\n connectedAt?: string;\n expiresAt?: string;\n} {\n const ctx = installation.context as Record<string, unknown> | null | undefined;\n if (!ctx) return { connected: false };\n\n const connected =\n ctx['connected'] === true ||\n Boolean(ctx['accessToken']) ||\n Boolean((ctx['oauth'] as Record<string, unknown> | undefined)?.['accessToken']) ||\n (ctx['connection'] as Record<string, unknown> | undefined)?.['status'] === 'connected';\n\n const account = ctx['account'] as Record<string, unknown> | undefined;\n const oauth = ctx['oauth'] as Record<string, unknown> | undefined;\n\n return {\n connected,\n accountName:\n (account?.['name'] as string | undefined) ??\n (ctx['accountName'] as string | undefined) ??\n (oauth?.['accountName'] as string | undefined),\n accountEmail:\n (account?.['email'] as string | undefined) ??\n (ctx['accountEmail'] as string | undefined) ??\n (oauth?.['email'] as string | undefined),\n connectedAt:\n (ctx['connectedAt'] as string | undefined) ??\n (oauth?.['connectedAt'] as string | undefined),\n expiresAt:\n (ctx['expiresAt'] as string | undefined) ?? (oauth?.['expiresAt'] as string | undefined),\n };\n}\n\nfunction getPermissions(installation: InstalledApp): string[] {\n const manifest = installation.manifest as Record<string, unknown> | null | undefined;\n const ctx = installation.context as Record<string, unknown> | null | undefined;\n\n const raw =\n (manifest?.['permissions'] as unknown[] | undefined) ??\n (ctx?.['scopes'] as unknown[] | undefined) ??\n (ctx?.['oauth'] as Record<string, unknown> | undefined)?.['scopes'];\n\n if (!raw) return [];\n if (Array.isArray(raw)) return raw.map(String);\n if (typeof raw === 'string') return raw.split(/[\\s,]+/).filter(Boolean);\n return [];\n}\n\nfunction getWebhooks(\n installation: InstalledApp\n): Array<{ event: string; endpoint?: string; active?: boolean }> {\n const manifest = installation.manifest as Record<string, unknown> | null | undefined;\n const ctx = installation.context as Record<string, unknown> | null | undefined;\n\n const raw =\n (manifest?.['webhooks'] as unknown[] | undefined) ??\n (ctx?.['webhooks'] as unknown[] | undefined) ??\n (manifest?.['events'] as unknown[] | undefined);\n\n if (!raw || !Array.isArray(raw)) return [];\n\n return raw.map((item) => {\n if (typeof item === 'string') return { event: item };\n const obj = item as Record<string, unknown>;\n return {\n event: String(obj['event'] ?? obj['name'] ?? obj['type'] ?? 'Unknown event'),\n endpoint: obj['endpoint'] as string | undefined,\n active: obj['active'] !== false,\n };\n });\n}\n\nfunction getOAuthEndpoints(installation: InstalledApp): {\n authorizationUrl?: string;\n tokenUrl?: string;\n redirectUri?: string;\n clientId?: string;\n} {\n const manifest = installation.manifest as Record<string, unknown> | null | undefined;\n const oauth =\n (manifest?.['oauth'] as Record<string, unknown> | undefined) ??\n (manifest?.['auth'] as Record<string, unknown> | undefined);\n if (!oauth) return {};\n return {\n authorizationUrl: oauth['authorizationUrl'] as string | undefined,\n tokenUrl: oauth['tokenUrl'] as string | undefined,\n redirectUri: oauth['redirectUri'] as string | undefined,\n clientId: oauth['clientId'] as string | undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction SectionCard({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-5\">{children}</div>\n );\n}\n\nfunction SectionTitle({ icon, title }: { icon: React.ReactNode; title: string }) {\n return (\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"text-text-muted\">{icon}</span>\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n </div>\n );\n}\n\nfunction InfoRow({ label, value }: { label: string; value: React.ReactNode }) {\n return (\n <div className=\"flex items-start justify-between gap-4 py-2\">\n <span className=\"text-sm text-text-muted\">{label}</span>\n <span className=\"text-right text-sm font-medium text-text-primary\">{value}</span>\n </div>\n );\n}\n\nfunction ConnectionSection({ installation }: { installation: InstalledApp }) {\n const conn = getConnectionState(installation);\n const oauth = getOAuthEndpoints(installation);\n const isActive = installation.status === StoreInstallationStatus.Active;\n\n return (\n <SectionCard>\n <SectionTitle icon={<Link2 className=\"size-5\" />} title=\"Connection\" />\n\n <div className=\"mb-4 flex items-center gap-3 rounded-lg bg-bg-sunken px-4 py-3\">\n {conn.connected ? (\n <>\n <CheckCircle2 className=\"size-5 shrink-0 text-status-success-text\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Connected</p>\n {conn.accountName && (\n <p className=\"text-xs text-text-muted\">\n {conn.accountName}\n {conn.accountEmail ? ` · ${conn.accountEmail}` : ''}\n </p>\n )}\n {conn.connectedAt && (\n <p className=\"text-xs text-text-muted\">\n Since {new Date(conn.connectedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n </>\n ) : (\n <>\n <Link2Off className=\"size-5 shrink-0 text-text-muted\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Not connected</p>\n <p className=\"text-xs text-text-muted\">\n Authorize this integration to start using it\n </p>\n </div>\n </>\n )}\n </div>\n\n {conn.expiresAt && (\n <p className=\"mb-3 text-xs text-text-muted\">\n Token expires: {new Date(conn.expiresAt).toLocaleString()}\n </p>\n )}\n\n <div className=\"flex flex-wrap gap-3\">\n {!conn.connected && isActive && oauth.authorizationUrl && (\n <a\n href={oauth.authorizationUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90\"\n >\n <Zap className=\"size-4\" />\n Connect\n <ExternalLink className=\"size-3.5\" />\n </a>\n )}\n {conn.connected && (\n <>\n {oauth.authorizationUrl && (\n <a\n href={oauth.authorizationUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className=\"size-4\" />\n Reconnect\n </a>\n )}\n <button\n type=\"button\"\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-status-error-border px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle\"\n >\n <Link2Off className=\"size-4\" />\n Revoke access\n </button>\n </>\n )}\n {!conn.connected && !oauth.authorizationUrl && (\n <p className=\"text-xs text-text-muted\">\n OAuth URL not configured. Contact the app developer to complete the setup.\n </p>\n )}\n </div>\n\n {oauth.clientId && (\n <p className=\"mt-3 text-xs text-text-muted\">Client ID: {oauth.clientId}</p>\n )}\n </SectionCard>\n );\n}\n\nfunction PermissionsSection({ installation }: { installation: InstalledApp }) {\n const permissions = getPermissions(installation);\n if (permissions.length === 0) return null;\n\n return (\n <SectionCard>\n <SectionTitle icon={<ShieldCheck className=\"size-5\" />} title=\"Granted Permissions\" />\n <div className=\"flex flex-wrap gap-2\">\n {permissions.map((perm) => (\n <span\n key={perm}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-3 py-1 text-xs font-medium text-text-primary\"\n >\n <Key className=\"size-3 text-text-muted\" />\n {perm}\n </span>\n ))}\n </div>\n <p className=\"mt-3 text-xs text-text-muted\">\n These are the scopes this integration is allowed to access in your workspace.\n </p>\n </SectionCard>\n );\n}\n\nfunction WebhooksSection({ installation }: { installation: InstalledApp }) {\n const webhooks = getWebhooks(installation);\n if (webhooks.length === 0) return null;\n\n return (\n <SectionCard>\n <SectionTitle icon={<Webhook className=\"size-5\" />} title=\"Event Subscriptions\" />\n <ul className=\"space-y-2\">\n {webhooks.map((wh, idx) => (\n <li\n key={idx}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken px-4 py-3\"\n >\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{wh.event}</p>\n {wh.endpoint && (\n <p className=\"mt-0.5 font-mono text-xs text-text-muted\">{wh.endpoint}</p>\n )}\n </div>\n <span\n className={`rounded-full px-2 py-0.5 text-xs font-medium ${\n wh.active !== false\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {wh.active !== false ? 'Active' : 'Inactive'}\n </span>\n </li>\n ))}\n </ul>\n </SectionCard>\n );\n}\n\nfunction InstallationDetailsSection({ installation }: { installation: InstalledApp }) {\n return (\n <SectionCard>\n <SectionTitle icon={<Globe className=\"size-5\" />} title=\"Installation Details\" />\n <div className=\"divide-y divide-border-default\">\n <InfoRow label=\"Product ID\" value={installation.productId} />\n {installation.version && <InfoRow label=\"Version\" value={`v${installation.version}`} />}\n <InfoRow label=\"Status\" value={installation.status} />\n <InfoRow label=\"Workspace\" value={installation.workspaceName ?? installation.workspaceId} />\n <InfoRow label=\"Installed\" value={new Date(installation.installedAt).toLocaleString()} />\n <InfoRow label=\"Last updated\" value={new Date(installation.updatedAt).toLocaleString()} />\n {installation.organizationId && (\n <InfoRow label=\"Organization\" value={installation.organizationId} />\n )}\n <InfoRow label=\"Installed by\" value={installation.installedById} />\n </div>\n </SectionCard>\n );\n}\n\nfunction RawConfigSection({ installation }: { installation: InstalledApp }) {\n const [open, setOpen] = useState(false);\n\n const hasData =\n (installation.manifest && Object.keys(installation.manifest).length > 0) ||\n (installation.metadata && Object.keys(installation.metadata).length > 0) ||\n (installation.context && Object.keys(installation.context).length > 0);\n\n if (!hasData) return null;\n\n return (\n <SectionCard>\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex w-full cursor-pointer items-center justify-between text-left\"\n >\n <span className=\"text-base font-semibold text-text-primary\">Raw configuration</span>\n {open ? (\n <ChevronDown className=\"size-4 text-text-muted\" />\n ) : (\n <ChevronRight className=\"size-4 text-text-muted\" />\n )}\n </button>\n\n {open && (\n <div className=\"mt-4 grid gap-4 lg:grid-cols-2\">\n {installation.manifest && Object.keys(installation.manifest).length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Manifest\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(installation.manifest, null, 2)}\n </pre>\n </div>\n )}\n {(installation.metadata || installation.context) && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Runtime state\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata ?? {},\n context: installation.context ?? {},\n },\n null,\n 2\n )}\n </pre>\n </div>\n )}\n </div>\n )}\n </SectionCard>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Page\n// ---------------------------------------------------------------------------\n\nexport const AppSettingsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appSettings.title', { defaultValue: 'Integration Settings' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.subtitle', {\n defaultValue: 'Manage your connection, permissions and webhook events',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading settings…\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"space-y-4\">\n <ConnectionSection installation={installation} />\n <PermissionsSection installation={installation} />\n <WebhooksSection installation={installation} />\n <InstallationDetailsSection installation={installation} />\n <RawConfigSection installation={installation} />\n\n <div className=\"flex flex-wrap gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {t('pages.appSettings.viewInstallation', { defaultValue: 'View Installation' })}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -4,11 +4,11 @@ import { useInstallations as n } from "../hooks/useInstallations.js";
|
|
|
4
4
|
import { StoreInstallationStatus as r } from "../generated/global-types.js";
|
|
5
5
|
import { useMemo as i, useState as a } from "react";
|
|
6
6
|
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
7
|
-
import { AlertCircle as s, Loader2 as c, Package as l, Plus as u, RefreshCw as d,
|
|
8
|
-
import { jsx as
|
|
9
|
-
import { useNavigate as
|
|
7
|
+
import { AlertCircle as s, Loader2 as c, Package as l, Plus as u, RefreshCw as d, Settings2 as f, Trash2 as p } from "lucide-react";
|
|
8
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
9
|
+
import { useNavigate as g } from "react-router-dom";
|
|
10
10
|
//#region src/pages/InstalledAppsPage.tsx
|
|
11
|
-
var
|
|
11
|
+
var _ = [
|
|
12
12
|
{
|
|
13
13
|
label: "All",
|
|
14
14
|
value: "all"
|
|
@@ -37,185 +37,191 @@ var g = [
|
|
|
37
37
|
label: "Uninstalled",
|
|
38
38
|
value: r.Uninstalled
|
|
39
39
|
}
|
|
40
|
-
],
|
|
41
|
-
let
|
|
40
|
+
], v = (e) => e === r.Active ? "bg-status-success-bg-subtle text-status-success-text" : e === r.Pending || e === r.Installing || e === r.Inactive ? "bg-status-warning-bg-subtle text-status-warning-text" : e === r.Failed ? "bg-status-error-bg-subtle text-status-error-text" : "bg-bg-sunken text-text-muted", y = () => {
|
|
41
|
+
let y = g(), { basePath: b, workspaceId: x } = e(), { t: S } = o(), [C, w] = a("all"), [T, E] = a(null), [D, O] = a(null), { installations: k, totalCount: A, hasMore: j, loading: M, error: N, uninstall: P, uninstallingIds: F, refetch: I, loadMore: L } = n({ filter: i(() => C === "all" ? void 0 : { status: C }, [C]) });
|
|
42
42
|
t({
|
|
43
|
-
variables: { workspaceId:
|
|
44
|
-
skip: !
|
|
43
|
+
variables: { workspaceId: x ?? "" },
|
|
44
|
+
skip: !x,
|
|
45
45
|
onData: () => {
|
|
46
|
-
|
|
46
|
+
I();
|
|
47
47
|
},
|
|
48
48
|
onError: (e) => {
|
|
49
49
|
console.warn("InstallationStatusUpdated subscription error:", e.message);
|
|
50
50
|
}
|
|
51
51
|
});
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
|
|
52
|
+
let R = i(() => [...k].sort((e, t) => t.installedAt.localeCompare(e.installedAt)), [k]), z = () => {
|
|
53
|
+
y(`${b}/marketplace`);
|
|
54
|
+
}, B = async (e) => {
|
|
55
|
+
E(null), await P(e, "User requested uninstall from store UI") || E("Could not uninstall. Try again."), O(null);
|
|
56
56
|
};
|
|
57
|
-
return /* @__PURE__ */
|
|
57
|
+
return /* @__PURE__ */ h("div", {
|
|
58
58
|
className: "flex h-full flex-col",
|
|
59
59
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ m("header", {
|
|
61
61
|
className: "border-b border-border-default bg-bg-surface px-6 py-4",
|
|
62
|
-
children: /* @__PURE__ */
|
|
62
|
+
children: /* @__PURE__ */ h("div", {
|
|
63
63
|
className: "flex items-center justify-between",
|
|
64
|
-
children: [/* @__PURE__ */
|
|
64
|
+
children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
65
65
|
className: "text-2xl font-bold text-text-primary",
|
|
66
|
-
children:
|
|
67
|
-
}), /* @__PURE__ */
|
|
66
|
+
children: S("pages.installed.title", { defaultValue: "Installed Apps" })
|
|
67
|
+
}), /* @__PURE__ */ m("p", {
|
|
68
68
|
className: "text-sm text-text-muted",
|
|
69
69
|
children: "Manage your installed applications"
|
|
70
|
-
})] }), /* @__PURE__ */
|
|
70
|
+
})] }), /* @__PURE__ */ h("button", {
|
|
71
71
|
type: "button",
|
|
72
|
-
onClick:
|
|
72
|
+
onClick: z,
|
|
73
73
|
className: "flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90",
|
|
74
|
-
children: [/* @__PURE__ */
|
|
74
|
+
children: [/* @__PURE__ */ m(u, { className: "size-5" }), /* @__PURE__ */ m("span", { children: S("pages.installed.installNew", { defaultValue: "Install New App" }) })]
|
|
75
75
|
})]
|
|
76
76
|
})
|
|
77
77
|
}),
|
|
78
|
-
/* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ m("main", {
|
|
79
79
|
className: "flex-1 overflow-y-auto p-6",
|
|
80
|
-
children: /* @__PURE__ */
|
|
80
|
+
children: /* @__PURE__ */ h("div", {
|
|
81
81
|
className: "mx-auto max-w-6xl",
|
|
82
82
|
children: [
|
|
83
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ h("div", {
|
|
84
84
|
className: "mb-4 flex flex-wrap items-center gap-2",
|
|
85
|
-
children: [
|
|
85
|
+
children: [_.map((e) => /* @__PURE__ */ m("button", {
|
|
86
86
|
type: "button",
|
|
87
|
-
onClick: () =>
|
|
88
|
-
className: `cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${
|
|
87
|
+
onClick: () => w(e.value),
|
|
88
|
+
className: `cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${C === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-primary hover:bg-bg-surface"}`,
|
|
89
89
|
children: e.label
|
|
90
|
-
}, e.label)), /* @__PURE__ */
|
|
90
|
+
}, e.label)), /* @__PURE__ */ h("button", {
|
|
91
91
|
type: "button",
|
|
92
|
-
onClick: () =>
|
|
92
|
+
onClick: () => I(),
|
|
93
93
|
className: "ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ m(d, { className: "size-4" }), "Refresh"]
|
|
95
95
|
})]
|
|
96
96
|
}),
|
|
97
|
-
|
|
97
|
+
T && /* @__PURE__ */ m("div", {
|
|
98
98
|
role: "alert",
|
|
99
99
|
className: "mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
100
|
-
children:
|
|
100
|
+
children: T
|
|
101
101
|
}),
|
|
102
|
-
|
|
102
|
+
M ? /* @__PURE__ */ m("output", {
|
|
103
103
|
"aria-label": "Loading installed apps",
|
|
104
104
|
className: "flex items-center justify-center py-16",
|
|
105
|
-
children: /* @__PURE__ */
|
|
105
|
+
children: /* @__PURE__ */ m(c, {
|
|
106
106
|
className: "size-8 animate-spin text-text-muted",
|
|
107
107
|
"aria-hidden": "true"
|
|
108
108
|
})
|
|
109
|
-
}) :
|
|
109
|
+
}) : N ? /* @__PURE__ */ h("div", {
|
|
110
110
|
role: "alert",
|
|
111
111
|
className: "flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center",
|
|
112
112
|
children: [
|
|
113
|
-
/* @__PURE__ */
|
|
114
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ m(s, { className: "mb-3 size-10 text-status-error-text" }),
|
|
114
|
+
/* @__PURE__ */ m("h3", {
|
|
115
115
|
className: "text-lg font-semibold text-status-error-text",
|
|
116
116
|
children: "Failed to load installations"
|
|
117
117
|
}),
|
|
118
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ m("p", {
|
|
119
119
|
className: "mt-2 text-sm text-text-muted",
|
|
120
|
-
children:
|
|
120
|
+
children: N.message
|
|
121
121
|
}),
|
|
122
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ m("button", {
|
|
123
123
|
type: "button",
|
|
124
|
-
onClick: () =>
|
|
124
|
+
onClick: () => I(),
|
|
125
125
|
className: "mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
126
126
|
children: "Try Again"
|
|
127
127
|
})
|
|
128
128
|
]
|
|
129
|
-
}) :
|
|
129
|
+
}) : R.length === 0 ? /* @__PURE__ */ m("div", {
|
|
130
130
|
className: "flex items-center justify-center py-16",
|
|
131
|
-
children: /* @__PURE__ */
|
|
131
|
+
children: /* @__PURE__ */ h("div", {
|
|
132
132
|
className: "text-center",
|
|
133
133
|
children: [
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ m(l, { className: "mx-auto mb-4 size-16 text-text-muted" }),
|
|
135
|
+
/* @__PURE__ */ m("h3", {
|
|
136
136
|
className: "mb-2 text-lg font-semibold text-text-primary",
|
|
137
137
|
children: "No apps installed"
|
|
138
138
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ m("p", {
|
|
140
140
|
className: "mb-4 text-sm text-text-muted",
|
|
141
141
|
children: "Browse the marketplace to install your first app"
|
|
142
142
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ m("button", {
|
|
144
144
|
type: "button",
|
|
145
|
-
onClick:
|
|
145
|
+
onClick: z,
|
|
146
146
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90",
|
|
147
147
|
children: "Browse Marketplace"
|
|
148
148
|
})
|
|
149
149
|
]
|
|
150
150
|
})
|
|
151
|
-
}) : /* @__PURE__ */
|
|
151
|
+
}) : /* @__PURE__ */ h("div", {
|
|
152
152
|
className: "flex flex-col gap-y-4",
|
|
153
153
|
children: [
|
|
154
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ h("p", {
|
|
155
155
|
"aria-live": "polite",
|
|
156
156
|
className: "text-sm text-text-muted",
|
|
157
157
|
children: [
|
|
158
|
-
|
|
158
|
+
A,
|
|
159
159
|
" installation",
|
|
160
|
-
|
|
160
|
+
A === 1 ? "" : "s",
|
|
161
161
|
" found"
|
|
162
162
|
]
|
|
163
163
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ m("ul", {
|
|
165
165
|
className: "flex flex-col gap-y-4",
|
|
166
|
-
children:
|
|
167
|
-
let t =
|
|
168
|
-
return /* @__PURE__ */
|
|
166
|
+
children: R.map((e) => {
|
|
167
|
+
let t = F.has(`${e.workspaceId}:${e.productId}`);
|
|
168
|
+
return /* @__PURE__ */ m("li", {
|
|
169
169
|
className: "rounded-xl border border-border-default bg-bg-surface p-4",
|
|
170
|
-
children: /* @__PURE__ */
|
|
170
|
+
children: /* @__PURE__ */ h("div", {
|
|
171
171
|
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ h("div", {
|
|
173
173
|
className: "flex items-start gap-3",
|
|
174
|
-
children: [/* @__PURE__ */
|
|
174
|
+
children: [/* @__PURE__ */ m("div", {
|
|
175
175
|
className: "flex size-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted",
|
|
176
|
-
children: /* @__PURE__ */
|
|
177
|
-
}), /* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
176
|
+
children: /* @__PURE__ */ m(l, { className: "size-6" })
|
|
177
|
+
}), /* @__PURE__ */ h("div", { children: [
|
|
178
|
+
/* @__PURE__ */ m("h3", {
|
|
179
179
|
className: "font-semibold text-text-primary",
|
|
180
180
|
children: e.workspaceName ?? e.workspaceId
|
|
181
181
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ h("p", {
|
|
183
183
|
className: "text-sm text-text-muted",
|
|
184
184
|
children: ["Product: ", e.productId]
|
|
185
185
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ h("div", {
|
|
187
187
|
className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-text-muted",
|
|
188
188
|
children: [
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
className: `rounded-full px-2 py-1 font-medium ${
|
|
189
|
+
/* @__PURE__ */ m("span", {
|
|
190
|
+
className: `rounded-full px-2 py-1 font-medium ${v(e.status)}`,
|
|
191
191
|
children: e.status
|
|
192
192
|
}),
|
|
193
|
-
e.version && /* @__PURE__ */
|
|
194
|
-
e.workspaceType && /* @__PURE__ */
|
|
193
|
+
e.version && /* @__PURE__ */ h("span", { children: ["v", e.version] }),
|
|
194
|
+
e.workspaceType && /* @__PURE__ */ m("span", { children: e.workspaceType })
|
|
195
195
|
]
|
|
196
196
|
})
|
|
197
197
|
] })]
|
|
198
|
-
}), /* @__PURE__ */
|
|
198
|
+
}), /* @__PURE__ */ h("div", {
|
|
199
199
|
className: "flex items-center gap-2",
|
|
200
200
|
children: [
|
|
201
|
-
/* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ m("button", {
|
|
202
202
|
type: "button",
|
|
203
|
-
onClick: () =>
|
|
203
|
+
onClick: () => y(`${b}/installed/${e.id}`),
|
|
204
204
|
className: "cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
205
205
|
children: "Details"
|
|
206
206
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ h("button", {
|
|
208
|
+
type: "button",
|
|
209
|
+
onClick: () => y(`${b}/installed/${e.id}/settings`),
|
|
210
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
211
|
+
children: [/* @__PURE__ */ m(f, { className: "size-4" }), "Configure"]
|
|
212
|
+
}),
|
|
213
|
+
/* @__PURE__ */ m("button", {
|
|
208
214
|
type: "button",
|
|
209
|
-
onClick: () =>
|
|
215
|
+
onClick: () => y(`${b}/marketplace/product/${e.productId}`),
|
|
210
216
|
className: "cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
211
217
|
children: "View Product"
|
|
212
218
|
}),
|
|
213
|
-
e.status !== r.Uninstalled && /* @__PURE__ */
|
|
219
|
+
e.status !== r.Uninstalled && /* @__PURE__ */ h("button", {
|
|
214
220
|
type: "button",
|
|
215
|
-
onClick: () =>
|
|
221
|
+
onClick: () => O(e),
|
|
216
222
|
disabled: t,
|
|
217
223
|
className: "cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60",
|
|
218
|
-
children: [t ? /* @__PURE__ */
|
|
224
|
+
children: [t ? /* @__PURE__ */ m(c, { className: "size-4 animate-spin" }) : /* @__PURE__ */ m(p, { className: "size-4" }), "Uninstall"]
|
|
219
225
|
})
|
|
220
226
|
]
|
|
221
227
|
})]
|
|
@@ -223,14 +229,14 @@ var g = [
|
|
|
223
229
|
}, e.id);
|
|
224
230
|
})
|
|
225
231
|
}),
|
|
226
|
-
|
|
232
|
+
j && /* @__PURE__ */ m("div", {
|
|
227
233
|
className: "flex justify-center pt-6",
|
|
228
|
-
children: /* @__PURE__ */
|
|
234
|
+
children: /* @__PURE__ */ m("button", {
|
|
229
235
|
type: "button",
|
|
230
|
-
onClick:
|
|
231
|
-
disabled:
|
|
236
|
+
onClick: L,
|
|
237
|
+
disabled: M,
|
|
232
238
|
className: "rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50",
|
|
233
|
-
children:
|
|
239
|
+
children: M ? "Loading…" : "Load more"
|
|
234
240
|
})
|
|
235
241
|
})
|
|
236
242
|
]
|
|
@@ -238,53 +244,53 @@ var g = [
|
|
|
238
244
|
]
|
|
239
245
|
})
|
|
240
246
|
}),
|
|
241
|
-
|
|
247
|
+
D && /* @__PURE__ */ h("div", {
|
|
242
248
|
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
243
|
-
children: [/* @__PURE__ */
|
|
249
|
+
children: [/* @__PURE__ */ m("button", {
|
|
244
250
|
type: "button",
|
|
245
251
|
"aria-label": "Cancel uninstall",
|
|
246
252
|
className: "absolute inset-0 bg-bg-overlay/50",
|
|
247
|
-
onClick: () =>
|
|
248
|
-
}), /* @__PURE__ */
|
|
253
|
+
onClick: () => O(null)
|
|
254
|
+
}), /* @__PURE__ */ h("dialog", {
|
|
249
255
|
open: !0,
|
|
250
256
|
"aria-modal": "true",
|
|
251
257
|
"aria-labelledby": "installed-apps-uninstall-title",
|
|
252
258
|
className: "relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl",
|
|
253
259
|
children: [
|
|
254
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ m("h2", {
|
|
255
261
|
id: "installed-apps-uninstall-title",
|
|
256
262
|
className: "text-lg font-semibold text-text-primary",
|
|
257
263
|
children: "Uninstall app"
|
|
258
264
|
}),
|
|
259
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ h("p", {
|
|
260
266
|
className: "mt-2 text-sm text-text-muted",
|
|
261
267
|
children: [
|
|
262
268
|
"Remove product",
|
|
263
269
|
" ",
|
|
264
|
-
/* @__PURE__ */
|
|
270
|
+
/* @__PURE__ */ m("span", {
|
|
265
271
|
className: "font-medium text-text-primary",
|
|
266
|
-
children:
|
|
272
|
+
children: D.productId
|
|
267
273
|
}),
|
|
268
274
|
" ",
|
|
269
275
|
"from workspace",
|
|
270
276
|
" ",
|
|
271
|
-
/* @__PURE__ */
|
|
277
|
+
/* @__PURE__ */ m("span", {
|
|
272
278
|
className: "font-medium text-text-primary",
|
|
273
|
-
children:
|
|
279
|
+
children: D.workspaceName ?? D.workspaceId
|
|
274
280
|
}),
|
|
275
281
|
"?"
|
|
276
282
|
]
|
|
277
283
|
}),
|
|
278
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ h("div", {
|
|
279
285
|
className: "mt-6 flex flex-wrap justify-end gap-3",
|
|
280
|
-
children: [/* @__PURE__ */
|
|
286
|
+
children: [/* @__PURE__ */ m("button", {
|
|
281
287
|
type: "button",
|
|
282
|
-
onClick: () =>
|
|
288
|
+
onClick: () => O(null),
|
|
283
289
|
className: "cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
284
290
|
children: "Cancel"
|
|
285
|
-
}), /* @__PURE__ */
|
|
291
|
+
}), /* @__PURE__ */ m("button", {
|
|
286
292
|
type: "button",
|
|
287
|
-
onClick: () =>
|
|
293
|
+
onClick: () => B(D),
|
|
288
294
|
className: "cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis",
|
|
289
295
|
children: "Uninstall"
|
|
290
296
|
})]
|
|
@@ -296,6 +302,6 @@ var g = [
|
|
|
296
302
|
});
|
|
297
303
|
};
|
|
298
304
|
//#endregion
|
|
299
|
-
export {
|
|
305
|
+
export { y as InstalledAppsPage };
|
|
300
306
|
|
|
301
307
|
//# sourceMappingURL=InstalledAppsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstalledAppsPage.js","names":[],"sources":["../../src/pages/InstalledAppsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Package, Plus, RefreshCw, Trash2, AlertCircle, Loader2 } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { type InstalledApp, useInstallations } from '../hooks/useInstallations';\nimport { StoreInstallationStatus } from '../generated/global-types';\nimport type { InstallationFilterInput } from '../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\n\nconst STATUS_FILTERS: Array<{ label: string; value: StoreInstallationStatus | 'all' }> = [\n { label: 'All', value: 'all' },\n { label: 'Active', value: StoreInstallationStatus.Active },\n { label: 'Inactive', value: StoreInstallationStatus.Inactive },\n { label: 'Pending', value: StoreInstallationStatus.Pending },\n { label: 'Installing', value: StoreInstallationStatus.Installing },\n { label: 'Failed', value: StoreInstallationStatus.Failed },\n { label: 'Uninstalled', value: StoreInstallationStatus.Uninstalled },\n];\n\nconst statusBadgeClass = (status: StoreInstallationStatus): string => {\n if (status === StoreInstallationStatus.Active) {\n return 'bg-status-success-bg-subtle text-status-success-text';\n }\n if (\n status === StoreInstallationStatus.Pending ||\n status === StoreInstallationStatus.Installing ||\n status === StoreInstallationStatus.Inactive\n ) {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === StoreInstallationStatus.Failed) {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\n/**\n * InstalledAppsPage component\n *\n * Displays installed applications for the current actor\n */\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const filter: InstallationFilterInput | undefined = useMemo(\n () => (selectedStatus === 'all' ? undefined : { status: selectedStatus }),\n [selectedStatus]\n );\n\n const {\n installations,\n totalCount,\n hasMore,\n loading,\n error,\n uninstall,\n uninstallingIds,\n refetch,\n loadMore,\n } = useInstallations({ filter });\n\n useInstallationStatusUpdatedSubscription({\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n onData: () => {\n void refetch();\n },\n onError: (err) => {\n console.warn('InstallationStatusUpdated subscription error:', err.message);\n },\n });\n\n const sortedInstallations = useMemo(\n () => [...installations].sort((a, b) => b.installedAt.localeCompare(a.installedAt)),\n [installations]\n );\n\n const handleInstallNew = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n const handleUninstall = async (installation: InstalledApp) => {\n setErrorMessage(null);\n const ok = await uninstall(installation, 'User requested uninstall from store UI');\n if (!ok) {\n setErrorMessage(`Could not uninstall. Try again.`);\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.installed.title', { defaultValue: 'Installed Apps' })}\n </h1>\n <p className=\"text-sm text-text-muted\">Manage your installed applications</p>\n </div>\n\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"size-5\" />\n <span>{t('pages.installed.installNew', { defaultValue: 'Install New App' })}</span>\n </button>\n </div>\n </header>\n\n {/* Content */}\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((filter) => (\n <button\n type=\"button\"\n key={filter.label}\n onClick={() => setSelectedStatus(filter.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === filter.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {filter.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n </div>\n\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {errorMessage}\n </div>\n )}\n\n {loading ? (\n <output\n aria-label=\"Loading installed apps\"\n className=\"flex items-center justify-center py-16\"\n >\n <Loader2 className=\"size-8 animate-spin text-text-muted\" aria-hidden=\"true\" />\n </output>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center\"\n >\n <AlertCircle className=\"mb-3 size-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n Failed to load installations\n </h3>\n <p className=\"mt-2 text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n Try Again\n </button>\n </div>\n ) : sortedInstallations.length === 0 ? (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"text-center\">\n <Package className=\"mx-auto mb-4 size-16 text-text-muted\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">No apps installed</h3>\n <p className=\"mb-4 text-sm text-text-muted\">\n Browse the marketplace to install your first app\n </p>\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n Browse Marketplace\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-y-4\">\n <p aria-live=\"polite\" className=\"text-sm text-text-muted\">\n {totalCount} installation{totalCount === 1 ? '' : 's'} found\n </p>\n <ul className=\"flex flex-col gap-y-4\">\n {sortedInstallations.map((installation) => {\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n return (\n <li\n key={installation.id}\n className=\"rounded-xl border border-border-default bg-bg-surface p-4\"\n >\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h3>\n <p className=\"text-sm text-text-muted\">\n Product: {installation.productId}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span\n className={`rounded-full px-2 py-1 font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.version && <span>v{installation.version}</span>}\n {installation.workspaceType && (\n <span>{installation.workspaceType}</span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Details\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n View Product\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n Uninstall\n </button>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-6\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50\"\n >\n {loading ? 'Loading…' : 'Load more'}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n </main>\n\n {pendingUninstall && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <button\n type=\"button\"\n aria-label=\"Cancel uninstall\"\n className=\"absolute inset-0 bg-bg-overlay/50\"\n onClick={() => setPendingUninstall(null)}\n />\n <dialog\n open\n aria-modal=\"true\"\n aria-labelledby=\"installed-apps-uninstall-title\"\n className=\"relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl\"\n >\n <h2\n id=\"installed-apps-uninstall-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Uninstall app\n </h2>\n <p className=\"mt-2 text-sm text-text-muted\">\n Remove product{' '}\n <span className=\"font-medium text-text-primary\">{pendingUninstall.productId}</span>{' '}\n from workspace{' '}\n <span className=\"font-medium text-text-primary\">\n {pendingUninstall.workspaceName ?? pendingUninstall.workspaceId}\n </span>\n ?\n </p>\n\n <div className=\"mt-6 flex flex-wrap justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(null)}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => handleUninstall(pendingUninstall)}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis\"\n >\n Uninstall\n </button>\n </div>\n </dialog>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InstalledAppsPage.js","names":[],"sources":["../../src/pages/InstalledAppsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Package, Plus, RefreshCw, Trash2, AlertCircle, Loader2, Settings2 } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { type InstalledApp, useInstallations } from '../hooks/useInstallations';\nimport { StoreInstallationStatus } from '../generated/global-types';\nimport type { InstallationFilterInput } from '../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\n\nconst STATUS_FILTERS: Array<{ label: string; value: StoreInstallationStatus | 'all' }> = [\n { label: 'All', value: 'all' },\n { label: 'Active', value: StoreInstallationStatus.Active },\n { label: 'Inactive', value: StoreInstallationStatus.Inactive },\n { label: 'Pending', value: StoreInstallationStatus.Pending },\n { label: 'Installing', value: StoreInstallationStatus.Installing },\n { label: 'Failed', value: StoreInstallationStatus.Failed },\n { label: 'Uninstalled', value: StoreInstallationStatus.Uninstalled },\n];\n\nconst statusBadgeClass = (status: StoreInstallationStatus): string => {\n if (status === StoreInstallationStatus.Active) {\n return 'bg-status-success-bg-subtle text-status-success-text';\n }\n if (\n status === StoreInstallationStatus.Pending ||\n status === StoreInstallationStatus.Installing ||\n status === StoreInstallationStatus.Inactive\n ) {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === StoreInstallationStatus.Failed) {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\n/**\n * InstalledAppsPage component\n *\n * Displays installed applications for the current actor\n */\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const filter: InstallationFilterInput | undefined = useMemo(\n () => (selectedStatus === 'all' ? undefined : { status: selectedStatus }),\n [selectedStatus]\n );\n\n const {\n installations,\n totalCount,\n hasMore,\n loading,\n error,\n uninstall,\n uninstallingIds,\n refetch,\n loadMore,\n } = useInstallations({ filter });\n\n useInstallationStatusUpdatedSubscription({\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n onData: () => {\n void refetch();\n },\n onError: (err) => {\n console.warn('InstallationStatusUpdated subscription error:', err.message);\n },\n });\n\n const sortedInstallations = useMemo(\n () => [...installations].sort((a, b) => b.installedAt.localeCompare(a.installedAt)),\n [installations]\n );\n\n const handleInstallNew = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n const handleUninstall = async (installation: InstalledApp) => {\n setErrorMessage(null);\n const ok = await uninstall(installation, 'User requested uninstall from store UI');\n if (!ok) {\n setErrorMessage(`Could not uninstall. Try again.`);\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.installed.title', { defaultValue: 'Installed Apps' })}\n </h1>\n <p className=\"text-sm text-text-muted\">Manage your installed applications</p>\n </div>\n\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"size-5\" />\n <span>{t('pages.installed.installNew', { defaultValue: 'Install New App' })}</span>\n </button>\n </div>\n </header>\n\n {/* Content */}\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((filter) => (\n <button\n type=\"button\"\n key={filter.label}\n onClick={() => setSelectedStatus(filter.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === filter.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {filter.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n </div>\n\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {errorMessage}\n </div>\n )}\n\n {loading ? (\n <output\n aria-label=\"Loading installed apps\"\n className=\"flex items-center justify-center py-16\"\n >\n <Loader2 className=\"size-8 animate-spin text-text-muted\" aria-hidden=\"true\" />\n </output>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center\"\n >\n <AlertCircle className=\"mb-3 size-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n Failed to load installations\n </h3>\n <p className=\"mt-2 text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n Try Again\n </button>\n </div>\n ) : sortedInstallations.length === 0 ? (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"text-center\">\n <Package className=\"mx-auto mb-4 size-16 text-text-muted\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">No apps installed</h3>\n <p className=\"mb-4 text-sm text-text-muted\">\n Browse the marketplace to install your first app\n </p>\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n Browse Marketplace\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-y-4\">\n <p aria-live=\"polite\" className=\"text-sm text-text-muted\">\n {totalCount} installation{totalCount === 1 ? '' : 's'} found\n </p>\n <ul className=\"flex flex-col gap-y-4\">\n {sortedInstallations.map((installation) => {\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n return (\n <li\n key={installation.id}\n className=\"rounded-xl border border-border-default bg-bg-surface p-4\"\n >\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h3>\n <p className=\"text-sm text-text-muted\">\n Product: {installation.productId}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span\n className={`rounded-full px-2 py-1 font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.version && <span>v{installation.version}</span>}\n {installation.workspaceType && (\n <span>{installation.workspaceType}</span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Details\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/installed/${installation.id}/settings`)\n }\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Settings2 className=\"size-4\" />\n Configure\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n View Product\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n Uninstall\n </button>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-6\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50\"\n >\n {loading ? 'Loading…' : 'Load more'}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n </main>\n\n {pendingUninstall && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <button\n type=\"button\"\n aria-label=\"Cancel uninstall\"\n className=\"absolute inset-0 bg-bg-overlay/50\"\n onClick={() => setPendingUninstall(null)}\n />\n <dialog\n open\n aria-modal=\"true\"\n aria-labelledby=\"installed-apps-uninstall-title\"\n className=\"relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl\"\n >\n <h2\n id=\"installed-apps-uninstall-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Uninstall app\n </h2>\n <p className=\"mt-2 text-sm text-text-muted\">\n Remove product{' '}\n <span className=\"font-medium text-text-primary\">{pendingUninstall.productId}</span>{' '}\n from workspace{' '}\n <span className=\"font-medium text-text-primary\">\n {pendingUninstall.workspaceName ?? pendingUninstall.workspaceId}\n </span>\n ?\n </p>\n\n <div className=\"mt-6 flex flex-wrap justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(null)}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => handleUninstall(pendingUninstall)}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis\"\n >\n Uninstall\n </button>\n </div>\n </dialog>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|