@burdenoff/microfe-channel 2026.624.3 → 2026.625.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +108 -98
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +287 -277
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +140 -131
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +170 -173
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +127 -120
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +161 -155
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +70 -68
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateChannelPage.js +27 -24
- package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js +161 -156
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +179 -182
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +130 -122
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditConnectionPage.js +137 -132
- package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/SendMessagePage.js +87 -84
- package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +193 -194
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -7,9 +7,10 @@ import { AlertCircle as o, ArrowLeft as s, CheckCircle2 as c, Link as l, Loader2
|
|
|
7
7
|
import { useNavigate as h } from "react-router-dom";
|
|
8
8
|
import { usePermissions as g } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
9
9
|
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
10
|
-
import {
|
|
10
|
+
import { GlassCard as b } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
import { TagBadge as x } from "@burdenoff/fe-libs/tag";
|
|
11
12
|
//#region src/channel/modules/channels/pages/ConnectionsListPage.tsx
|
|
12
|
-
var
|
|
13
|
+
var S = {
|
|
13
14
|
true: {
|
|
14
15
|
icon: c,
|
|
15
16
|
color: "text-status-success-text",
|
|
@@ -21,18 +22,18 @@ var x = {
|
|
|
21
22
|
label: "Unhealthy"
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
|
-
function
|
|
25
|
-
let c = h(), { hasPermission: m } = g(),
|
|
25
|
+
function C() {
|
|
26
|
+
let c = h(), { hasPermission: m } = g(), C = m(n.CHANNEL_CONNECTION_CREATE), { channels: w, isLoading: T, error: E, refetch: D } = e();
|
|
26
27
|
r(() => {
|
|
27
28
|
let e = setInterval(() => {
|
|
28
|
-
|
|
29
|
+
D();
|
|
29
30
|
}, 6e4);
|
|
30
31
|
return () => clearInterval(e);
|
|
31
|
-
}, [
|
|
32
|
-
let { t:
|
|
33
|
-
let n =
|
|
32
|
+
}, [D]);
|
|
33
|
+
let { t: O } = a(), k = (e, t) => {
|
|
34
|
+
let n = O(e);
|
|
34
35
|
return n === e ? t : n;
|
|
35
|
-
}, [
|
|
36
|
+
}, [A, j] = i("all"), [M, N] = i("all"), [P, F] = i(0), I = w.reduce((e, t) => {
|
|
36
37
|
let n = t.connection;
|
|
37
38
|
return n && e.push({
|
|
38
39
|
channelId: t.id,
|
|
@@ -48,27 +49,27 @@ function S() {
|
|
|
48
49
|
authType: n.authType,
|
|
49
50
|
tagAssignments: n.tagAssignments ?? []
|
|
50
51
|
}), e;
|
|
51
|
-
}, []),
|
|
52
|
-
return
|
|
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__ */ v("div", {
|
|
53
54
|
className: "flex items-center justify-center min-h-screen",
|
|
54
55
|
children: /* @__PURE__ */ v(u, { className: "size-8 animate-spin text-text-secondary" })
|
|
55
|
-
}) :
|
|
56
|
+
}) : E ? /* @__PURE__ */ y("div", {
|
|
56
57
|
className: "flex flex-col items-center justify-center min-h-screen gap-4",
|
|
57
58
|
children: [
|
|
58
59
|
/* @__PURE__ */ v(o, { className: "size-12 text-status-error-text" }),
|
|
59
60
|
/* @__PURE__ */ v("h1", {
|
|
60
61
|
className: "text-xl font-semibold",
|
|
61
|
-
children:
|
|
62
|
+
children: k("channel.loadConnectionsFailed", "Failed to load connections")
|
|
62
63
|
}),
|
|
63
64
|
/* @__PURE__ */ v("p", {
|
|
64
65
|
className: "text-sm text-text-secondary",
|
|
65
|
-
children:
|
|
66
|
+
children: E
|
|
66
67
|
}),
|
|
67
68
|
/* @__PURE__ */ v("button", {
|
|
68
69
|
type: "button",
|
|
69
|
-
onClick: () =>
|
|
70
|
+
onClick: () => D(),
|
|
70
71
|
className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
|
|
71
|
-
children:
|
|
72
|
+
children: k("channel.retry", "Retry")
|
|
72
73
|
})
|
|
73
74
|
]
|
|
74
75
|
}) : /* @__PURE__ */ v(t, {
|
|
@@ -88,25 +89,25 @@ function S() {
|
|
|
88
89
|
children: [/* @__PURE__ */ v("button", {
|
|
89
90
|
type: "button",
|
|
90
91
|
onClick: () => c("./list"),
|
|
91
|
-
"aria-label":
|
|
92
|
+
"aria-label": k("channel.backToChannels", "Back to channels"),
|
|
92
93
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
93
94
|
children: /* @__PURE__ */ v(s, { className: "size-5 " })
|
|
94
95
|
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
|
|
95
96
|
className: "text-2xl font-bold",
|
|
96
|
-
children:
|
|
97
|
+
children: k("channel.connections", "Connections")
|
|
97
98
|
}), /* @__PURE__ */ y("p", {
|
|
98
99
|
className: "text-sm text-text-secondary",
|
|
99
100
|
children: [
|
|
100
|
-
|
|
101
|
+
R.length,
|
|
101
102
|
" connection",
|
|
102
|
-
|
|
103
|
+
R.length === 1 ? "" : "s"
|
|
103
104
|
]
|
|
104
105
|
})] })]
|
|
105
|
-
}),
|
|
106
|
+
}), C && /* @__PURE__ */ y("button", {
|
|
106
107
|
type: "button",
|
|
107
108
|
onClick: () => c("./create"),
|
|
108
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",
|
|
109
|
-
children: [/* @__PURE__ */ v(d, { className: "size-4 " }),
|
|
110
|
+
children: [/* @__PURE__ */ v(d, { className: "size-4 " }), k("channel.newConnection", "New Connection")]
|
|
110
111
|
})]
|
|
111
112
|
})
|
|
112
113
|
})
|
|
@@ -118,40 +119,40 @@ function S() {
|
|
|
118
119
|
className: "flex items-center gap-4",
|
|
119
120
|
children: [
|
|
120
121
|
/* @__PURE__ */ y("select", {
|
|
121
|
-
value:
|
|
122
|
-
onChange: (e) =>
|
|
122
|
+
value: M,
|
|
123
|
+
onChange: (e) => N(e.target.value),
|
|
123
124
|
className: "px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
|
|
124
125
|
children: [/* @__PURE__ */ v("option", {
|
|
125
126
|
value: "all",
|
|
126
|
-
children:
|
|
127
|
-
}),
|
|
127
|
+
children: k("channel.allPlatforms", "All Platforms")
|
|
128
|
+
}), L.map((e) => /* @__PURE__ */ v("option", {
|
|
128
129
|
value: e,
|
|
129
130
|
children: e
|
|
130
131
|
}, e))]
|
|
131
132
|
}),
|
|
132
133
|
/* @__PURE__ */ y("select", {
|
|
133
|
-
value:
|
|
134
|
-
onChange: (e) =>
|
|
134
|
+
value: A,
|
|
135
|
+
onChange: (e) => j(e.target.value),
|
|
135
136
|
className: "px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
|
|
136
137
|
children: [
|
|
137
138
|
/* @__PURE__ */ v("option", {
|
|
138
139
|
value: "all",
|
|
139
|
-
children:
|
|
140
|
+
children: k("channel.allStatus", "All Status")
|
|
140
141
|
}),
|
|
141
142
|
/* @__PURE__ */ v("option", {
|
|
142
143
|
value: "healthy",
|
|
143
|
-
children:
|
|
144
|
+
children: k("channel.healthy", "Healthy")
|
|
144
145
|
}),
|
|
145
146
|
/* @__PURE__ */ v("option", {
|
|
146
147
|
value: "unhealthy",
|
|
147
|
-
children:
|
|
148
|
+
children: k("channel.unhealthy", "Unhealthy")
|
|
148
149
|
})
|
|
149
150
|
]
|
|
150
151
|
}),
|
|
151
152
|
/* @__PURE__ */ v("button", {
|
|
152
153
|
type: "button",
|
|
153
|
-
onClick: () =>
|
|
154
|
-
"aria-label":
|
|
154
|
+
onClick: () => D(),
|
|
155
|
+
"aria-label": k("channel.refresh", "Refresh"),
|
|
155
156
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
156
157
|
children: /* @__PURE__ */ v(f, {
|
|
157
158
|
className: "size-5 ",
|
|
@@ -163,36 +164,37 @@ function S() {
|
|
|
163
164
|
}),
|
|
164
165
|
/* @__PURE__ */ v("div", {
|
|
165
166
|
className: "container mx-auto px-6 pb-8",
|
|
166
|
-
children:
|
|
167
|
+
children: R.length === 0 ? /* @__PURE__ */ y("div", {
|
|
167
168
|
className: "text-center py-12",
|
|
168
169
|
children: [
|
|
169
170
|
/* @__PURE__ */ v(l, { className: "size-12 mx-auto text-text-secondary mb-4" }),
|
|
170
171
|
/* @__PURE__ */ v("h2", {
|
|
171
172
|
className: "text-lg font-semibold mb-2",
|
|
172
|
-
children:
|
|
173
|
+
children: k("channel.noConnectionsFound", "No connections found")
|
|
173
174
|
}),
|
|
174
175
|
/* @__PURE__ */ v("p", {
|
|
175
176
|
className: "text-sm text-text-secondary mb-4",
|
|
176
|
-
children:
|
|
177
|
+
children: w.length === 0 ? k("channel.createChannelFirst", "Create a channel first to add connections") : k("channel.noConnectionsMatchFilter", "No connections match your filters")
|
|
177
178
|
}),
|
|
178
|
-
|
|
179
|
+
w.length === 0 ? C ? /* @__PURE__ */ v("button", {
|
|
179
180
|
type: "button",
|
|
180
181
|
onClick: () => c("./create"),
|
|
181
182
|
className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
|
|
182
|
-
children:
|
|
183
|
+
children: k("channel.createConnection", "Create Connection")
|
|
183
184
|
}) : null : /* @__PURE__ */ v("button", {
|
|
184
185
|
type: "button",
|
|
185
186
|
onClick: () => {
|
|
186
|
-
|
|
187
|
+
j("all"), N("all");
|
|
187
188
|
},
|
|
188
189
|
className: "px-4 py-2 rounded-lg border hover:bg-bg-sunken",
|
|
189
|
-
children:
|
|
190
|
+
children: k("channel.clearFilters", "Clear Filters")
|
|
190
191
|
})
|
|
191
192
|
]
|
|
192
|
-
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(b, {
|
|
194
|
+
treatment: "plain",
|
|
195
|
+
className: "border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden",
|
|
196
|
+
children: B.map((e) => {
|
|
197
|
+
let t = S[String(e.isHealthy)] || S.false, n = t.icon;
|
|
196
198
|
return /* @__PURE__ */ v("div", {
|
|
197
199
|
role: "button",
|
|
198
200
|
tabIndex: 0,
|
|
@@ -234,22 +236,22 @@ function S() {
|
|
|
234
236
|
className: "text-sm",
|
|
235
237
|
children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ v("span", {
|
|
236
238
|
className: "text-status-success-text",
|
|
237
|
-
children:
|
|
239
|
+
children: k("channel.activeStatus", "Active")
|
|
238
240
|
}) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ v("span", {
|
|
239
241
|
className: "text-text-secondary",
|
|
240
|
-
children:
|
|
242
|
+
children: k("channel.inactiveStatus", "Inactive")
|
|
241
243
|
}) : e.channelStatus === "ERROR" ? /* @__PURE__ */ v("span", {
|
|
242
244
|
className: "text-status-error-text",
|
|
243
|
-
children:
|
|
245
|
+
children: k("channel.errorStatus", "Error")
|
|
244
246
|
}) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ v("span", {
|
|
245
247
|
className: "text-status-warning-text",
|
|
246
|
-
children:
|
|
248
|
+
children: k("channel.suspendedStatus", "Suspended")
|
|
247
249
|
}) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ v("span", {
|
|
248
250
|
className: "text-text-secondary",
|
|
249
|
-
children:
|
|
251
|
+
children: k("channel.archivedStatus", "Archived")
|
|
250
252
|
}) : /* @__PURE__ */ v("span", {
|
|
251
253
|
className: "text-text-secondary",
|
|
252
|
-
children:
|
|
254
|
+
children: k("channel.inactiveStatus", "Inactive")
|
|
253
255
|
})
|
|
254
256
|
}), e.lastTestedAt && /* @__PURE__ */ y("p", {
|
|
255
257
|
className: "text-xs text-text-secondary",
|
|
@@ -263,7 +265,7 @@ function S() {
|
|
|
263
265
|
e.tagAssignments.length > 0 && /* @__PURE__ */ y("div", {
|
|
264
266
|
className: "flex flex-wrap gap-1",
|
|
265
267
|
"aria-label": "Connection tags",
|
|
266
|
-
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ v(
|
|
268
|
+
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ v(x, {
|
|
267
269
|
id: e.tagId,
|
|
268
270
|
label: e.tagId,
|
|
269
271
|
size: "sm"
|
|
@@ -277,7 +279,7 @@ function S() {
|
|
|
277
279
|
onClick: (t) => {
|
|
278
280
|
t.stopPropagation(), c(`/channels/${e.channelId}`);
|
|
279
281
|
},
|
|
280
|
-
"aria-label":
|
|
282
|
+
"aria-label": k("channel.viewChannelSettings", "View channel settings"),
|
|
281
283
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
282
284
|
children: /* @__PURE__ */ v(p, { className: "size-4 " })
|
|
283
285
|
})
|
|
@@ -286,27 +288,27 @@ function S() {
|
|
|
286
288
|
})
|
|
287
289
|
}, e.connectionId);
|
|
288
290
|
})
|
|
289
|
-
}),
|
|
290
|
-
className: "flex items-center justify-between px-4 py-3 border border-border-
|
|
291
|
+
}), R.length > 10 && /* @__PURE__ */ y("div", {
|
|
292
|
+
className: "flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3",
|
|
291
293
|
children: [/* @__PURE__ */ y("p", {
|
|
292
294
|
className: "text-sm text-text-secondary",
|
|
293
295
|
children: [
|
|
294
|
-
|
|
296
|
+
k("channel.showing", "Showing"),
|
|
295
297
|
" ",
|
|
296
298
|
/* @__PURE__ */ y("span", {
|
|
297
299
|
className: "font-medium",
|
|
298
300
|
children: [
|
|
299
|
-
|
|
301
|
+
P * 10 + 1,
|
|
300
302
|
"–",
|
|
301
|
-
Math.min((
|
|
303
|
+
Math.min((P + 1) * 10, R.length)
|
|
302
304
|
]
|
|
303
305
|
}),
|
|
304
306
|
" ",
|
|
305
|
-
|
|
307
|
+
k("channel.of", "of"),
|
|
306
308
|
" ",
|
|
307
309
|
/* @__PURE__ */ v("span", {
|
|
308
310
|
className: "font-medium",
|
|
309
|
-
children:
|
|
311
|
+
children: R.length
|
|
310
312
|
})
|
|
311
313
|
]
|
|
312
314
|
}), /* @__PURE__ */ y("div", {
|
|
@@ -314,25 +316,25 @@ function S() {
|
|
|
314
316
|
children: [
|
|
315
317
|
/* @__PURE__ */ v("button", {
|
|
316
318
|
type: "button",
|
|
317
|
-
onClick: () =>
|
|
318
|
-
disabled:
|
|
319
|
+
onClick: () => F((e) => Math.max(0, e - 1)),
|
|
320
|
+
disabled: P === 0,
|
|
319
321
|
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",
|
|
320
|
-
children:
|
|
322
|
+
children: k("channel.previous", "Previous")
|
|
321
323
|
}),
|
|
322
324
|
/* @__PURE__ */ y("span", {
|
|
323
325
|
className: "text-sm text-text-secondary",
|
|
324
326
|
children: [
|
|
325
|
-
|
|
327
|
+
P + 1,
|
|
326
328
|
" / ",
|
|
327
|
-
|
|
329
|
+
z
|
|
328
330
|
]
|
|
329
331
|
}),
|
|
330
332
|
/* @__PURE__ */ v("button", {
|
|
331
333
|
type: "button",
|
|
332
|
-
onClick: () =>
|
|
333
|
-
disabled:
|
|
334
|
+
onClick: () => F((e) => Math.min(z - 1, e + 1)),
|
|
335
|
+
disabled: P >= z - 1,
|
|
334
336
|
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",
|
|
335
|
-
children:
|
|
337
|
+
children: k("channel.next", "Next")
|
|
336
338
|
})
|
|
337
339
|
]
|
|
338
340
|
})]
|
|
@@ -343,6 +345,6 @@ function S() {
|
|
|
343
345
|
});
|
|
344
346
|
}
|
|
345
347
|
//#endregion
|
|
346
|
-
export {
|
|
348
|
+
export { C as ConnectionsListPage };
|
|
347
349
|
|
|
348
350
|
//# sourceMappingURL=ConnectionsListPage.js.map
|
|
@@ -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 { 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 <div className=\"border rounded-lg divide-y\">\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 </div>\n {/* Pagination */}\n {filteredConnections.length > pageSize && (\n <div className=\"flex items-center justify-between px-4 py-3 border border-border-subtle 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 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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"}
|
|
@@ -7,9 +7,9 @@ import { useI18n as a } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
|
|
|
7
7
|
import { AlertCircle as o, ArrowLeft as s, Bell as c, CheckCircle2 as l, Globe as u, Hash as d, Inbox as f, Loader2 as p, Mail as m, MessageCircle as h, MessageSquare as g, Radio as _, Save as v, Send as y, Smartphone as b, Users as x } from "lucide-react";
|
|
8
8
|
import { useNavigate as S } from "react-router-dom";
|
|
9
9
|
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
10
|
-
import {
|
|
10
|
+
import { GlassCard as T, OptionalFields as E } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/channel/modules/channels/pages/CreateChannelPage.tsx
|
|
12
|
-
var
|
|
12
|
+
var D = [
|
|
13
13
|
{
|
|
14
14
|
value: "EMAIL",
|
|
15
15
|
label: "Email",
|
|
@@ -127,11 +127,11 @@ var E = [
|
|
|
127
127
|
}]
|
|
128
128
|
}
|
|
129
129
|
];
|
|
130
|
-
function
|
|
130
|
+
function O() {
|
|
131
131
|
let c = S(), { organizationId: u } = e(), { createChannel: d } = t(), { t: f } = a(), m = (e, t) => {
|
|
132
132
|
let n = f(e);
|
|
133
133
|
return n === e ? t : n;
|
|
134
|
-
}, [h, g] = i(!1), [y, b] = i(null), [x,
|
|
134
|
+
}, [h, g] = i(!1), [y, b] = i(null), [x, O] = i({
|
|
135
135
|
name: "",
|
|
136
136
|
platform: "EMAIL",
|
|
137
137
|
description: "",
|
|
@@ -139,8 +139,8 @@ function D() {
|
|
|
139
139
|
status: "ACTIVE",
|
|
140
140
|
priority: 100,
|
|
141
141
|
platformConfig: ""
|
|
142
|
-
}),
|
|
143
|
-
|
|
142
|
+
}), k = D.find((e) => e.value === x.platform)?.adapters || [], A = D.find((e) => e.value === x.platform), j = (e) => {
|
|
143
|
+
O((t) => ({
|
|
144
144
|
...t,
|
|
145
145
|
platform: e,
|
|
146
146
|
adapterType: ""
|
|
@@ -222,8 +222,9 @@ function D() {
|
|
|
222
222
|
children: [
|
|
223
223
|
/* @__PURE__ */ w("div", {
|
|
224
224
|
className: "grid gap-5 lg:grid-cols-2",
|
|
225
|
-
children: [/* @__PURE__ */ w(
|
|
226
|
-
|
|
225
|
+
children: [/* @__PURE__ */ w(T, {
|
|
226
|
+
treatment: "plain",
|
|
227
|
+
className: "p-5 border-border-seam space-y-4",
|
|
227
228
|
children: [
|
|
228
229
|
/* @__PURE__ */ C("h2", {
|
|
229
230
|
className: "text-sm font-semibold text-text-primary uppercase tracking-wider",
|
|
@@ -243,7 +244,7 @@ function D() {
|
|
|
243
244
|
"aria-label": m("channel.namePlaceholder", "e.g., Primary Email, SMS Alerts"),
|
|
244
245
|
type: "text",
|
|
245
246
|
value: x.name,
|
|
246
|
-
onChange: (e) =>
|
|
247
|
+
onChange: (e) => O((t) => ({
|
|
247
248
|
...t,
|
|
248
249
|
name: e.target.value
|
|
249
250
|
})),
|
|
@@ -256,7 +257,7 @@ function D() {
|
|
|
256
257
|
}), /* @__PURE__ */ C("textarea", {
|
|
257
258
|
"aria-label": m("channel.descriptionPlaceholder", "Optional description of this channel's purpose"),
|
|
258
259
|
value: x.description,
|
|
259
|
-
onChange: (e) =>
|
|
260
|
+
onChange: (e) => O((t) => ({
|
|
260
261
|
...t,
|
|
261
262
|
description: e.target.value
|
|
262
263
|
})),
|
|
@@ -265,8 +266,9 @@ function D() {
|
|
|
265
266
|
placeholder: m("channel.descriptionPlaceholder", "Optional description of this channel's purpose")
|
|
266
267
|
})] })
|
|
267
268
|
]
|
|
268
|
-
}), /* @__PURE__ */ w(
|
|
269
|
-
|
|
269
|
+
}), /* @__PURE__ */ w(T, {
|
|
270
|
+
treatment: "plain",
|
|
271
|
+
className: "p-5 border-border-seam space-y-4",
|
|
270
272
|
children: [
|
|
271
273
|
/* @__PURE__ */ C("h2", {
|
|
272
274
|
className: "text-sm font-semibold text-text-primary uppercase tracking-wider",
|
|
@@ -279,7 +281,7 @@ function D() {
|
|
|
279
281
|
}),
|
|
280
282
|
/* @__PURE__ */ w("select", {
|
|
281
283
|
value: x.adapterType,
|
|
282
|
-
onChange: (e) =>
|
|
284
|
+
onChange: (e) => O((t) => ({
|
|
283
285
|
...t,
|
|
284
286
|
adapterType: e.target.value
|
|
285
287
|
})),
|
|
@@ -287,14 +289,14 @@ function D() {
|
|
|
287
289
|
children: [/* @__PURE__ */ C("option", {
|
|
288
290
|
value: "",
|
|
289
291
|
children: m("channel.selectProvider", "Select a provider (optional)")
|
|
290
|
-
}),
|
|
292
|
+
}), k.map((e) => /* @__PURE__ */ C("option", {
|
|
291
293
|
value: e.value,
|
|
292
294
|
children: e.label
|
|
293
295
|
}, e.value))]
|
|
294
296
|
}),
|
|
295
297
|
/* @__PURE__ */ C("p", {
|
|
296
298
|
className: "mt-1 text-[11px] text-text-secondary",
|
|
297
|
-
children:
|
|
299
|
+
children: A ? m("channel.providersAvailableForPlatform", `${k.length} providers available for ${A.label}`) : m("channel.selectPlatformFirst", "Select a platform first")
|
|
298
300
|
})
|
|
299
301
|
] }),
|
|
300
302
|
/* @__PURE__ */ w("div", {
|
|
@@ -306,7 +308,7 @@ function D() {
|
|
|
306
308
|
}),
|
|
307
309
|
/* @__PURE__ */ w("select", {
|
|
308
310
|
value: x.status,
|
|
309
|
-
onChange: (e) =>
|
|
311
|
+
onChange: (e) => O((t) => ({
|
|
310
312
|
...t,
|
|
311
313
|
status: e.target.value
|
|
312
314
|
})),
|
|
@@ -334,7 +336,7 @@ function D() {
|
|
|
334
336
|
"aria-label": m("channel.priority", "Priority"),
|
|
335
337
|
type: "number",
|
|
336
338
|
value: x.priority,
|
|
337
|
-
onChange: (e) =>
|
|
339
|
+
onChange: (e) => O((t) => ({
|
|
338
340
|
...t,
|
|
339
341
|
priority: parseInt(e.target.value) || 0
|
|
340
342
|
})),
|
|
@@ -351,8 +353,9 @@ function D() {
|
|
|
351
353
|
]
|
|
352
354
|
})]
|
|
353
355
|
}),
|
|
354
|
-
/* @__PURE__ */ w(
|
|
355
|
-
|
|
356
|
+
/* @__PURE__ */ w(T, {
|
|
357
|
+
treatment: "plain",
|
|
358
|
+
className: "p-5 border-border-seam space-y-4",
|
|
356
359
|
children: [/* @__PURE__ */ w("h2", {
|
|
357
360
|
className: "text-sm font-semibold text-text-primary uppercase tracking-wider",
|
|
358
361
|
children: [
|
|
@@ -365,11 +368,11 @@ function D() {
|
|
|
365
368
|
]
|
|
366
369
|
}), /* @__PURE__ */ C("div", {
|
|
367
370
|
className: "grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
|
|
368
|
-
children:
|
|
371
|
+
children: D.map((e) => {
|
|
369
372
|
let t = e.icon, n = x.platform === e.value;
|
|
370
373
|
return /* @__PURE__ */ w("button", {
|
|
371
374
|
type: "button",
|
|
372
|
-
onClick: () =>
|
|
375
|
+
onClick: () => j(e.value),
|
|
373
376
|
className: `relative flex flex-col items-center gap-1.5 p-3 rounded-lg border transition-all text-center ${n ? `${e.color} border-current ring-1 ring-current` : "border-border-subtle hover:bg-surface-hover text-text-secondary hover:text-text-primary"}`,
|
|
374
377
|
children: [
|
|
375
378
|
n && /* @__PURE__ */ C(l, {
|
|
@@ -390,7 +393,7 @@ function D() {
|
|
|
390
393
|
})
|
|
391
394
|
})]
|
|
392
395
|
}),
|
|
393
|
-
/* @__PURE__ */ C(
|
|
396
|
+
/* @__PURE__ */ C(E, {
|
|
394
397
|
title: m("channel.advancedOptions", "Advanced settings"),
|
|
395
398
|
children: /* @__PURE__ */ C("div", {
|
|
396
399
|
className: "grid gap-4 lg:grid-cols-2",
|
|
@@ -409,7 +412,7 @@ function D() {
|
|
|
409
412
|
/* @__PURE__ */ C("textarea", {
|
|
410
413
|
"aria-label": m("channel.platformConfig", "Platform Config"),
|
|
411
414
|
value: x.platformConfig,
|
|
412
|
-
onChange: (e) =>
|
|
415
|
+
onChange: (e) => O((t) => ({
|
|
413
416
|
...t,
|
|
414
417
|
platformConfig: e.target.value
|
|
415
418
|
})),
|
|
@@ -448,6 +451,6 @@ function D() {
|
|
|
448
451
|
});
|
|
449
452
|
}
|
|
450
453
|
//#endregion
|
|
451
|
-
export {
|
|
454
|
+
export { O as CreateChannelPage };
|
|
452
455
|
|
|
453
456
|
//# sourceMappingURL=CreateChannelPage.js.map
|