@burdenoff/microfe-channel 2026.625.1 → 2026.625.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +66 -76
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +117 -114
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +159 -171
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +88 -97
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +10 -22
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +131 -128
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,11 +3,11 @@ import { PermissionGuard as t } from "../../../components/PermissionGuard.js";
|
|
|
3
3
|
import { CHANNEL_PERMISSIONS as n } from "../../../constants/permissions.js";
|
|
4
4
|
import { useEffect as r, useState as i } from "react";
|
|
5
5
|
import { useI18n as a } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
-
import { AlertCircle as o, ArrowLeft as s, CheckCircle2 as c,
|
|
7
|
-
import { useNavigate as
|
|
8
|
-
import { usePermissions as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import { GlassCard as b } from "@burdenoff/fe-libs/ui";
|
|
6
|
+
import { AlertCircle as o, ArrowLeft as s, CheckCircle2 as c, Loader2 as l, Plus as u, RefreshCw as d, Settings as f, XCircle as p } from "lucide-react";
|
|
7
|
+
import { useNavigate as m } from "react-router-dom";
|
|
8
|
+
import { usePermissions as h } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
9
|
+
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { GlassCard as y, IllustratedEmptyState as b } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
import { TagBadge as x } from "@burdenoff/fe-libs/tag";
|
|
12
12
|
//#region src/channel/modules/channels/pages/ConnectionsListPage.tsx
|
|
13
13
|
var S = {
|
|
@@ -17,13 +17,13 @@ var S = {
|
|
|
17
17
|
label: "Healthy"
|
|
18
18
|
},
|
|
19
19
|
false: {
|
|
20
|
-
icon:
|
|
20
|
+
icon: p,
|
|
21
21
|
color: "text-status-error-text",
|
|
22
22
|
label: "Unhealthy"
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
function C() {
|
|
26
|
-
let c =
|
|
26
|
+
let c = m(), { hasPermission: p } = h(), C = p(n.CHANNEL_CONNECTION_CREATE), { channels: w, isLoading: T, error: E, refetch: D } = e();
|
|
27
27
|
r(() => {
|
|
28
28
|
let e = setInterval(() => {
|
|
29
29
|
D();
|
|
@@ -50,52 +50,52 @@ function C() {
|
|
|
50
50
|
tagAssignments: n.tagAssignments ?? []
|
|
51
51
|
}), e;
|
|
52
52
|
}, []), L = [...new Set(I.map((e) => e.platform))], R = I.filter((e) => !(A === "healthy" && !e.isHealthy || A === "unhealthy" && e.isHealthy || M !== "all" && e.platform !== M)), z = Math.max(1, Math.ceil(R.length / 10)), B = R.slice(P * 10, (P + 1) * 10);
|
|
53
|
-
return T ? /* @__PURE__ */
|
|
53
|
+
return T ? /* @__PURE__ */ _("div", {
|
|
54
54
|
className: "flex items-center justify-center min-h-screen",
|
|
55
|
-
children: /* @__PURE__ */
|
|
56
|
-
}) : E ? /* @__PURE__ */
|
|
55
|
+
children: /* @__PURE__ */ _(l, { className: "size-8 animate-spin text-text-secondary" })
|
|
56
|
+
}) : E ? /* @__PURE__ */ v("div", {
|
|
57
57
|
className: "flex flex-col items-center justify-center min-h-screen gap-4",
|
|
58
58
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ _(o, { className: "size-12 text-status-error-text" }),
|
|
60
|
+
/* @__PURE__ */ _("h1", {
|
|
61
61
|
className: "text-xl font-semibold",
|
|
62
62
|
children: k("channel.loadConnectionsFailed", "Failed to load connections")
|
|
63
63
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ _("p", {
|
|
65
65
|
className: "text-sm text-text-secondary",
|
|
66
66
|
children: E
|
|
67
67
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ _("button", {
|
|
69
69
|
type: "button",
|
|
70
70
|
onClick: () => D(),
|
|
71
71
|
className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
|
|
72
72
|
children: k("channel.retry", "Retry")
|
|
73
73
|
})
|
|
74
74
|
]
|
|
75
|
-
}) : /* @__PURE__ */
|
|
75
|
+
}) : /* @__PURE__ */ _(t, {
|
|
76
76
|
permission: n.CHANNEL_CONNECTION_READ,
|
|
77
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: /* @__PURE__ */ v("div", {
|
|
78
78
|
className: "min-h-screen bg-bg-surface",
|
|
79
79
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ _("div", {
|
|
81
81
|
className: "border-b bg-bg-surface",
|
|
82
82
|
"data-tour": "channels-connections-header",
|
|
83
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ _("div", {
|
|
84
84
|
className: "container mx-auto px-6 py-4",
|
|
85
|
-
children: /* @__PURE__ */
|
|
85
|
+
children: /* @__PURE__ */ v("div", {
|
|
86
86
|
className: "flex items-center justify-between",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
87
|
+
children: [/* @__PURE__ */ v("div", {
|
|
88
88
|
className: "flex items-center gap-4",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
89
|
+
children: [/* @__PURE__ */ _("button", {
|
|
90
90
|
type: "button",
|
|
91
91
|
onClick: () => c("./list"),
|
|
92
92
|
"aria-label": k("channel.backToChannels", "Back to channels"),
|
|
93
93
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
94
|
-
children: /* @__PURE__ */
|
|
95
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ _(s, { className: "size-5 " })
|
|
95
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
|
|
96
96
|
className: "text-2xl font-bold",
|
|
97
97
|
children: k("channel.connections", "Connections")
|
|
98
|
-
}), /* @__PURE__ */
|
|
98
|
+
}), /* @__PURE__ */ v("p", {
|
|
99
99
|
className: "text-sm text-text-secondary",
|
|
100
100
|
children: [
|
|
101
101
|
R.length,
|
|
@@ -103,58 +103,58 @@ function C() {
|
|
|
103
103
|
R.length === 1 ? "" : "s"
|
|
104
104
|
]
|
|
105
105
|
})] })]
|
|
106
|
-
}), C && /* @__PURE__ */
|
|
106
|
+
}), C && /* @__PURE__ */ v("button", {
|
|
107
107
|
type: "button",
|
|
108
108
|
onClick: () => c("./create"),
|
|
109
109
|
className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors flex items-center gap-2",
|
|
110
|
-
children: [/* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ _(u, { className: "size-4 " }), k("channel.newConnection", "New Connection")]
|
|
111
111
|
})]
|
|
112
112
|
})
|
|
113
113
|
})
|
|
114
114
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ _("div", {
|
|
116
116
|
className: "container mx-auto px-6 py-4",
|
|
117
117
|
"data-tour": "channels-connections-filters",
|
|
118
|
-
children: /* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ v("div", {
|
|
119
119
|
className: "flex items-center gap-4",
|
|
120
120
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ v("select", {
|
|
122
122
|
value: M,
|
|
123
123
|
onChange: (e) => N(e.target.value),
|
|
124
124
|
className: "px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
|
|
125
|
-
children: [/* @__PURE__ */
|
|
125
|
+
children: [/* @__PURE__ */ _("option", {
|
|
126
126
|
value: "all",
|
|
127
127
|
children: k("channel.allPlatforms", "All Platforms")
|
|
128
|
-
}), L.map((e) => /* @__PURE__ */
|
|
128
|
+
}), L.map((e) => /* @__PURE__ */ _("option", {
|
|
129
129
|
value: e,
|
|
130
130
|
children: e
|
|
131
131
|
}, e))]
|
|
132
132
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
133
|
+
/* @__PURE__ */ v("select", {
|
|
134
134
|
value: A,
|
|
135
135
|
onChange: (e) => j(e.target.value),
|
|
136
136
|
className: "px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
|
|
137
137
|
children: [
|
|
138
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ _("option", {
|
|
139
139
|
value: "all",
|
|
140
140
|
children: k("channel.allStatus", "All Status")
|
|
141
141
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ _("option", {
|
|
143
143
|
value: "healthy",
|
|
144
144
|
children: k("channel.healthy", "Healthy")
|
|
145
145
|
}),
|
|
146
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ _("option", {
|
|
147
147
|
value: "unhealthy",
|
|
148
148
|
children: k("channel.unhealthy", "Unhealthy")
|
|
149
149
|
})
|
|
150
150
|
]
|
|
151
151
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
152
|
+
/* @__PURE__ */ _("button", {
|
|
153
153
|
type: "button",
|
|
154
154
|
onClick: () => D(),
|
|
155
155
|
"aria-label": k("channel.refresh", "Refresh"),
|
|
156
156
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
157
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ _(d, {
|
|
158
158
|
className: "size-5 ",
|
|
159
159
|
"aria-hidden": "true"
|
|
160
160
|
})
|
|
@@ -162,40 +162,31 @@ function C() {
|
|
|
162
162
|
]
|
|
163
163
|
})
|
|
164
164
|
}),
|
|
165
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ _("div", {
|
|
166
166
|
className: "container mx-auto px-6 pb-8",
|
|
167
|
-
children: R.length === 0 ? /* @__PURE__ */
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
type: "button",
|
|
186
|
-
onClick: () => {
|
|
187
|
-
j("all"), N("all");
|
|
188
|
-
},
|
|
189
|
-
className: "px-4 py-2 rounded-lg border hover:bg-bg-sunken",
|
|
190
|
-
children: k("channel.clearFilters", "Clear Filters")
|
|
191
|
-
})
|
|
192
|
-
]
|
|
193
|
-
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(b, {
|
|
167
|
+
children: R.length === 0 ? /* @__PURE__ */ _(b, {
|
|
168
|
+
illustration: w.length === 0 ? "empty-data" : "empty-search",
|
|
169
|
+
title: k("channel.noConnectionsFound", "No connections found"),
|
|
170
|
+
description: w.length === 0 ? k("channel.createChannelFirst", "Create a channel first to add connections") : k("channel.noConnectionsMatchFilter", "No connections match your filters"),
|
|
171
|
+
action: w.length === 0 ? C ? /* @__PURE__ */ _("button", {
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: () => c("./create"),
|
|
174
|
+
className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
|
|
175
|
+
children: k("channel.createConnection", "Create Connection")
|
|
176
|
+
}) : void 0 : /* @__PURE__ */ _("button", {
|
|
177
|
+
type: "button",
|
|
178
|
+
onClick: () => {
|
|
179
|
+
j("all"), N("all");
|
|
180
|
+
},
|
|
181
|
+
className: "px-4 py-2 rounded-lg border hover:bg-bg-sunken",
|
|
182
|
+
children: k("channel.clearFilters", "Clear Filters")
|
|
183
|
+
})
|
|
184
|
+
}) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(y, {
|
|
194
185
|
treatment: "plain",
|
|
195
186
|
className: "border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden",
|
|
196
187
|
children: B.map((e) => {
|
|
197
188
|
let t = S[String(e.isHealthy)] || S.false, n = t.icon;
|
|
198
|
-
return /* @__PURE__ */
|
|
189
|
+
return /* @__PURE__ */ _("div", {
|
|
199
190
|
role: "button",
|
|
200
191
|
tabIndex: 0,
|
|
201
192
|
className: "p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
|
|
@@ -203,21 +194,21 @@ function C() {
|
|
|
203
194
|
onKeyDown: (t) => {
|
|
204
195
|
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), c(`./${e.channelId}`));
|
|
205
196
|
},
|
|
206
|
-
children: /* @__PURE__ */
|
|
197
|
+
children: /* @__PURE__ */ v("div", {
|
|
207
198
|
className: "flex items-center justify-between",
|
|
208
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ v("div", {
|
|
209
200
|
className: "flex items-center gap-4",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
211
|
-
className: t.color
|
|
201
|
+
children: [/* @__PURE__ */ _("div", {
|
|
202
|
+
className: `${t.color} ${e.isHealthy ? "motion-safe:animate-vc-glow-pulse" : ""}`,
|
|
212
203
|
"aria-label": t.label,
|
|
213
|
-
children: /* @__PURE__ */
|
|
204
|
+
children: /* @__PURE__ */ _(n, {
|
|
214
205
|
className: "size-5 ",
|
|
215
206
|
"aria-hidden": "true"
|
|
216
207
|
})
|
|
217
|
-
}), /* @__PURE__ */
|
|
208
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
218
209
|
className: "font-medium",
|
|
219
210
|
children: e.channelName
|
|
220
|
-
}), /* @__PURE__ */
|
|
211
|
+
}), /* @__PURE__ */ v("p", {
|
|
221
212
|
className: "text-sm text-text-secondary",
|
|
222
213
|
children: [
|
|
223
214
|
e.platform,
|
|
@@ -227,75 +218,75 @@ function C() {
|
|
|
227
218
|
e.authType
|
|
228
219
|
]
|
|
229
220
|
})] })]
|
|
230
|
-
}), /* @__PURE__ */
|
|
221
|
+
}), /* @__PURE__ */ v("div", {
|
|
231
222
|
className: "flex items-center gap-4",
|
|
232
223
|
children: [
|
|
233
|
-
/* @__PURE__ */
|
|
224
|
+
/* @__PURE__ */ v("div", {
|
|
234
225
|
className: "text-right",
|
|
235
|
-
children: [/* @__PURE__ */
|
|
226
|
+
children: [/* @__PURE__ */ _("p", {
|
|
236
227
|
className: "text-sm",
|
|
237
|
-
children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */
|
|
228
|
+
children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ _("span", {
|
|
238
229
|
className: "text-status-success-text",
|
|
239
230
|
children: k("channel.activeStatus", "Active")
|
|
240
|
-
}) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */
|
|
231
|
+
}) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ _("span", {
|
|
241
232
|
className: "text-text-secondary",
|
|
242
233
|
children: k("channel.inactiveStatus", "Inactive")
|
|
243
|
-
}) : e.channelStatus === "ERROR" ? /* @__PURE__ */
|
|
234
|
+
}) : e.channelStatus === "ERROR" ? /* @__PURE__ */ _("span", {
|
|
244
235
|
className: "text-status-error-text",
|
|
245
236
|
children: k("channel.errorStatus", "Error")
|
|
246
|
-
}) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */
|
|
237
|
+
}) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ _("span", {
|
|
247
238
|
className: "text-status-warning-text",
|
|
248
239
|
children: k("channel.suspendedStatus", "Suspended")
|
|
249
|
-
}) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */
|
|
240
|
+
}) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ _("span", {
|
|
250
241
|
className: "text-text-secondary",
|
|
251
242
|
children: k("channel.archivedStatus", "Archived")
|
|
252
|
-
}) : /* @__PURE__ */
|
|
243
|
+
}) : /* @__PURE__ */ _("span", {
|
|
253
244
|
className: "text-text-secondary",
|
|
254
245
|
children: k("channel.inactiveStatus", "Inactive")
|
|
255
246
|
})
|
|
256
|
-
}), e.lastTestedAt && /* @__PURE__ */
|
|
247
|
+
}), e.lastTestedAt && /* @__PURE__ */ v("p", {
|
|
257
248
|
className: "text-xs text-text-secondary",
|
|
258
249
|
children: ["Last tested: ", new Date(e.lastTestedAt).toLocaleDateString()]
|
|
259
250
|
})]
|
|
260
251
|
}),
|
|
261
|
-
e.needsTokenRefresh && /* @__PURE__ */
|
|
252
|
+
e.needsTokenRefresh && /* @__PURE__ */ _("span", {
|
|
262
253
|
className: "px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded",
|
|
263
254
|
children: "Token Expires"
|
|
264
255
|
}),
|
|
265
|
-
e.tagAssignments.length > 0 && /* @__PURE__ */
|
|
256
|
+
e.tagAssignments.length > 0 && /* @__PURE__ */ v("div", {
|
|
266
257
|
className: "flex flex-wrap gap-1",
|
|
267
258
|
"aria-label": "Connection tags",
|
|
268
|
-
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */
|
|
259
|
+
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ _(x, {
|
|
269
260
|
id: e.tagId,
|
|
270
261
|
label: e.tagId,
|
|
271
262
|
size: "sm"
|
|
272
|
-
}, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */
|
|
263
|
+
}, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */ v("span", {
|
|
273
264
|
className: "text-xs text-text-secondary",
|
|
274
265
|
children: ["+", e.tagAssignments.length - 3]
|
|
275
266
|
})]
|
|
276
267
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
268
|
+
/* @__PURE__ */ _("button", {
|
|
278
269
|
type: "button",
|
|
279
270
|
onClick: (t) => {
|
|
280
271
|
t.stopPropagation(), c(`/channels/${e.channelId}`);
|
|
281
272
|
},
|
|
282
273
|
"aria-label": k("channel.viewChannelSettings", "View channel settings"),
|
|
283
274
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
284
|
-
children: /* @__PURE__ */
|
|
275
|
+
children: /* @__PURE__ */ _(f, { className: "size-4 " })
|
|
285
276
|
})
|
|
286
277
|
]
|
|
287
278
|
})]
|
|
288
279
|
})
|
|
289
280
|
}, e.connectionId);
|
|
290
281
|
})
|
|
291
|
-
}), R.length > 10 && /* @__PURE__ */
|
|
282
|
+
}), R.length > 10 && /* @__PURE__ */ v("div", {
|
|
292
283
|
className: "flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3",
|
|
293
|
-
children: [/* @__PURE__ */
|
|
284
|
+
children: [/* @__PURE__ */ v("p", {
|
|
294
285
|
className: "text-sm text-text-secondary",
|
|
295
286
|
children: [
|
|
296
287
|
k("channel.showing", "Showing"),
|
|
297
288
|
" ",
|
|
298
|
-
/* @__PURE__ */
|
|
289
|
+
/* @__PURE__ */ v("span", {
|
|
299
290
|
className: "font-medium",
|
|
300
291
|
children: [
|
|
301
292
|
P * 10 + 1,
|
|
@@ -306,22 +297,22 @@ function C() {
|
|
|
306
297
|
" ",
|
|
307
298
|
k("channel.of", "of"),
|
|
308
299
|
" ",
|
|
309
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ _("span", {
|
|
310
301
|
className: "font-medium",
|
|
311
302
|
children: R.length
|
|
312
303
|
})
|
|
313
304
|
]
|
|
314
|
-
}), /* @__PURE__ */
|
|
305
|
+
}), /* @__PURE__ */ v("div", {
|
|
315
306
|
className: "flex items-center gap-2",
|
|
316
307
|
children: [
|
|
317
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ _("button", {
|
|
318
309
|
type: "button",
|
|
319
310
|
onClick: () => F((e) => Math.max(0, e - 1)),
|
|
320
311
|
disabled: P === 0,
|
|
321
312
|
className: "px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
|
|
322
313
|
children: k("channel.previous", "Previous")
|
|
323
314
|
}),
|
|
324
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ v("span", {
|
|
325
316
|
className: "text-sm text-text-secondary",
|
|
326
317
|
children: [
|
|
327
318
|
P + 1,
|
|
@@ -329,7 +320,7 @@ function C() {
|
|
|
329
320
|
z
|
|
330
321
|
]
|
|
331
322
|
}),
|
|
332
|
-
/* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ _("button", {
|
|
333
324
|
type: "button",
|
|
334
325
|
onClick: () => F((e) => Math.min(z - 1, e + 1)),
|
|
335
326
|
disabled: P >= z - 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n *\n * Displays all connections across channels with their status.\n */\n\nimport { useState, useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ArrowLeft,\n Plus,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n RefreshCw,\n Settings,\n Link,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { useChannels } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst HEALTH_CONFIG: Record<string, { icon: typeof CheckCircle2; color: string; label: string }> = {\n true: { icon: CheckCircle2, color: 'text-status-success-text', label: 'Healthy' },\n false: { icon: XCircle, color: 'text-status-error-text', label: 'Unhealthy' },\n};\n\nexport function ConnectionsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Creating connections is admin-only (channel-connection:create) — hide otherwise.\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const { channels, isLoading, error, refetch } = useChannels();\n\n // Poll for connection status updates every 60 s.\n // connectionStatusChanged subscription is scoped to a single channelId;\n // this page shows all connections, so polling is used instead.\n useEffect(() => {\n const timer = setInterval(() => {\n void refetch();\n }, 60_000);\n return () => clearInterval(timer);\n }, [refetch]);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Filter state\n const [healthFilter, setHealthFilter] = useState<string>('all');\n const [platformFilter, setPlatformFilter] = useState<string>('all');\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n\n // Get all connections from channels (single-pass; avoids filter().map() double traversal)\n const connections = channels.reduce<\n Array<{\n channelId: string;\n channelName: string;\n platform: (typeof channels)[number]['platform'];\n adapterType: (typeof channels)[number]['adapterType'];\n channelStatus: (typeof channels)[number]['status'];\n connectionId: string;\n isActive: boolean;\n isHealthy: boolean;\n needsTokenRefresh: boolean;\n lastTestedAt: string | null | undefined;\n authType: NonNullable<(typeof channels)[number]['connection']>['authType'];\n tagAssignments: NonNullable<\n NonNullable<(typeof channels)[number]['connection']>['tagAssignments']\n >;\n }>\n >((acc, ch) => {\n const conn = ch.connection;\n if (!conn) return acc;\n acc.push({\n channelId: ch.id,\n channelName: ch.name,\n platform: ch.platform,\n adapterType: ch.adapterType,\n channelStatus: ch.status,\n connectionId: conn.id,\n isActive: conn.isActive,\n isHealthy: conn.isHealthy,\n needsTokenRefresh: conn.needsTokenRefresh,\n lastTestedAt: conn.lastTestedAt,\n authType: conn.authType,\n tagAssignments: conn.tagAssignments ?? [],\n });\n return acc;\n }, []);\n\n // Get unique platforms\n const platforms = [...new Set(connections.map((c) => c.platform))];\n\n // Apply filters\n const filteredConnections = connections.filter((conn) => {\n if (healthFilter === 'healthy' && !conn.isHealthy) return false;\n if (healthFilter === 'unhealthy' && conn.isHealthy) return false;\n if (platformFilter !== 'all' && conn.platform !== platformFilter) return false;\n return true;\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredConnections.length / pageSize));\n const pagedConnections = filteredConnections.slice(\n currentPage * pageSize,\n (currentPage + 1) * pageSize\n );\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 className=\"size-8 animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-screen gap-4\">\n <AlertCircle className=\"size-12 text-status-error-text\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{error}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_READ}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\" data-tour=\"channels-connections-header\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n aria-label={tr('channel.backToChannels', 'Back to channels')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('channel.connections', 'Connections')}</h1>\n <p className=\"text-sm text-text-secondary\">\n {filteredConnections.length} connection\n {filteredConnections.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {canConnectionCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors flex items-center gap-2\"\n >\n <Plus className=\"size-4 \" />\n {tr('channel.newConnection', 'New Connection')}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"container mx-auto px-6 py-4\" data-tour=\"channels-connections-filters\">\n <div className=\"flex items-center gap-4\">\n <select\n value={platformFilter}\n onChange={(e) => setPlatformFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"all\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {platforms.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n\n <select\n value={healthFilter}\n onChange={(e) => setHealthFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"all\">{tr('channel.allStatus', 'All Status')}</option>\n <option value=\"healthy\">{tr('channel.healthy', 'Healthy')}</option>\n <option value=\"unhealthy\">{tr('channel.unhealthy', 'Unhealthy')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n aria-label={tr('channel.refresh', 'Refresh')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <RefreshCw className=\"size-5 \" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Connections List */}\n <div className=\"container mx-auto px-6 pb-8\">\n {filteredConnections.length === 0 ? (\n <div className=\"text-center py-12\">\n <Link className=\"size-12 mx-auto text-text-secondary mb-4\" />\n <h2 className=\"text-lg font-semibold mb-2\">\n {tr('channel.noConnectionsFound', 'No connections found')}\n </h2>\n <p className=\"text-sm text-text-secondary mb-4\">\n {channels.length === 0\n ? tr('channel.createChannelFirst', 'Create a channel first to add connections')\n : tr('channel.noConnectionsMatchFilter', 'No connections match your filters')}\n </p>\n {channels.length === 0 ? (\n canConnectionCreate ? (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.createConnection', 'Create Connection')}\n </button>\n ) : null\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setHealthFilter('all');\n setPlatformFilter('all');\n }}\n className=\"px-4 py-2 rounded-lg border hover:bg-bg-sunken\"\n >\n {tr('channel.clearFilters', 'Clear Filters')}\n </button>\n )}\n </div>\n ) : (\n <>\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden\"\n >\n {pagedConnections.map((conn) => {\n const health = HEALTH_CONFIG[String(conn.isHealthy)] || HEALTH_CONFIG.false;\n const HealthIcon = health.icon;\n\n return (\n <div\n key={conn.connectionId}\n role=\"button\"\n tabIndex={0}\n className=\"p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n onClick={() => navigate(`./${conn.channelId}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`./${conn.channelId}`);\n }\n }}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <div className={health.color} aria-label={health.label}>\n <HealthIcon className=\"size-5 \" aria-hidden=\"true\" />\n </div>\n <div>\n <p className=\"font-medium\">{conn.channelName}</p>\n <p className=\"text-sm text-text-secondary\">\n {conn.platform} • {conn.adapterType} • {conn.authType}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm\">\n {conn.channelStatus === 'ACTIVE' ? (\n <span className=\"text-status-success-text\">\n {tr('channel.activeStatus', 'Active')}\n </span>\n ) : conn.channelStatus === 'INACTIVE' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n ) : conn.channelStatus === 'ERROR' ? (\n <span className=\"text-status-error-text\">\n {tr('channel.errorStatus', 'Error')}\n </span>\n ) : conn.channelStatus === 'SUSPENDED' ? (\n <span className=\"text-status-warning-text\">\n {tr('channel.suspendedStatus', 'Suspended')}\n </span>\n ) : conn.channelStatus === 'ARCHIVED' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last tested: {new Date(conn.lastTestedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n\n {conn.needsTokenRefresh && (\n <span className=\"px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded\">\n Token Expires\n </span>\n )}\n\n {conn.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1\" aria-label=\"Connection tags\">\n {conn.tagAssignments.slice(0, 3).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {conn.tagAssignments.length > 3 && (\n <span className=\"text-xs text-text-secondary\">\n +{conn.tagAssignments.length - 3}\n </span>\n )}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`/channels/${conn.channelId}`);\n }}\n aria-label={tr('channel.viewChannelSettings', 'View channel settings')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <Settings className=\"size-4 \" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </GlassCard>\n {/* Pagination */}\n {filteredConnections.length > pageSize && (\n <div className=\"flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, filteredConnections.length)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium\">{filteredConnections.length}</span>\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {currentPage + 1} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={currentPage >= totalPages - 1}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n *\n * Displays all connections across channels with their status.\n */\n\nimport { useState, useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ArrowLeft,\n Plus,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n RefreshCw,\n Settings,\n} from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useChannels } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst HEALTH_CONFIG: Record<string, { icon: typeof CheckCircle2; color: string; label: string }> = {\n true: { icon: CheckCircle2, color: 'text-status-success-text', label: 'Healthy' },\n false: { icon: XCircle, color: 'text-status-error-text', label: 'Unhealthy' },\n};\n\nexport function ConnectionsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Creating connections is admin-only (channel-connection:create) — hide otherwise.\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const { channels, isLoading, error, refetch } = useChannels();\n\n // Poll for connection status updates every 60 s.\n // connectionStatusChanged subscription is scoped to a single channelId;\n // this page shows all connections, so polling is used instead.\n useEffect(() => {\n const timer = setInterval(() => {\n void refetch();\n }, 60_000);\n return () => clearInterval(timer);\n }, [refetch]);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Filter state\n const [healthFilter, setHealthFilter] = useState<string>('all');\n const [platformFilter, setPlatformFilter] = useState<string>('all');\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n\n // Get all connections from channels (single-pass; avoids filter().map() double traversal)\n const connections = channels.reduce<\n Array<{\n channelId: string;\n channelName: string;\n platform: (typeof channels)[number]['platform'];\n adapterType: (typeof channels)[number]['adapterType'];\n channelStatus: (typeof channels)[number]['status'];\n connectionId: string;\n isActive: boolean;\n isHealthy: boolean;\n needsTokenRefresh: boolean;\n lastTestedAt: string | null | undefined;\n authType: NonNullable<(typeof channels)[number]['connection']>['authType'];\n tagAssignments: NonNullable<\n NonNullable<(typeof channels)[number]['connection']>['tagAssignments']\n >;\n }>\n >((acc, ch) => {\n const conn = ch.connection;\n if (!conn) return acc;\n acc.push({\n channelId: ch.id,\n channelName: ch.name,\n platform: ch.platform,\n adapterType: ch.adapterType,\n channelStatus: ch.status,\n connectionId: conn.id,\n isActive: conn.isActive,\n isHealthy: conn.isHealthy,\n needsTokenRefresh: conn.needsTokenRefresh,\n lastTestedAt: conn.lastTestedAt,\n authType: conn.authType,\n tagAssignments: conn.tagAssignments ?? [],\n });\n return acc;\n }, []);\n\n // Get unique platforms\n const platforms = [...new Set(connections.map((c) => c.platform))];\n\n // Apply filters\n const filteredConnections = connections.filter((conn) => {\n if (healthFilter === 'healthy' && !conn.isHealthy) return false;\n if (healthFilter === 'unhealthy' && conn.isHealthy) return false;\n if (platformFilter !== 'all' && conn.platform !== platformFilter) return false;\n return true;\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredConnections.length / pageSize));\n const pagedConnections = filteredConnections.slice(\n currentPage * pageSize,\n (currentPage + 1) * pageSize\n );\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 className=\"size-8 animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-screen gap-4\">\n <AlertCircle className=\"size-12 text-status-error-text\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{error}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_READ}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\" data-tour=\"channels-connections-header\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n aria-label={tr('channel.backToChannels', 'Back to channels')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('channel.connections', 'Connections')}</h1>\n <p className=\"text-sm text-text-secondary\">\n {filteredConnections.length} connection\n {filteredConnections.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {canConnectionCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors flex items-center gap-2\"\n >\n <Plus className=\"size-4 \" />\n {tr('channel.newConnection', 'New Connection')}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"container mx-auto px-6 py-4\" data-tour=\"channels-connections-filters\">\n <div className=\"flex items-center gap-4\">\n <select\n value={platformFilter}\n onChange={(e) => setPlatformFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"all\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {platforms.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n\n <select\n value={healthFilter}\n onChange={(e) => setHealthFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"all\">{tr('channel.allStatus', 'All Status')}</option>\n <option value=\"healthy\">{tr('channel.healthy', 'Healthy')}</option>\n <option value=\"unhealthy\">{tr('channel.unhealthy', 'Unhealthy')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n aria-label={tr('channel.refresh', 'Refresh')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <RefreshCw className=\"size-5 \" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Connections List */}\n <div className=\"container mx-auto px-6 pb-8\">\n {filteredConnections.length === 0 ? (\n <IllustratedEmptyState\n illustration={channels.length === 0 ? 'empty-data' : 'empty-search'}\n title={tr('channel.noConnectionsFound', 'No connections found')}\n description={\n channels.length === 0\n ? tr('channel.createChannelFirst', 'Create a channel first to add connections')\n : tr('channel.noConnectionsMatchFilter', 'No connections match your filters')\n }\n action={\n channels.length === 0 ? (\n canConnectionCreate ? (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.createConnection', 'Create Connection')}\n </button>\n ) : undefined\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setHealthFilter('all');\n setPlatformFilter('all');\n }}\n className=\"px-4 py-2 rounded-lg border hover:bg-bg-sunken\"\n >\n {tr('channel.clearFilters', 'Clear Filters')}\n </button>\n )\n }\n />\n ) : (\n <>\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden\"\n >\n {pagedConnections.map((conn) => {\n const health = HEALTH_CONFIG[String(conn.isHealthy)] || HEALTH_CONFIG.false;\n const HealthIcon = health.icon;\n\n return (\n <div\n key={conn.connectionId}\n role=\"button\"\n tabIndex={0}\n className=\"p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n onClick={() => navigate(`./${conn.channelId}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`./${conn.channelId}`);\n }\n }}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <div\n className={`${health.color} ${\n conn.isHealthy ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n aria-label={health.label}\n >\n <HealthIcon className=\"size-5 \" aria-hidden=\"true\" />\n </div>\n <div>\n <p className=\"font-medium\">{conn.channelName}</p>\n <p className=\"text-sm text-text-secondary\">\n {conn.platform} • {conn.adapterType} • {conn.authType}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm\">\n {conn.channelStatus === 'ACTIVE' ? (\n <span className=\"text-status-success-text\">\n {tr('channel.activeStatus', 'Active')}\n </span>\n ) : conn.channelStatus === 'INACTIVE' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n ) : conn.channelStatus === 'ERROR' ? (\n <span className=\"text-status-error-text\">\n {tr('channel.errorStatus', 'Error')}\n </span>\n ) : conn.channelStatus === 'SUSPENDED' ? (\n <span className=\"text-status-warning-text\">\n {tr('channel.suspendedStatus', 'Suspended')}\n </span>\n ) : conn.channelStatus === 'ARCHIVED' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last tested: {new Date(conn.lastTestedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n\n {conn.needsTokenRefresh && (\n <span className=\"px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded\">\n Token Expires\n </span>\n )}\n\n {conn.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1\" aria-label=\"Connection tags\">\n {conn.tagAssignments.slice(0, 3).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {conn.tagAssignments.length > 3 && (\n <span className=\"text-xs text-text-secondary\">\n +{conn.tagAssignments.length - 3}\n </span>\n )}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`/channels/${conn.channelId}`);\n }}\n aria-label={tr('channel.viewChannelSettings', 'View channel settings')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <Settings className=\"size-4 \" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </GlassCard>\n {/* Pagination */}\n {filteredConnections.length > pageSize && (\n <div className=\"flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, filteredConnections.length)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium\">{filteredConnections.length}</span>\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {currentPage + 1} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={currentPage >= totalPages - 1}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import { AlertCircle as u, ArrowLeft as ee, CheckCircle2 as d, ChevronLeft as te
|
|
|
9
9
|
import { useNavigate as ne, useParams as re } from "react-router-dom";
|
|
10
10
|
import { usePermissions as b } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
11
11
|
import { Fragment as ie, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
12
|
-
import { CTAOverflowMenu as
|
|
12
|
+
import { CTAOverflowMenu as ae, GlassCard as oe, IllustratedEmptyState as C, ResponsiveTable as w } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
//#region src/channel/modules/channels/pages/DeliveryHistoryPage.tsx
|
|
14
14
|
var T = {
|
|
15
15
|
PENDING: {
|
|
@@ -74,8 +74,8 @@ var T = {
|
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
function E() {
|
|
77
|
-
let { channelId: d } = re(), p = ne(), { hasPermission:
|
|
78
|
-
let n =
|
|
77
|
+
let { channelId: d } = re(), p = ne(), { hasPermission: v } = b(), y = v(r.CHANNEL_DELIVERY_EXPORT), { t: E } = l(), D = (e, t) => {
|
|
78
|
+
let n = E(e);
|
|
79
79
|
return n === e ? t : n;
|
|
80
80
|
}, { organizationId: O } = e(), { triggerExport: k, isExporting: A, exportError: j } = i(O ?? void 0, d), [M, N] = c(!0), [P, se] = c([]), [F, I] = c(null), [L, R] = c(null), [z, B] = c(25), [V, H] = c(0), [U, W] = c([void 0]), [G, K] = c(0), { channel: q, isLoading: ce, error: J, fetchDeliveryAttempts: Y, retryDelivery: le } = t(d), X = a(async (e) => {
|
|
81
81
|
if (d) {
|
|
@@ -287,7 +287,7 @@ function E() {
|
|
|
287
287
|
children: "100 results"
|
|
288
288
|
})
|
|
289
289
|
]
|
|
290
|
-
}), /* @__PURE__ */ x(
|
|
290
|
+
}), /* @__PURE__ */ x(ae, { actions: [{
|
|
291
291
|
label: D("channel.refresh", "Refresh"),
|
|
292
292
|
icon: /* @__PURE__ */ x(g, {
|
|
293
293
|
size: 16,
|
|
@@ -296,7 +296,7 @@ function E() {
|
|
|
296
296
|
}),
|
|
297
297
|
disabled: M,
|
|
298
298
|
onSelect: () => void X(U[G])
|
|
299
|
-
}, ...
|
|
299
|
+
}, ...y ? [{
|
|
300
300
|
label: D("channel.export", "Export"),
|
|
301
301
|
icon: A ? /* @__PURE__ */ x(h, {
|
|
302
302
|
size: 16,
|
|
@@ -340,7 +340,7 @@ function E() {
|
|
|
340
340
|
})
|
|
341
341
|
]
|
|
342
342
|
}),
|
|
343
|
-
/* @__PURE__ */ x(
|
|
343
|
+
/* @__PURE__ */ x(oe, {
|
|
344
344
|
treatment: "insight",
|
|
345
345
|
className: "p-4 border-border-seam",
|
|
346
346
|
children: /* @__PURE__ */ x("p", {
|
|
@@ -362,22 +362,10 @@ function E() {
|
|
|
362
362
|
data: P,
|
|
363
363
|
rowKey: (e) => e.id,
|
|
364
364
|
className: "bg-surface border border-border-seam rounded-lg",
|
|
365
|
-
emptyState: /* @__PURE__ */
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
size: 32,
|
|
370
|
-
className: "text-text-secondary"
|
|
371
|
-
}),
|
|
372
|
-
/* @__PURE__ */ x("p", {
|
|
373
|
-
className: "text-sm font-medium text-text-primary",
|
|
374
|
-
children: D("channel.noDeliveryAttempts", "No delivery attempts yet")
|
|
375
|
-
}),
|
|
376
|
-
/* @__PURE__ */ x("p", {
|
|
377
|
-
className: "text-xs text-text-secondary",
|
|
378
|
-
children: D("channel.sendMessageToSeeHistory", "Send a message through this channel to see delivery history")
|
|
379
|
-
})
|
|
380
|
-
]
|
|
365
|
+
emptyState: /* @__PURE__ */ x(C, {
|
|
366
|
+
illustration: "empty-notifications",
|
|
367
|
+
title: D("channel.noDeliveryAttempts", "No delivery attempts yet"),
|
|
368
|
+
description: D("channel.sendMessageToSeeHistory", "Send a message through this channel to see delivery history")
|
|
381
369
|
})
|
|
382
370
|
}),
|
|
383
371
|
/* @__PURE__ */ S("div", {
|