@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.
Files changed (29) hide show
  1. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +134 -134
  2. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  3. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +287 -277
  4. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  5. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +140 -131
  6. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  7. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +169 -169
  8. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  9. package/dist/channel/modules/channels/pages/ChannelsListPage.js +204 -209
  10. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  11. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +161 -155
  12. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  13. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +141 -148
  14. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  15. package/dist/channel/modules/channels/pages/CreateChannelPage.js +27 -24
  16. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  17. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +161 -156
  18. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  19. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +177 -192
  20. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  21. package/dist/channel/modules/channels/pages/EditChannelPage.js +130 -122
  22. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  23. package/dist/channel/modules/channels/pages/EditConnectionPage.js +137 -132
  24. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  25. package/dist/channel/modules/channels/pages/SendMessagePage.js +87 -84
  26. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  27. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +185 -183
  28. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  29. 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, Link as l, Loader2 as u, Plus as d, RefreshCw as f, Settings as p, XCircle as m } from "lucide-react";
7
- import { useNavigate as h } from "react-router-dom";
8
- import { usePermissions as g } from "@burdenoff/fe-libs/shared/providers/shell";
9
- import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
10
- import { TagBadge as b } from "@burdenoff/fe-libs/tag";
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 x = {
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: m,
20
+ icon: p,
20
21
  color: "text-status-error-text",
21
22
  label: "Unhealthy"
22
23
  }
23
24
  };
24
- function S() {
25
- let c = h(), { hasPermission: m } = g(), S = m(n.CHANNEL_CONNECTION_CREATE), { channels: C, isLoading: w, error: T, refetch: E } = e();
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
- E();
29
+ D();
29
30
  }, 6e4);
30
31
  return () => clearInterval(e);
31
- }, [E]);
32
- let { t: D } = a(), O = (e, t) => {
33
- let n = D(e);
32
+ }, [D]);
33
+ let { t: O } = a(), k = (e, t) => {
34
+ let n = O(e);
34
35
  return n === e ? t : n;
35
- }, [k, A] = i("all"), [j, M] = i("all"), [N, P] = i(0), F = C.reduce((e, t) => {
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
- }, []), I = [...new Set(F.map((e) => e.platform))], L = F.filter((e) => !(k === "healthy" && !e.isHealthy || k === "unhealthy" && e.isHealthy || j !== "all" && e.platform !== j)), R = Math.max(1, Math.ceil(L.length / 10)), z = L.slice(N * 10, (N + 1) * 10);
52
- return w ? /* @__PURE__ */ v("div", {
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__ */ v(u, { className: "size-8 animate-spin text-text-secondary" })
55
- }) : T ? /* @__PURE__ */ y("div", {
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__ */ v(o, { className: "size-12 text-status-error-text" }),
59
- /* @__PURE__ */ v("h1", {
59
+ /* @__PURE__ */ _(o, { className: "size-12 text-status-error-text" }),
60
+ /* @__PURE__ */ _("h1", {
60
61
  className: "text-xl font-semibold",
61
- children: O("channel.loadConnectionsFailed", "Failed to load connections")
62
+ children: k("channel.loadConnectionsFailed", "Failed to load connections")
62
63
  }),
63
- /* @__PURE__ */ v("p", {
64
+ /* @__PURE__ */ _("p", {
64
65
  className: "text-sm text-text-secondary",
65
- children: T
66
+ children: E
66
67
  }),
67
- /* @__PURE__ */ v("button", {
68
+ /* @__PURE__ */ _("button", {
68
69
  type: "button",
69
- onClick: () => E(),
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: O("channel.retry", "Retry")
72
+ children: k("channel.retry", "Retry")
72
73
  })
73
74
  ]
74
- }) : /* @__PURE__ */ v(t, {
75
+ }) : /* @__PURE__ */ _(t, {
75
76
  permission: n.CHANNEL_CONNECTION_READ,
76
- children: /* @__PURE__ */ y("div", {
77
+ children: /* @__PURE__ */ v("div", {
77
78
  className: "min-h-screen bg-bg-surface",
78
79
  children: [
79
- /* @__PURE__ */ v("div", {
80
+ /* @__PURE__ */ _("div", {
80
81
  className: "border-b bg-bg-surface",
81
82
  "data-tour": "channels-connections-header",
82
- children: /* @__PURE__ */ v("div", {
83
+ children: /* @__PURE__ */ _("div", {
83
84
  className: "container mx-auto px-6 py-4",
84
- children: /* @__PURE__ */ y("div", {
85
+ children: /* @__PURE__ */ v("div", {
85
86
  className: "flex items-center justify-between",
86
- children: [/* @__PURE__ */ y("div", {
87
+ children: [/* @__PURE__ */ v("div", {
87
88
  className: "flex items-center gap-4",
88
- children: [/* @__PURE__ */ v("button", {
89
+ children: [/* @__PURE__ */ _("button", {
89
90
  type: "button",
90
91
  onClick: () => c("./list"),
91
- "aria-label": O("channel.backToChannels", "Back to channels"),
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__ */ v(s, { className: "size-5 " })
94
- }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
94
+ children: /* @__PURE__ */ _(s, { className: "size-5 " })
95
+ }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
95
96
  className: "text-2xl font-bold",
96
- children: O("channel.connections", "Connections")
97
- }), /* @__PURE__ */ y("p", {
97
+ children: k("channel.connections", "Connections")
98
+ }), /* @__PURE__ */ v("p", {
98
99
  className: "text-sm text-text-secondary",
99
100
  children: [
100
- L.length,
101
+ R.length,
101
102
  " connection",
102
- L.length === 1 ? "" : "s"
103
+ R.length === 1 ? "" : "s"
103
104
  ]
104
105
  })] })]
105
- }), S && /* @__PURE__ */ y("button", {
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__ */ v(d, { className: "size-4 " }), O("channel.newConnection", "New Connection")]
110
+ children: [/* @__PURE__ */ _(u, { className: "size-4 " }), k("channel.newConnection", "New Connection")]
110
111
  })]
111
112
  })
112
113
  })
113
114
  }),
114
- /* @__PURE__ */ v("div", {
115
+ /* @__PURE__ */ _("div", {
115
116
  className: "container mx-auto px-6 py-4",
116
117
  "data-tour": "channels-connections-filters",
117
- children: /* @__PURE__ */ y("div", {
118
+ children: /* @__PURE__ */ v("div", {
118
119
  className: "flex items-center gap-4",
119
120
  children: [
120
- /* @__PURE__ */ y("select", {
121
- value: j,
122
- onChange: (e) => M(e.target.value),
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__ */ v("option", {
125
+ children: [/* @__PURE__ */ _("option", {
125
126
  value: "all",
126
- children: O("channel.allPlatforms", "All Platforms")
127
- }), I.map((e) => /* @__PURE__ */ v("option", {
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__ */ y("select", {
133
- value: k,
134
- onChange: (e) => A(e.target.value),
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__ */ v("option", {
138
+ /* @__PURE__ */ _("option", {
138
139
  value: "all",
139
- children: O("channel.allStatus", "All Status")
140
+ children: k("channel.allStatus", "All Status")
140
141
  }),
141
- /* @__PURE__ */ v("option", {
142
+ /* @__PURE__ */ _("option", {
142
143
  value: "healthy",
143
- children: O("channel.healthy", "Healthy")
144
+ children: k("channel.healthy", "Healthy")
144
145
  }),
145
- /* @__PURE__ */ v("option", {
146
+ /* @__PURE__ */ _("option", {
146
147
  value: "unhealthy",
147
- children: O("channel.unhealthy", "Unhealthy")
148
+ children: k("channel.unhealthy", "Unhealthy")
148
149
  })
149
150
  ]
150
151
  }),
151
- /* @__PURE__ */ v("button", {
152
+ /* @__PURE__ */ _("button", {
152
153
  type: "button",
153
- onClick: () => E(),
154
- "aria-label": O("channel.refresh", "Refresh"),
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__ */ v(f, {
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__ */ v("div", {
165
+ /* @__PURE__ */ _("div", {
165
166
  className: "container mx-auto px-6 pb-8",
166
- children: L.length === 0 ? /* @__PURE__ */ y("div", {
167
- className: "text-center py-12",
168
- children: [
169
- /* @__PURE__ */ v(l, { className: "size-12 mx-auto text-text-secondary mb-4" }),
170
- /* @__PURE__ */ v("h2", {
171
- className: "text-lg font-semibold mb-2",
172
- children: O("channel.noConnectionsFound", "No connections found")
173
- }),
174
- /* @__PURE__ */ v("p", {
175
- className: "text-sm text-text-secondary mb-4",
176
- children: C.length === 0 ? O("channel.createChannelFirst", "Create a channel first to add connections") : O("channel.noConnectionsMatchFilter", "No connections match your filters")
177
- }),
178
- C.length === 0 ? S ? /* @__PURE__ */ v("button", {
179
- type: "button",
180
- onClick: () => c("./create"),
181
- className: "px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
182
- children: O("channel.createConnection", "Create Connection")
183
- }) : null : /* @__PURE__ */ v("button", {
184
- type: "button",
185
- onClick: () => {
186
- A("all"), M("all");
187
- },
188
- className: "px-4 py-2 rounded-lg border hover:bg-bg-sunken",
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__ */ y("div", {
197
+ children: /* @__PURE__ */ v("div", {
205
198
  className: "flex items-center justify-between",
206
- children: [/* @__PURE__ */ y("div", {
199
+ children: [/* @__PURE__ */ v("div", {
207
200
  className: "flex items-center gap-4",
208
- children: [/* @__PURE__ */ v("div", {
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__ */ v(n, {
204
+ children: /* @__PURE__ */ _(n, {
212
205
  className: "size-5 ",
213
206
  "aria-hidden": "true"
214
207
  })
215
- }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
208
+ }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
216
209
  className: "font-medium",
217
210
  children: e.channelName
218
- }), /* @__PURE__ */ y("p", {
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__ */ y("div", {
221
+ }), /* @__PURE__ */ v("div", {
229
222
  className: "flex items-center gap-4",
230
223
  children: [
231
- /* @__PURE__ */ y("div", {
224
+ /* @__PURE__ */ v("div", {
232
225
  className: "text-right",
233
- children: [/* @__PURE__ */ v("p", {
226
+ children: [/* @__PURE__ */ _("p", {
234
227
  className: "text-sm",
235
- children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ v("span", {
228
+ children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ _("span", {
236
229
  className: "text-status-success-text",
237
- children: O("channel.activeStatus", "Active")
238
- }) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ v("span", {
230
+ children: k("channel.activeStatus", "Active")
231
+ }) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ _("span", {
239
232
  className: "text-text-secondary",
240
- children: O("channel.inactiveStatus", "Inactive")
241
- }) : e.channelStatus === "ERROR" ? /* @__PURE__ */ v("span", {
233
+ children: k("channel.inactiveStatus", "Inactive")
234
+ }) : e.channelStatus === "ERROR" ? /* @__PURE__ */ _("span", {
242
235
  className: "text-status-error-text",
243
- children: O("channel.errorStatus", "Error")
244
- }) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ v("span", {
236
+ children: k("channel.errorStatus", "Error")
237
+ }) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ _("span", {
245
238
  className: "text-status-warning-text",
246
- children: O("channel.suspendedStatus", "Suspended")
247
- }) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ v("span", {
239
+ children: k("channel.suspendedStatus", "Suspended")
240
+ }) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ _("span", {
248
241
  className: "text-text-secondary",
249
- children: O("channel.archivedStatus", "Archived")
250
- }) : /* @__PURE__ */ v("span", {
242
+ children: k("channel.archivedStatus", "Archived")
243
+ }) : /* @__PURE__ */ _("span", {
251
244
  className: "text-text-secondary",
252
- children: O("channel.inactiveStatus", "Inactive")
245
+ children: k("channel.inactiveStatus", "Inactive")
253
246
  })
254
- }), e.lastTestedAt && /* @__PURE__ */ y("p", {
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__ */ v("span", {
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__ */ y("div", {
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__ */ v(b, {
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__ */ y("span", {
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__ */ v("button", {
268
+ /* @__PURE__ */ _("button", {
276
269
  type: "button",
277
270
  onClick: (t) => {
278
271
  t.stopPropagation(), c(`/channels/${e.channelId}`);
279
272
  },
280
- "aria-label": O("channel.viewChannelSettings", "View channel settings"),
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__ */ v(p, { className: "size-4 " })
275
+ children: /* @__PURE__ */ _(f, { className: "size-4 " })
283
276
  })
284
277
  ]
285
278
  })]
286
279
  })
287
280
  }, e.connectionId);
288
281
  })
289
- }), L.length > 10 && /* @__PURE__ */ y("div", {
290
- className: "flex items-center justify-between px-4 py-3 border border-border-subtle rounded-lg mt-3",
291
- children: [/* @__PURE__ */ y("p", {
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
- O("channel.showing", "Showing"),
287
+ k("channel.showing", "Showing"),
295
288
  " ",
296
- /* @__PURE__ */ y("span", {
289
+ /* @__PURE__ */ v("span", {
297
290
  className: "font-medium",
298
291
  children: [
299
- N * 10 + 1,
292
+ P * 10 + 1,
300
293
  "–",
301
- Math.min((N + 1) * 10, L.length)
294
+ Math.min((P + 1) * 10, R.length)
302
295
  ]
303
296
  }),
304
297
  " ",
305
- O("channel.of", "of"),
298
+ k("channel.of", "of"),
306
299
  " ",
307
- /* @__PURE__ */ v("span", {
300
+ /* @__PURE__ */ _("span", {
308
301
  className: "font-medium",
309
- children: L.length
302
+ children: R.length
310
303
  })
311
304
  ]
312
- }), /* @__PURE__ */ y("div", {
305
+ }), /* @__PURE__ */ v("div", {
313
306
  className: "flex items-center gap-2",
314
307
  children: [
315
- /* @__PURE__ */ v("button", {
308
+ /* @__PURE__ */ _("button", {
316
309
  type: "button",
317
- onClick: () => P((e) => Math.max(0, e - 1)),
318
- disabled: N === 0,
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: O("channel.previous", "Previous")
313
+ children: k("channel.previous", "Previous")
321
314
  }),
322
- /* @__PURE__ */ y("span", {
315
+ /* @__PURE__ */ v("span", {
323
316
  className: "text-sm text-text-secondary",
324
317
  children: [
325
- N + 1,
318
+ P + 1,
326
319
  " / ",
327
- R
320
+ z
328
321
  ]
329
322
  }),
330
- /* @__PURE__ */ v("button", {
323
+ /* @__PURE__ */ _("button", {
331
324
  type: "button",
332
- onClick: () => P((e) => Math.min(R - 1, e + 1)),
333
- disabled: N >= R - 1,
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: O("channel.next", "Next")
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 { S as ConnectionsListPage };
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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"}