@burdenoff/microfe-channel 2026.625.1 → 2026.625.2

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