@burdenoff/microfe-channel 2026.526.1 → 2026.530.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/channel/ChannelRoutes.js +5 -6
  2. package/dist/channel/ChannelRoutes.js.map +1 -1
  3. package/dist/channel/components/AdapterCredentialsForm.js +14 -2
  4. package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
  5. package/dist/channel/components/FeatureNotEnabled.js +1 -1
  6. package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
  7. package/dist/channel/components/PermissionGuard.js +1 -1
  8. package/dist/channel/components/PermissionGuard.js.map +1 -1
  9. package/dist/channel/config/adapterCredentials.js +19 -11
  10. package/dist/channel/config/adapterCredentials.js.map +1 -1
  11. package/dist/channel/constants/permissions.js +3 -0
  12. package/dist/channel/constants/permissions.js.map +1 -1
  13. package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
  14. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +244 -220
  15. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  16. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +416 -390
  17. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  18. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +30 -15
  19. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  20. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +8 -2
  21. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  22. package/dist/channel/modules/channels/pages/ChannelsListPage.js +241 -213
  23. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  24. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +107 -98
  25. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  26. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +160 -140
  27. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  28. package/dist/channel/modules/channels/pages/CreateChannelPage.js +47 -50
  29. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  30. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +16 -5
  31. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  32. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +11 -4
  33. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  34. package/dist/channel/modules/channels/pages/EditChannelPage.js +17 -8
  35. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  36. package/dist/channel/modules/channels/pages/EditConnectionPage.js +11 -6
  37. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  38. package/dist/channel/modules/channels/pages/SendMessagePage.js +21 -9
  39. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  40. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +8 -1
  41. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  42. package/dist/channel/pages/ChannelLandingPage.js +50 -35
  43. package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
  44. package/dist/channel/providers/ChannelProvider.js +1 -1
  45. package/dist/channel/providers/ChannelProvider.js.map +1 -1
  46. package/package.json +2 -2
  47. package/dist/channel/modules/channels/pages/index.js +0 -14
@@ -5,10 +5,11 @@ import { useEffect as r, useState as i } from "react";
5
5
  import { useI18n as a } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
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
7
  import { useNavigate as h } from "react-router-dom";
8
- import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
9
- import { TagBadge as y } from "@burdenoff/fe-libs/tag";
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";
10
11
  //#region src/channel/modules/channels/pages/ConnectionsListPage.tsx
11
- var b = {
12
+ var x = {
12
13
  true: {
13
14
  icon: c,
14
15
  color: "text-status-success-text",
@@ -20,185 +21,201 @@ var b = {
20
21
  label: "Unhealthy"
21
22
  }
22
23
  };
23
- function x() {
24
- let c = h(), { channels: m, isLoading: x, error: S, refetch: C } = e();
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
26
  r(() => {
26
27
  let e = setInterval(() => {
27
- C();
28
+ E();
28
29
  }, 6e4);
29
30
  return () => clearInterval(e);
30
- }, [C]);
31
- let { t: w } = a(), T = (e, t) => {
32
- let n = w(e);
31
+ }, [E]);
32
+ let { t: D } = a(), O = (e, t) => {
33
+ let n = D(e);
33
34
  return n === e ? t : n;
34
- }, [E, D] = i("all"), [O, k] = i("all"), [A, j] = i(0), M = m.filter((e) => e.connection).map((e) => ({
35
- channelId: e.id,
36
- channelName: e.name,
37
- platform: e.platform,
38
- adapterType: e.adapterType,
39
- channelStatus: e.status,
40
- connectionId: e.connection.id,
41
- isActive: e.connection.isActive,
42
- isHealthy: e.connection.isHealthy,
43
- needsTokenRefresh: e.connection.needsTokenRefresh,
44
- lastTestedAt: e.connection.lastTestedAt,
45
- authType: e.connection.authType,
46
- tagAssignments: e.connection.tagAssignments ?? []
47
- })), N = [...new Set(M.map((e) => e.platform))], P = M.filter((e) => !(E === "healthy" && !e.isHealthy || E === "unhealthy" && e.isHealthy || O !== "all" && e.platform !== O)), F = Math.max(1, Math.ceil(P.length / 10)), I = P.slice(A * 10, (A + 1) * 10);
48
- return x ? /* @__PURE__ */ _("div", {
35
+ }, [k, A] = i("all"), [j, M] = i("all"), [N, P] = i(0), F = C.reduce((e, t) => {
36
+ let n = t.connection;
37
+ return n && e.push({
38
+ channelId: t.id,
39
+ channelName: t.name,
40
+ platform: t.platform,
41
+ adapterType: t.adapterType,
42
+ channelStatus: t.status,
43
+ connectionId: n.id,
44
+ isActive: n.isActive,
45
+ isHealthy: n.isHealthy,
46
+ needsTokenRefresh: n.needsTokenRefresh,
47
+ lastTestedAt: n.lastTestedAt,
48
+ authType: n.authType,
49
+ tagAssignments: n.tagAssignments ?? []
50
+ }), 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", {
49
53
  className: "flex items-center justify-center min-h-screen",
50
- children: /* @__PURE__ */ _(u, { className: "w-8 h-8 animate-spin text-muted-foreground" })
51
- }) : S ? /* @__PURE__ */ v("div", {
54
+ children: /* @__PURE__ */ v(u, { className: "size-8 animate-spin text-muted-foreground" })
55
+ }) : T ? /* @__PURE__ */ y("div", {
52
56
  className: "flex flex-col items-center justify-center min-h-screen gap-4",
53
57
  children: [
54
- /* @__PURE__ */ _(o, { className: "w-12 h-12 text-destructive" }),
55
- /* @__PURE__ */ _("h1", {
58
+ /* @__PURE__ */ v(o, { className: "size-12 text-destructive" }),
59
+ /* @__PURE__ */ v("h1", {
56
60
  className: "text-xl font-semibold",
57
- children: T("channel.loadConnectionsFailed", "Failed to load connections")
61
+ children: O("channel.loadConnectionsFailed", "Failed to load connections")
58
62
  }),
59
- /* @__PURE__ */ _("p", {
63
+ /* @__PURE__ */ v("p", {
60
64
  className: "text-sm text-muted-foreground",
61
- children: S
65
+ children: T
62
66
  }),
63
- /* @__PURE__ */ _("button", {
64
- onClick: () => C(),
67
+ /* @__PURE__ */ v("button", {
68
+ type: "button",
69
+ onClick: () => E(),
65
70
  className: "px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90",
66
- children: T("channel.retry", "Retry")
71
+ children: O("channel.retry", "Retry")
67
72
  })
68
73
  ]
69
- }) : /* @__PURE__ */ _(t, {
74
+ }) : /* @__PURE__ */ v(t, {
70
75
  permission: n.CHANNEL_CONNECTION_READ,
71
- children: /* @__PURE__ */ v("div", {
76
+ children: /* @__PURE__ */ y("div", {
72
77
  className: "min-h-screen bg-background",
73
78
  children: [
74
- /* @__PURE__ */ _("div", {
79
+ /* @__PURE__ */ v("div", {
75
80
  className: "border-b bg-card",
76
- children: /* @__PURE__ */ _("div", {
81
+ "data-tour": "channels-connections-header",
82
+ children: /* @__PURE__ */ v("div", {
77
83
  className: "container mx-auto px-6 py-4",
78
- children: /* @__PURE__ */ v("div", {
84
+ children: /* @__PURE__ */ y("div", {
79
85
  className: "flex items-center justify-between",
80
- children: [/* @__PURE__ */ v("div", {
86
+ children: [/* @__PURE__ */ y("div", {
81
87
  className: "flex items-center gap-4",
82
- children: [/* @__PURE__ */ _("button", {
88
+ children: [/* @__PURE__ */ v("button", {
89
+ type: "button",
83
90
  onClick: () => c("./list"),
84
- "aria-label": T("channel.backToChannels", "Back to channels"),
91
+ "aria-label": O("channel.backToChannels", "Back to channels"),
85
92
  className: "p-2 hover:bg-muted rounded-lg transition-colors",
86
- children: /* @__PURE__ */ _(s, { className: "w-5 h-5" })
87
- }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
93
+ children: /* @__PURE__ */ v(s, { className: "size-5 " })
94
+ }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
88
95
  className: "text-2xl font-bold",
89
- children: T("channel.connections", "Connections")
90
- }), /* @__PURE__ */ v("p", {
96
+ children: O("channel.connections", "Connections")
97
+ }), /* @__PURE__ */ y("p", {
91
98
  className: "text-sm text-muted-foreground",
92
99
  children: [
93
- P.length,
100
+ L.length,
94
101
  " connection",
95
- P.length === 1 ? "" : "s"
102
+ L.length === 1 ? "" : "s"
96
103
  ]
97
104
  })] })]
98
- }), /* @__PURE__ */ v("button", {
105
+ }), S && /* @__PURE__ */ y("button", {
106
+ type: "button",
99
107
  onClick: () => c("./create"),
100
108
  className: "px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-2",
101
- children: [/* @__PURE__ */ _(d, { className: "w-4 h-4" }), T("channel.newChannel", "New Channel")]
109
+ children: [/* @__PURE__ */ v(d, { className: "size-4 " }), O("channel.newConnection", "New Connection")]
102
110
  })]
103
111
  })
104
112
  })
105
113
  }),
106
- /* @__PURE__ */ _("div", {
114
+ /* @__PURE__ */ v("div", {
107
115
  className: "container mx-auto px-6 py-4",
108
- children: /* @__PURE__ */ v("div", {
116
+ "data-tour": "channels-connections-filters",
117
+ children: /* @__PURE__ */ y("div", {
109
118
  className: "flex items-center gap-4",
110
119
  children: [
111
- /* @__PURE__ */ v("select", {
112
- value: O,
113
- onChange: (e) => k(e.target.value),
120
+ /* @__PURE__ */ y("select", {
121
+ value: j,
122
+ onChange: (e) => M(e.target.value),
114
123
  className: "px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary",
115
- children: [/* @__PURE__ */ _("option", {
124
+ children: [/* @__PURE__ */ v("option", {
116
125
  value: "all",
117
- children: T("channel.allPlatforms", "All Platforms")
118
- }), N.map((e) => /* @__PURE__ */ _("option", {
126
+ children: O("channel.allPlatforms", "All Platforms")
127
+ }), I.map((e) => /* @__PURE__ */ v("option", {
119
128
  value: e,
120
129
  children: e
121
130
  }, e))]
122
131
  }),
123
- /* @__PURE__ */ v("select", {
124
- value: E,
125
- onChange: (e) => D(e.target.value),
132
+ /* @__PURE__ */ y("select", {
133
+ value: k,
134
+ onChange: (e) => A(e.target.value),
126
135
  className: "px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary",
127
136
  children: [
128
- /* @__PURE__ */ _("option", {
137
+ /* @__PURE__ */ v("option", {
129
138
  value: "all",
130
- children: T("channel.allStatus", "All Status")
139
+ children: O("channel.allStatus", "All Status")
131
140
  }),
132
- /* @__PURE__ */ _("option", {
141
+ /* @__PURE__ */ v("option", {
133
142
  value: "healthy",
134
- children: T("channel.healthy", "Healthy")
143
+ children: O("channel.healthy", "Healthy")
135
144
  }),
136
- /* @__PURE__ */ _("option", {
145
+ /* @__PURE__ */ v("option", {
137
146
  value: "unhealthy",
138
- children: T("channel.unhealthy", "Unhealthy")
147
+ children: O("channel.unhealthy", "Unhealthy")
139
148
  })
140
149
  ]
141
150
  }),
142
- /* @__PURE__ */ _("button", {
143
- onClick: () => C(),
144
- "aria-label": T("channel.refresh", "Refresh"),
151
+ /* @__PURE__ */ v("button", {
152
+ type: "button",
153
+ onClick: () => E(),
154
+ "aria-label": O("channel.refresh", "Refresh"),
145
155
  className: "p-2 hover:bg-muted rounded-lg transition-colors",
146
- children: /* @__PURE__ */ _(f, {
147
- className: "w-5 h-5",
156
+ children: /* @__PURE__ */ v(f, {
157
+ className: "size-5 ",
148
158
  "aria-hidden": "true"
149
159
  })
150
160
  })
151
161
  ]
152
162
  })
153
163
  }),
154
- /* @__PURE__ */ _("div", {
164
+ /* @__PURE__ */ v("div", {
155
165
  className: "container mx-auto px-6 pb-8",
156
- children: P.length === 0 ? /* @__PURE__ */ v("div", {
166
+ children: L.length === 0 ? /* @__PURE__ */ y("div", {
157
167
  className: "text-center py-12",
158
168
  children: [
159
- /* @__PURE__ */ _(l, { className: "w-12 h-12 mx-auto text-muted-foreground mb-4" }),
160
- /* @__PURE__ */ _("h2", {
169
+ /* @__PURE__ */ v(l, { className: "size-12 mx-auto text-muted-foreground mb-4" }),
170
+ /* @__PURE__ */ v("h2", {
161
171
  className: "text-lg font-semibold mb-2",
162
- children: T("channel.noConnectionsFound", "No connections found")
172
+ children: O("channel.noConnectionsFound", "No connections found")
163
173
  }),
164
- /* @__PURE__ */ _("p", {
174
+ /* @__PURE__ */ v("p", {
165
175
  className: "text-sm text-muted-foreground mb-4",
166
- children: m.length === 0 ? T("channel.createChannelFirst", "Create a channel first to add connections") : T("channel.noConnectionsMatchFilter", "No connections match your filters")
176
+ children: C.length === 0 ? O("channel.createChannelFirst", "Create a channel first to add connections") : O("channel.noConnectionsMatchFilter", "No connections match your filters")
167
177
  }),
168
- m.length === 0 ? /* @__PURE__ */ _("button", {
178
+ C.length === 0 ? S ? /* @__PURE__ */ v("button", {
179
+ type: "button",
169
180
  onClick: () => c("./create"),
170
181
  className: "px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90",
171
- children: T("channel.createChannel", "Create Channel")
172
- }) : /* @__PURE__ */ _("button", {
182
+ children: O("channel.createConnection", "Create Connection")
183
+ }) : null : /* @__PURE__ */ v("button", {
184
+ type: "button",
173
185
  onClick: () => {
174
- D("all"), k("all");
186
+ A("all"), M("all");
175
187
  },
176
188
  className: "px-4 py-2 rounded-lg border hover:bg-muted",
177
- children: T("channel.clearFilters", "Clear Filters")
189
+ children: O("channel.clearFilters", "Clear Filters")
178
190
  })
179
191
  ]
180
- }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("div", {
192
+ }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
181
193
  className: "border rounded-lg divide-y",
182
- children: I.map((e) => {
183
- let t = b[String(e.isHealthy)] || b.false, n = t.icon;
184
- return /* @__PURE__ */ _("div", {
194
+ children: z.map((e) => {
195
+ let t = x[String(e.isHealthy)] || x.false, n = t.icon;
196
+ return /* @__PURE__ */ v("div", {
197
+ role: "button",
198
+ tabIndex: 0,
185
199
  className: "p-4 hover:bg-muted/50 transition-colors cursor-pointer",
186
200
  onClick: () => c(`./${e.channelId}`),
187
- children: /* @__PURE__ */ v("div", {
201
+ onKeyDown: (t) => {
202
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), c(`./${e.channelId}`));
203
+ },
204
+ children: /* @__PURE__ */ y("div", {
188
205
  className: "flex items-center justify-between",
189
- children: [/* @__PURE__ */ v("div", {
206
+ children: [/* @__PURE__ */ y("div", {
190
207
  className: "flex items-center gap-4",
191
- children: [/* @__PURE__ */ _("div", {
208
+ children: [/* @__PURE__ */ v("div", {
192
209
  className: t.color,
193
210
  "aria-label": t.label,
194
- children: /* @__PURE__ */ _(n, {
195
- className: "w-5 h-5",
211
+ children: /* @__PURE__ */ v(n, {
212
+ className: "size-5 ",
196
213
  "aria-hidden": "true"
197
214
  })
198
- }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
215
+ }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
199
216
  className: "font-medium",
200
217
  children: e.channelName
201
- }), /* @__PURE__ */ v("p", {
218
+ }), /* @__PURE__ */ y("p", {
202
219
  className: "text-sm text-muted-foreground",
203
220
  children: [
204
221
  e.platform,
@@ -208,111 +225,114 @@ function x() {
208
225
  e.authType
209
226
  ]
210
227
  })] })]
211
- }), /* @__PURE__ */ v("div", {
228
+ }), /* @__PURE__ */ y("div", {
212
229
  className: "flex items-center gap-4",
213
230
  children: [
214
- /* @__PURE__ */ v("div", {
231
+ /* @__PURE__ */ y("div", {
215
232
  className: "text-right",
216
- children: [/* @__PURE__ */ _("p", {
233
+ children: [/* @__PURE__ */ v("p", {
217
234
  className: "text-sm",
218
- children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ _("span", {
235
+ children: e.channelStatus === "ACTIVE" ? /* @__PURE__ */ v("span", {
219
236
  className: "text-status-success-text",
220
- children: T("channel.activeStatus", "Active")
221
- }) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ _("span", {
237
+ children: O("channel.activeStatus", "Active")
238
+ }) : e.channelStatus === "INACTIVE" ? /* @__PURE__ */ v("span", {
222
239
  className: "text-muted-foreground",
223
- children: T("channel.inactiveStatus", "Inactive")
224
- }) : e.channelStatus === "ERROR" ? /* @__PURE__ */ _("span", {
240
+ children: O("channel.inactiveStatus", "Inactive")
241
+ }) : e.channelStatus === "ERROR" ? /* @__PURE__ */ v("span", {
225
242
  className: "text-status-error-text",
226
- children: T("channel.errorStatus", "Error")
227
- }) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ _("span", {
243
+ children: O("channel.errorStatus", "Error")
244
+ }) : e.channelStatus === "SUSPENDED" ? /* @__PURE__ */ v("span", {
228
245
  className: "text-status-warning-text",
229
- children: T("channel.suspendedStatus", "Suspended")
230
- }) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ _("span", {
246
+ children: O("channel.suspendedStatus", "Suspended")
247
+ }) : e.channelStatus === "ARCHIVED" ? /* @__PURE__ */ v("span", {
231
248
  className: "text-muted-foreground",
232
- children: T("channel.archivedStatus", "Archived")
233
- }) : /* @__PURE__ */ _("span", {
249
+ children: O("channel.archivedStatus", "Archived")
250
+ }) : /* @__PURE__ */ v("span", {
234
251
  className: "text-muted-foreground",
235
- children: T("channel.inactiveStatus", "Inactive")
252
+ children: O("channel.inactiveStatus", "Inactive")
236
253
  })
237
- }), e.lastTestedAt && /* @__PURE__ */ v("p", {
254
+ }), e.lastTestedAt && /* @__PURE__ */ y("p", {
238
255
  className: "text-xs text-muted-foreground",
239
256
  children: ["Last tested: ", new Date(e.lastTestedAt).toLocaleDateString()]
240
257
  })]
241
258
  }),
242
- e.needsTokenRefresh && /* @__PURE__ */ _("span", {
259
+ e.needsTokenRefresh && /* @__PURE__ */ v("span", {
243
260
  className: "px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded",
244
261
  children: "Token Expires"
245
262
  }),
246
- e.tagAssignments.length > 0 && /* @__PURE__ */ v("div", {
263
+ e.tagAssignments.length > 0 && /* @__PURE__ */ y("div", {
247
264
  className: "flex flex-wrap gap-1",
248
265
  "aria-label": "Connection tags",
249
- children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ _(y, {
266
+ children: [e.tagAssignments.slice(0, 3).map((e) => /* @__PURE__ */ v(b, {
250
267
  id: e.tagId,
251
268
  label: e.tagId,
252
269
  size: "sm"
253
- }, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */ v("span", {
270
+ }, e.id)), e.tagAssignments.length > 3 && /* @__PURE__ */ y("span", {
254
271
  className: "text-xs text-muted-foreground",
255
272
  children: ["+", e.tagAssignments.length - 3]
256
273
  })]
257
274
  }),
258
- /* @__PURE__ */ _("button", {
275
+ /* @__PURE__ */ v("button", {
276
+ type: "button",
259
277
  onClick: (t) => {
260
278
  t.stopPropagation(), c(`/channels/${e.channelId}`);
261
279
  },
262
- "aria-label": T("channel.viewChannelSettings", "View channel settings"),
280
+ "aria-label": O("channel.viewChannelSettings", "View channel settings"),
263
281
  className: "p-2 hover:bg-muted rounded-lg transition-colors",
264
- children: /* @__PURE__ */ _(p, { className: "w-4 h-4" })
282
+ children: /* @__PURE__ */ v(p, { className: "size-4 " })
265
283
  })
266
284
  ]
267
285
  })]
268
286
  })
269
287
  }, e.connectionId);
270
288
  })
271
- }), P.length > 10 && /* @__PURE__ */ v("div", {
289
+ }), L.length > 10 && /* @__PURE__ */ y("div", {
272
290
  className: "flex items-center justify-between px-4 py-3 border border-border rounded-lg mt-3",
273
- children: [/* @__PURE__ */ v("p", {
291
+ children: [/* @__PURE__ */ y("p", {
274
292
  className: "text-sm text-muted-foreground",
275
293
  children: [
276
- T("channel.showing", "Showing"),
294
+ O("channel.showing", "Showing"),
277
295
  " ",
278
- /* @__PURE__ */ v("span", {
296
+ /* @__PURE__ */ y("span", {
279
297
  className: "font-medium",
280
298
  children: [
281
- A * 10 + 1,
299
+ N * 10 + 1,
282
300
  "–",
283
- Math.min((A + 1) * 10, P.length)
301
+ Math.min((N + 1) * 10, L.length)
284
302
  ]
285
303
  }),
286
304
  " ",
287
- T("channel.of", "of"),
305
+ O("channel.of", "of"),
288
306
  " ",
289
- /* @__PURE__ */ _("span", {
307
+ /* @__PURE__ */ v("span", {
290
308
  className: "font-medium",
291
- children: P.length
309
+ children: L.length
292
310
  })
293
311
  ]
294
- }), /* @__PURE__ */ v("div", {
312
+ }), /* @__PURE__ */ y("div", {
295
313
  className: "flex items-center gap-2",
296
314
  children: [
297
- /* @__PURE__ */ _("button", {
298
- onClick: () => j((e) => Math.max(0, e - 1)),
299
- disabled: A === 0,
315
+ /* @__PURE__ */ v("button", {
316
+ type: "button",
317
+ onClick: () => P((e) => Math.max(0, e - 1)),
318
+ disabled: N === 0,
300
319
  className: "px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
301
- children: T("channel.previous", "Previous")
320
+ children: O("channel.previous", "Previous")
302
321
  }),
303
- /* @__PURE__ */ v("span", {
322
+ /* @__PURE__ */ y("span", {
304
323
  className: "text-sm text-muted-foreground",
305
324
  children: [
306
- A + 1,
325
+ N + 1,
307
326
  " / ",
308
- F
327
+ R
309
328
  ]
310
329
  }),
311
- /* @__PURE__ */ _("button", {
312
- onClick: () => j((e) => Math.min(F - 1, e + 1)),
313
- disabled: A >= F - 1,
330
+ /* @__PURE__ */ v("button", {
331
+ type: "button",
332
+ onClick: () => P((e) => Math.min(R - 1, e + 1)),
333
+ disabled: N >= R - 1,
314
334
  className: "px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
315
- children: T("channel.next", "Next")
335
+ children: O("channel.next", "Next")
316
336
  })
317
337
  ]
318
338
  })]
@@ -323,6 +343,6 @@ function x() {
323
343
  });
324
344
  }
325
345
  //#endregion
326
- export { x as ConnectionsListPage };
346
+ export { S as ConnectionsListPage };
327
347
 
328
348
  //# 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 { 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 { 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\n const connections = channels\n .filter((ch) => ch.connection)\n .map((ch) => ({\n channelId: ch.id,\n channelName: ch.name,\n platform: ch.platform,\n adapterType: ch.adapterType,\n channelStatus: ch.status,\n connectionId: ch.connection!.id,\n isActive: ch.connection!.isActive,\n isHealthy: ch.connection!.isHealthy,\n needsTokenRefresh: ch.connection!.needsTokenRefresh,\n lastTestedAt: ch.connection!.lastTestedAt,\n authType: ch.connection!.authType,\n tagAssignments: ch.connection!.tagAssignments ?? [],\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=\"w-8 h-8 animate-spin text-muted-foreground\" />\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=\"w-12 h-12 text-destructive\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">{error}</p>\n <button\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/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-background\">\n {/* Header */}\n <div className=\"border-b bg-card\">\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 onClick={() => navigate('./list')}\n aria-label={tr('channel.backToChannels', 'Back to channels')}\n className=\"p-2 hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"w-5 h-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('channel.connections', 'Connections')}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {filteredConnections.length} connection\n {filteredConnections.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n <button\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-2\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('channel.newChannel', 'New Channel')}\n </button>\n </div>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"container mx-auto px-6 py-4\">\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-background 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-background 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 onClick={() => refetch()}\n aria-label={tr('channel.refresh', 'Refresh')}\n className=\"p-2 hover:bg-muted rounded-lg transition-colors\"\n >\n <RefreshCw className=\"w-5 h-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=\"w-12 h-12 mx-auto text-muted-foreground 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-muted-foreground 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 <button\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n ) : (\n <button\n onClick={() => {\n setHealthFilter('all');\n setPlatformFilter('all');\n }}\n className=\"px-4 py-2 rounded-lg border hover:bg-muted\"\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 className=\"p-4 hover:bg-muted/50 transition-colors cursor-pointer\"\n onClick={() => navigate(`./${conn.channelId}`)}\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=\"w-5 h-5\" aria-hidden=\"true\" />\n </div>\n <div>\n <p className=\"font-medium\">{conn.channelName}</p>\n <p className=\"text-sm text-muted-foreground\">\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-muted-foreground\">\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-muted-foreground\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-muted-foreground\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-muted-foreground\">\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-muted-foreground\">\n +{conn.tagAssignments.length - 3}\n </span>\n )}\n </div>\n )}\n\n <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-muted rounded-lg transition-colors\"\n >\n <Settings className=\"w-4 h-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 rounded-lg mt-3\">\n <p className=\"text-sm text-muted-foreground\">\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 onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-muted-foreground\">\n {currentPage + 1} / {totalPages}\n </span>\n <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 rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n *\n * Displays all connections across channels with their status.\n */\n\nimport { useState, useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ArrowLeft,\n Plus,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n RefreshCw,\n Settings,\n Link,\n} from 'lucide-react';\nimport { 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-muted-foreground\" />\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-destructive\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">{error}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/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-background\">\n {/* Header */}\n <div className=\"border-b bg-card\" 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-muted 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-muted-foreground\">\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-primary text-primary-foreground hover:bg-primary/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-background 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-background 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-muted 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-muted-foreground 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-muted-foreground 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-primary text-primary-foreground hover:bg-primary/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-muted\"\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-muted/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-muted-foreground\">\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-muted-foreground\">\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-muted-foreground\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-muted-foreground\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-muted-foreground\">\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-muted-foreground\">\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-muted 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 rounded-lg mt-3\">\n <p className=\"text-sm text-muted-foreground\">\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 rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-muted-foreground\">\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 rounded-lg hover:bg-muted 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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"}