@burdenoff/microfe-channel 2026.624.3 → 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 +134 -134
- 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 +169 -169
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +204 -209
- 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 +141 -148
- 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 +177 -192
- 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 +185 -183
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,36 +3,37 @@ 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 {
|
|
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
|
+
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",
|
|
16
17
|
label: "Healthy"
|
|
17
18
|
},
|
|
18
19
|
false: {
|
|
19
|
-
icon:
|
|
20
|
+
icon: p,
|
|
20
21
|
color: "text-status-error-text",
|
|
21
22
|
label: "Unhealthy"
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
|
-
function
|
|
25
|
-
let c =
|
|
25
|
+
function C() {
|
|
26
|
+
let c = m(), { hasPermission: p } = h(), C = p(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,112 +49,112 @@ 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__ */ _("div", {
|
|
53
54
|
className: "flex items-center justify-center min-h-screen",
|
|
54
|
-
children: /* @__PURE__ */
|
|
55
|
-
}) :
|
|
55
|
+
children: /* @__PURE__ */ _(l, { className: "size-8 animate-spin text-text-secondary" })
|
|
56
|
+
}) : E ? /* @__PURE__ */ v("div", {
|
|
56
57
|
className: "flex flex-col items-center justify-center min-h-screen gap-4",
|
|
57
58
|
children: [
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ _(o, { className: "size-12 text-status-error-text" }),
|
|
60
|
+
/* @__PURE__ */ _("h1", {
|
|
60
61
|
className: "text-xl font-semibold",
|
|
61
|
-
children:
|
|
62
|
+
children: k("channel.loadConnectionsFailed", "Failed to load connections")
|
|
62
63
|
}),
|
|
63
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ _("p", {
|
|
64
65
|
className: "text-sm text-text-secondary",
|
|
65
|
-
children:
|
|
66
|
+
children: E
|
|
66
67
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ _("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
|
-
}) : /* @__PURE__ */
|
|
75
|
+
}) : /* @__PURE__ */ _(t, {
|
|
75
76
|
permission: n.CHANNEL_CONNECTION_READ,
|
|
76
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: /* @__PURE__ */ v("div", {
|
|
77
78
|
className: "min-h-screen bg-bg-surface",
|
|
78
79
|
children: [
|
|
79
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ _("div", {
|
|
80
81
|
className: "border-b bg-bg-surface",
|
|
81
82
|
"data-tour": "channels-connections-header",
|
|
82
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ _("div", {
|
|
83
84
|
className: "container mx-auto px-6 py-4",
|
|
84
|
-
children: /* @__PURE__ */
|
|
85
|
+
children: /* @__PURE__ */ v("div", {
|
|
85
86
|
className: "flex items-center justify-between",
|
|
86
|
-
children: [/* @__PURE__ */
|
|
87
|
+
children: [/* @__PURE__ */ v("div", {
|
|
87
88
|
className: "flex items-center gap-4",
|
|
88
|
-
children: [/* @__PURE__ */
|
|
89
|
+
children: [/* @__PURE__ */ _("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
|
-
children: /* @__PURE__ */
|
|
94
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ _(s, { className: "size-5 " })
|
|
95
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
|
|
95
96
|
className: "text-2xl font-bold",
|
|
96
|
-
children:
|
|
97
|
-
}), /* @__PURE__ */
|
|
97
|
+
children: k("channel.connections", "Connections")
|
|
98
|
+
}), /* @__PURE__ */ v("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__ */ v("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__ */
|
|
110
|
+
children: [/* @__PURE__ */ _(u, { className: "size-4 " }), k("channel.newConnection", "New Connection")]
|
|
110
111
|
})]
|
|
111
112
|
})
|
|
112
113
|
})
|
|
113
114
|
}),
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ _("div", {
|
|
115
116
|
className: "container mx-auto px-6 py-4",
|
|
116
117
|
"data-tour": "channels-connections-filters",
|
|
117
|
-
children: /* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ v("div", {
|
|
118
119
|
className: "flex items-center gap-4",
|
|
119
120
|
children: [
|
|
120
|
-
/* @__PURE__ */
|
|
121
|
-
value:
|
|
122
|
-
onChange: (e) =>
|
|
121
|
+
/* @__PURE__ */ v("select", {
|
|
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
|
-
children: [/* @__PURE__ */
|
|
125
|
+
children: [/* @__PURE__ */ _("option", {
|
|
125
126
|
value: "all",
|
|
126
|
-
children:
|
|
127
|
-
}),
|
|
127
|
+
children: k("channel.allPlatforms", "All Platforms")
|
|
128
|
+
}), L.map((e) => /* @__PURE__ */ _("option", {
|
|
128
129
|
value: e,
|
|
129
130
|
children: e
|
|
130
131
|
}, e))]
|
|
131
132
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
-
value:
|
|
134
|
-
onChange: (e) =>
|
|
133
|
+
/* @__PURE__ */ v("select", {
|
|
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
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ _("option", {
|
|
138
139
|
value: "all",
|
|
139
|
-
children:
|
|
140
|
+
children: k("channel.allStatus", "All Status")
|
|
140
141
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ _("option", {
|
|
142
143
|
value: "healthy",
|
|
143
|
-
children:
|
|
144
|
+
children: k("channel.healthy", "Healthy")
|
|
144
145
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ _("option", {
|
|
146
147
|
value: "unhealthy",
|
|
147
|
-
children:
|
|
148
|
+
children: k("channel.unhealthy", "Unhealthy")
|
|
148
149
|
})
|
|
149
150
|
]
|
|
150
151
|
}),
|
|
151
|
-
/* @__PURE__ */
|
|
152
|
+
/* @__PURE__ */ _("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
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ _(d, {
|
|
157
158
|
className: "size-5 ",
|
|
158
159
|
"aria-hidden": "true"
|
|
159
160
|
})
|
|
@@ -161,39 +162,31 @@ function S() {
|
|
|
161
162
|
]
|
|
162
163
|
})
|
|
163
164
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ _("div", {
|
|
165
166
|
className: "container mx-auto px-6 pb-8",
|
|
166
|
-
children:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
children: O("channel.clearFilters", "Clear Filters")
|
|
190
|
-
})
|
|
191
|
-
]
|
|
192
|
-
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
|
|
193
|
-
className: "border rounded-lg divide-y",
|
|
194
|
-
children: z.map((e) => {
|
|
195
|
-
let t = x[String(e.isHealthy)] || x.false, n = t.icon;
|
|
196
|
-
return /* @__PURE__ */ v("div", {
|
|
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, {
|
|
185
|
+
treatment: "plain",
|
|
186
|
+
className: "border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden",
|
|
187
|
+
children: B.map((e) => {
|
|
188
|
+
let t = S[String(e.isHealthy)] || S.false, n = t.icon;
|
|
189
|
+
return /* @__PURE__ */ _("div", {
|
|
197
190
|
role: "button",
|
|
198
191
|
tabIndex: 0,
|
|
199
192
|
className: "p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
|
|
@@ -201,21 +194,21 @@ function S() {
|
|
|
201
194
|
onKeyDown: (t) => {
|
|
202
195
|
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), c(`./${e.channelId}`));
|
|
203
196
|
},
|
|
204
|
-
children: /* @__PURE__ */
|
|
197
|
+
children: /* @__PURE__ */ v("div", {
|
|
205
198
|
className: "flex items-center justify-between",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ v("div", {
|
|
207
200
|
className: "flex items-center gap-4",
|
|
208
|
-
children: [/* @__PURE__ */
|
|
209
|
-
className: t.color
|
|
201
|
+
children: [/* @__PURE__ */ _("div", {
|
|
202
|
+
className: `${t.color} ${e.isHealthy ? "motion-safe:animate-vc-glow-pulse" : ""}`,
|
|
210
203
|
"aria-label": t.label,
|
|
211
|
-
children: /* @__PURE__ */
|
|
204
|
+
children: /* @__PURE__ */ _(n, {
|
|
212
205
|
className: "size-5 ",
|
|
213
206
|
"aria-hidden": "true"
|
|
214
207
|
})
|
|
215
|
-
}), /* @__PURE__ */
|
|
208
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
216
209
|
className: "font-medium",
|
|
217
210
|
children: e.channelName
|
|
218
|
-
}), /* @__PURE__ */
|
|
211
|
+
}), /* @__PURE__ */ v("p", {
|
|
219
212
|
className: "text-sm text-text-secondary",
|
|
220
213
|
children: [
|
|
221
214
|
e.platform,
|
|
@@ -225,114 +218,114 @@ function S() {
|
|
|
225
218
|
e.authType
|
|
226
219
|
]
|
|
227
220
|
})] })]
|
|
228
|
-
}), /* @__PURE__ */
|
|
221
|
+
}), /* @__PURE__ */ v("div", {
|
|
229
222
|
className: "flex items-center gap-4",
|
|
230
223
|
children: [
|
|
231
|
-
/* @__PURE__ */
|
|
224
|
+
/* @__PURE__ */ v("div", {
|
|
232
225
|
className: "text-right",
|
|
233
|
-
children: [/* @__PURE__ */
|
|
226
|
+
children: [/* @__PURE__ */ _("p", {
|
|
234
227
|
className: "text-sm",
|
|
235
|
-
children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */
|
|
228
|
+
children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ _("span", {
|
|
236
229
|
className: "text-status-success-text",
|
|
237
|
-
children:
|
|
238
|
-
}) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */
|
|
230
|
+
children: k("channel.activeStatus", "Active")
|
|
231
|
+
}) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ _("span", {
|
|
239
232
|
className: "text-text-secondary",
|
|
240
|
-
children:
|
|
241
|
-
}) : e.channelStatus === "ERROR" ? /* @__PURE__ */
|
|
233
|
+
children: k("channel.inactiveStatus", "Inactive")
|
|
234
|
+
}) : e.channelStatus === "ERROR" ? /* @__PURE__ */ _("span", {
|
|
242
235
|
className: "text-status-error-text",
|
|
243
|
-
children:
|
|
244
|
-
}) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */
|
|
236
|
+
children: k("channel.errorStatus", "Error")
|
|
237
|
+
}) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ _("span", {
|
|
245
238
|
className: "text-status-warning-text",
|
|
246
|
-
children:
|
|
247
|
-
}) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */
|
|
239
|
+
children: k("channel.suspendedStatus", "Suspended")
|
|
240
|
+
}) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ _("span", {
|
|
248
241
|
className: "text-text-secondary",
|
|
249
|
-
children:
|
|
250
|
-
}) : /* @__PURE__ */
|
|
242
|
+
children: k("channel.archivedStatus", "Archived")
|
|
243
|
+
}) : /* @__PURE__ */ _("span", {
|
|
251
244
|
className: "text-text-secondary",
|
|
252
|
-
children:
|
|
245
|
+
children: k("channel.inactiveStatus", "Inactive")
|
|
253
246
|
})
|
|
254
|
-
}), e.lastTestedAt && /* @__PURE__ */
|
|
247
|
+
}), e.lastTestedAt && /* @__PURE__ */ v("p", {
|
|
255
248
|
className: "text-xs text-text-secondary",
|
|
256
249
|
children: ["Last tested: ", new Date(e.lastTestedAt).toLocaleDateString()]
|
|
257
250
|
})]
|
|
258
251
|
}),
|
|
259
|
-
e.needsTokenRefresh && /* @__PURE__ */
|
|
252
|
+
e.needsTokenRefresh && /* @__PURE__ */ _("span", {
|
|
260
253
|
className: "px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded",
|
|
261
254
|
children: "Token Expires"
|
|
262
255
|
}),
|
|
263
|
-
e.tagAssignments.length > 0 && /* @__PURE__ */
|
|
256
|
+
e.tagAssignments.length > 0 && /* @__PURE__ */ v("div", {
|
|
264
257
|
className: "flex flex-wrap gap-1",
|
|
265
258
|
"aria-label": "Connection tags",
|
|
266
|
-
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */
|
|
259
|
+
children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ _(x, {
|
|
267
260
|
id: e.tagId,
|
|
268
261
|
label: e.tagId,
|
|
269
262
|
size: "sm"
|
|
270
|
-
}, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */
|
|
263
|
+
}, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */ v("span", {
|
|
271
264
|
className: "text-xs text-text-secondary",
|
|
272
265
|
children: ["+", e.tagAssignments.length - 3]
|
|
273
266
|
})]
|
|
274
267
|
}),
|
|
275
|
-
/* @__PURE__ */
|
|
268
|
+
/* @__PURE__ */ _("button", {
|
|
276
269
|
type: "button",
|
|
277
270
|
onClick: (t) => {
|
|
278
271
|
t.stopPropagation(), c(`/channels/${e.channelId}`);
|
|
279
272
|
},
|
|
280
|
-
"aria-label":
|
|
273
|
+
"aria-label": k("channel.viewChannelSettings", "View channel settings"),
|
|
281
274
|
className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
|
|
282
|
-
children: /* @__PURE__ */
|
|
275
|
+
children: /* @__PURE__ */ _(f, { className: "size-4 " })
|
|
283
276
|
})
|
|
284
277
|
]
|
|
285
278
|
})]
|
|
286
279
|
})
|
|
287
280
|
}, e.connectionId);
|
|
288
281
|
})
|
|
289
|
-
}),
|
|
290
|
-
className: "flex items-center justify-between px-4 py-3 border border-border-
|
|
291
|
-
children: [/* @__PURE__ */
|
|
282
|
+
}), R.length > 10 && /* @__PURE__ */ v("div", {
|
|
283
|
+
className: "flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3",
|
|
284
|
+
children: [/* @__PURE__ */ v("p", {
|
|
292
285
|
className: "text-sm text-text-secondary",
|
|
293
286
|
children: [
|
|
294
|
-
|
|
287
|
+
k("channel.showing", "Showing"),
|
|
295
288
|
" ",
|
|
296
|
-
/* @__PURE__ */
|
|
289
|
+
/* @__PURE__ */ v("span", {
|
|
297
290
|
className: "font-medium",
|
|
298
291
|
children: [
|
|
299
|
-
|
|
292
|
+
P * 10 + 1,
|
|
300
293
|
"–",
|
|
301
|
-
Math.min((
|
|
294
|
+
Math.min((P + 1) * 10, R.length)
|
|
302
295
|
]
|
|
303
296
|
}),
|
|
304
297
|
" ",
|
|
305
|
-
|
|
298
|
+
k("channel.of", "of"),
|
|
306
299
|
" ",
|
|
307
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ _("span", {
|
|
308
301
|
className: "font-medium",
|
|
309
|
-
children:
|
|
302
|
+
children: R.length
|
|
310
303
|
})
|
|
311
304
|
]
|
|
312
|
-
}), /* @__PURE__ */
|
|
305
|
+
}), /* @__PURE__ */ v("div", {
|
|
313
306
|
className: "flex items-center gap-2",
|
|
314
307
|
children: [
|
|
315
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ _("button", {
|
|
316
309
|
type: "button",
|
|
317
|
-
onClick: () =>
|
|
318
|
-
disabled:
|
|
310
|
+
onClick: () => F((e) => Math.max(0, e - 1)),
|
|
311
|
+
disabled: P === 0,
|
|
319
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",
|
|
320
|
-
children:
|
|
313
|
+
children: k("channel.previous", "Previous")
|
|
321
314
|
}),
|
|
322
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ v("span", {
|
|
323
316
|
className: "text-sm text-text-secondary",
|
|
324
317
|
children: [
|
|
325
|
-
|
|
318
|
+
P + 1,
|
|
326
319
|
" / ",
|
|
327
|
-
|
|
320
|
+
z
|
|
328
321
|
]
|
|
329
322
|
}),
|
|
330
|
-
/* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ _("button", {
|
|
331
324
|
type: "button",
|
|
332
|
-
onClick: () =>
|
|
333
|
-
disabled:
|
|
325
|
+
onClick: () => F((e) => Math.min(z - 1, e + 1)),
|
|
326
|
+
disabled: P >= z - 1,
|
|
334
327
|
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:
|
|
328
|
+
children: k("channel.next", "Next")
|
|
336
329
|
})
|
|
337
330
|
]
|
|
338
331
|
})]
|
|
@@ -343,6 +336,6 @@ function S() {
|
|
|
343
336
|
});
|
|
344
337
|
}
|
|
345
338
|
//#endregion
|
|
346
|
-
export {
|
|
339
|
+
export { C as ConnectionsListPage };
|
|
347
340
|
|
|
348
341
|
//# 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} 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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"}
|