@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.
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +66 -76
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +117 -114
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +159 -171
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +88 -97
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +10 -22
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +131 -128
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,13 +5,13 @@ import { CHANNEL_PERMISSIONS as r } from "../../../constants/permissions.js";
|
|
|
5
5
|
import { useExportChannelUsers as ee } from "../hooks/useExport.js";
|
|
6
6
|
import { useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
|
|
7
7
|
import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
-
import { AlertCircle as l, ArrowLeft as u, Bell as d, BellOff as f, CheckCircle2 as p, ChevronLeft as m, ChevronRight as h, Clock as g, Download as
|
|
8
|
+
import { AlertCircle as l, ArrowLeft as u, Bell as d, BellOff as f, CheckCircle2 as p, ChevronLeft as m, ChevronRight as h, Clock as g, Download as te, Loader2 as _, RefreshCw as ne, User as re, UserCheck as v, XCircle as y } from "lucide-react";
|
|
9
9
|
import { useNavigate as b, useParams as ie } from "react-router-dom";
|
|
10
|
-
import { usePermissions as
|
|
11
|
-
import { Fragment as
|
|
12
|
-
import { CTAOverflowMenu as
|
|
10
|
+
import { usePermissions as x } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
11
|
+
import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
12
|
+
import { CTAOverflowMenu as T, GlassCard as E, IllustratedEmptyState as ae, ResponsiveTable as oe } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
//#region src/channel/modules/channels/pages/ChannelUsersPage.tsx
|
|
14
|
-
var
|
|
14
|
+
var D = {
|
|
15
15
|
ONLINE: {
|
|
16
16
|
label: "Online",
|
|
17
17
|
color: "text-status-success-text"
|
|
@@ -28,7 +28,7 @@ var E = {
|
|
|
28
28
|
label: "Do Not Disturb",
|
|
29
29
|
color: "text-status-error-text"
|
|
30
30
|
}
|
|
31
|
-
},
|
|
31
|
+
}, O = {
|
|
32
32
|
PENDING: {
|
|
33
33
|
label: "Pending",
|
|
34
34
|
color: "text-status-warning-text bg-status-warning-bg-subtle",
|
|
@@ -45,24 +45,24 @@ var E = {
|
|
|
45
45
|
icon: y
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
function
|
|
49
|
-
let { channelId: p } = ie(), g = b(), { hasPermission:
|
|
50
|
-
let n =
|
|
48
|
+
function k() {
|
|
49
|
+
let { channelId: p } = ie(), g = b(), { hasPermission: k } = x(), A = k(r.CHANNEL_USER_EXPORT), { t: j } = c(), M = (e, t) => {
|
|
50
|
+
let n = j(e);
|
|
51
51
|
return n === e ? t : n;
|
|
52
|
-
}, { organizationId:
|
|
52
|
+
}, { organizationId: N } = e(), { triggerExport: P, isExporting: F, exportError: I } = ee(N ?? void 0, p), [L, R] = s(!0), [z, B] = s([]), [V, H] = s(25), [U, se] = s(0), [W, G] = s(0), { channel: K, isLoading: ce, error: q, fetchUserPlatformConnections: J } = t(p), Y = i(async (e = 0) => {
|
|
53
53
|
if (p) {
|
|
54
|
-
|
|
54
|
+
R(!0);
|
|
55
55
|
try {
|
|
56
|
-
let t = await J(
|
|
57
|
-
|
|
56
|
+
let t = await J(V, e * V);
|
|
57
|
+
B(t.items), se(t.total);
|
|
58
58
|
} finally {
|
|
59
|
-
|
|
59
|
+
R(!1);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}, [
|
|
63
63
|
p,
|
|
64
64
|
J,
|
|
65
|
-
|
|
65
|
+
V
|
|
66
66
|
]), X = o(Y);
|
|
67
67
|
a(() => {
|
|
68
68
|
X.current = Y;
|
|
@@ -74,30 +74,30 @@ function O() {
|
|
|
74
74
|
Q.current = !1;
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
|
-
}, [
|
|
77
|
+
}, [V]), a(() => {
|
|
78
78
|
if (Z.current) {
|
|
79
79
|
Z.current = !1;
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
Q.current || Promise.resolve().then(() => X.current(W));
|
|
83
83
|
}, [W]);
|
|
84
|
-
let $ = Math.max(1, Math.ceil(
|
|
84
|
+
let $ = Math.max(1, Math.ceil(U / V)), le = W > 0, ue = W < $ - 1, de = (e) => D[e] || D.OFFLINE, fe = (e) => O[e] || O.EXPIRED, pe = [
|
|
85
85
|
{
|
|
86
86
|
key: "user",
|
|
87
|
-
header:
|
|
87
|
+
header: M("channel.user", "User"),
|
|
88
88
|
priority: "primary",
|
|
89
|
-
cell: (e) => /* @__PURE__ */
|
|
89
|
+
cell: (e) => /* @__PURE__ */ w("div", {
|
|
90
90
|
className: "flex items-center gap-3",
|
|
91
|
-
children: [/* @__PURE__ */
|
|
91
|
+
children: [/* @__PURE__ */ C("div", {
|
|
92
92
|
className: "size-8 bg-accent-primary/10 rounded-full flex items-center justify-center",
|
|
93
|
-
children: /* @__PURE__ */
|
|
93
|
+
children: /* @__PURE__ */ C(re, {
|
|
94
94
|
size: 16,
|
|
95
95
|
className: "text-accent-primary"
|
|
96
96
|
})
|
|
97
|
-
}), /* @__PURE__ */
|
|
97
|
+
}), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
|
|
98
98
|
className: "text-sm font-medium text-text-primary",
|
|
99
99
|
children: e.externalDisplayName || "Unknown"
|
|
100
|
-
}), e.externalUsername && /* @__PURE__ */
|
|
100
|
+
}), e.externalUsername && /* @__PURE__ */ C("p", {
|
|
101
101
|
className: "text-xs text-text-secondary",
|
|
102
102
|
children: e.externalUsername
|
|
103
103
|
})] })]
|
|
@@ -105,29 +105,29 @@ function O() {
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
key: "platformId",
|
|
108
|
-
header:
|
|
108
|
+
header: M("channel.platformId", "Platform ID"),
|
|
109
109
|
priority: "secondary",
|
|
110
110
|
cellClassName: "text-sm font-mono text-text-secondary",
|
|
111
111
|
cell: (e) => e.externalUserId
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
key: "source",
|
|
115
|
-
header:
|
|
115
|
+
header: M("channel.source", "Source"),
|
|
116
116
|
priority: "secondary",
|
|
117
|
-
cell: (e) => /* @__PURE__ */
|
|
117
|
+
cell: (e) => /* @__PURE__ */ C("span", {
|
|
118
118
|
className: "text-xs text-text-secondary bg-bg-sunken px-2 py-1 rounded",
|
|
119
119
|
children: e.source
|
|
120
120
|
})
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
key: "status",
|
|
124
|
-
header:
|
|
124
|
+
header: M("channel.status", "Status"),
|
|
125
125
|
priority: "primary",
|
|
126
126
|
cell: (e) => {
|
|
127
|
-
let t =
|
|
128
|
-
return /* @__PURE__ */
|
|
127
|
+
let t = de(e.presenceStatus);
|
|
128
|
+
return /* @__PURE__ */ w("div", {
|
|
129
129
|
className: "flex items-center gap-2",
|
|
130
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ C("span", { className: `w-2 h-2 rounded-full ${t.color.replace("text-", "bg-")}` }), /* @__PURE__ */ C("span", {
|
|
131
131
|
className: `text-sm ${t.color}`,
|
|
132
132
|
children: t.label
|
|
133
133
|
})]
|
|
@@ -136,14 +136,14 @@ function O() {
|
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
key: "pairing",
|
|
139
|
-
header:
|
|
139
|
+
header: M("channel.pairing", "Pairing"),
|
|
140
140
|
priority: "secondary",
|
|
141
141
|
cell: (e) => {
|
|
142
|
-
let t = e.pairingStatus ?
|
|
143
|
-
return e.pairingStatus && t ? /* @__PURE__ */
|
|
142
|
+
let t = e.pairingStatus ? fe(e.pairingStatus) : null, n = t?.icon ?? y;
|
|
143
|
+
return e.pairingStatus && t ? /* @__PURE__ */ w("span", {
|
|
144
144
|
className: `inline-flex items-center gap-1 text-xs px-2 py-1 rounded ${t.color}`,
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
-
}) : /* @__PURE__ */
|
|
145
|
+
children: [/* @__PURE__ */ C(n, { size: 12 }), t.label]
|
|
146
|
+
}) : /* @__PURE__ */ C("span", {
|
|
147
147
|
className: "text-xs text-text-secondary",
|
|
148
148
|
children: "-"
|
|
149
149
|
});
|
|
@@ -151,63 +151,63 @@ function O() {
|
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
key: "notifications",
|
|
154
|
-
header:
|
|
154
|
+
header: M("channel.notifications", "Notifications"),
|
|
155
155
|
priority: "secondary",
|
|
156
|
-
cell: (e) => e.notificationsEnabled ? /* @__PURE__ */
|
|
156
|
+
cell: (e) => e.notificationsEnabled ? /* @__PURE__ */ w("span", {
|
|
157
157
|
className: "inline-flex items-center gap-1",
|
|
158
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ C(d, {
|
|
159
159
|
size: 16,
|
|
160
160
|
className: "text-status-success-text",
|
|
161
161
|
"aria-hidden": "true"
|
|
162
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ C("span", {
|
|
163
163
|
className: "sr-only",
|
|
164
|
-
children:
|
|
164
|
+
children: M("channel.enabled", "Enabled")
|
|
165
165
|
})]
|
|
166
|
-
}) : /* @__PURE__ */
|
|
166
|
+
}) : /* @__PURE__ */ w("span", {
|
|
167
167
|
className: "inline-flex items-center gap-1",
|
|
168
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ C(f, {
|
|
169
169
|
size: 16,
|
|
170
170
|
className: "text-text-secondary",
|
|
171
171
|
"aria-hidden": "true"
|
|
172
|
-
}), /* @__PURE__ */
|
|
172
|
+
}), /* @__PURE__ */ C("span", {
|
|
173
173
|
className: "sr-only",
|
|
174
|
-
children:
|
|
174
|
+
children: M("channel.disabled", "Disabled")
|
|
175
175
|
})]
|
|
176
176
|
})
|
|
177
177
|
},
|
|
178
178
|
{
|
|
179
179
|
key: "dmAllowed",
|
|
180
|
-
header:
|
|
180
|
+
header: M("channel.dmAllowed", "DM Allowed"),
|
|
181
181
|
priority: "secondary",
|
|
182
|
-
cell: (e) => e.dmAllowed ? /* @__PURE__ */
|
|
182
|
+
cell: (e) => e.dmAllowed ? /* @__PURE__ */ w("span", {
|
|
183
183
|
className: "inline-flex items-center gap-1 text-sm text-status-success-text",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
185
|
-
}) : /* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ C(v, { size: 16 }), M("channel.allowed", "Allowed")]
|
|
185
|
+
}) : /* @__PURE__ */ w("span", {
|
|
186
186
|
className: "inline-flex items-center gap-1 text-sm text-text-secondary",
|
|
187
|
-
children: [/* @__PURE__ */
|
|
187
|
+
children: [/* @__PURE__ */ C(y, { size: 16 }), M("channel.blocked", "Blocked")]
|
|
188
188
|
})
|
|
189
189
|
}
|
|
190
190
|
];
|
|
191
|
-
return
|
|
191
|
+
return ce ? /* @__PURE__ */ C("div", {
|
|
192
192
|
className: "flex items-center justify-center min-h-screen",
|
|
193
|
-
children: /* @__PURE__ */
|
|
193
|
+
children: /* @__PURE__ */ C(_, {
|
|
194
194
|
size: 32,
|
|
195
195
|
className: "animate-spin text-accent-primary"
|
|
196
196
|
})
|
|
197
|
-
}) : q || !K ? /* @__PURE__ */
|
|
197
|
+
}) : q || !K ? /* @__PURE__ */ C("div", {
|
|
198
198
|
className: "p-6",
|
|
199
|
-
children: /* @__PURE__ */
|
|
199
|
+
children: /* @__PURE__ */ w("div", {
|
|
200
200
|
className: "flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg",
|
|
201
201
|
children: [
|
|
202
|
-
/* @__PURE__ */
|
|
202
|
+
/* @__PURE__ */ C(l, {
|
|
203
203
|
size: 20,
|
|
204
204
|
className: "text-status-error-text"
|
|
205
205
|
}),
|
|
206
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ C("div", { children: /* @__PURE__ */ C("p", {
|
|
207
207
|
className: "text-sm font-medium text-status-error-text",
|
|
208
208
|
children: q || "Channel not found"
|
|
209
209
|
}) }),
|
|
210
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ C("button", {
|
|
211
211
|
type: "button",
|
|
212
212
|
onClick: () => g(`/channels/${p}`),
|
|
213
213
|
className: "ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors",
|
|
@@ -215,138 +215,141 @@ function O() {
|
|
|
215
215
|
})
|
|
216
216
|
]
|
|
217
217
|
})
|
|
218
|
-
}) : /* @__PURE__ */
|
|
218
|
+
}) : /* @__PURE__ */ C(n, {
|
|
219
219
|
permission: r.USER_PLATFORM_CONNECTION_READ,
|
|
220
|
-
children: /* @__PURE__ */
|
|
220
|
+
children: /* @__PURE__ */ w("div", {
|
|
221
221
|
className: "space-y-6 p-3 sm:p-6",
|
|
222
222
|
children: [
|
|
223
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ w("div", {
|
|
224
224
|
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
225
|
-
children: [/* @__PURE__ */
|
|
225
|
+
children: [/* @__PURE__ */ w("div", {
|
|
226
226
|
className: "flex items-center gap-3",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
227
|
+
children: [/* @__PURE__ */ C("button", {
|
|
228
228
|
type: "button",
|
|
229
229
|
onClick: () => g(`/channels/${p}`),
|
|
230
|
-
"aria-label":
|
|
230
|
+
"aria-label": M("channel.backToChannel", "Back to channel"),
|
|
231
231
|
className: "p-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors",
|
|
232
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ C(u, {
|
|
233
233
|
size: 20,
|
|
234
234
|
"aria-hidden": "true"
|
|
235
235
|
})
|
|
236
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("h1", {
|
|
237
237
|
className: "text-xl sm:text-2xl font-bold text-text-primary",
|
|
238
|
-
children:
|
|
239
|
-
}), /* @__PURE__ */
|
|
238
|
+
children: M("channel.channelUsers", "Channel Users")
|
|
239
|
+
}), /* @__PURE__ */ w("p", {
|
|
240
240
|
className: "text-sm text-text-secondary mt-1",
|
|
241
241
|
children: [K.name, " - User connections"]
|
|
242
242
|
})] })]
|
|
243
|
-
}), /* @__PURE__ */
|
|
243
|
+
}), /* @__PURE__ */ w("div", {
|
|
244
244
|
className: "flex items-center gap-3",
|
|
245
|
-
children: [/* @__PURE__ */
|
|
246
|
-
value:
|
|
247
|
-
onChange: (e) =>
|
|
245
|
+
children: [/* @__PURE__ */ w("select", {
|
|
246
|
+
value: V,
|
|
247
|
+
onChange: (e) => H(Number(e.target.value)),
|
|
248
248
|
className: "px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-text-primary",
|
|
249
249
|
children: [
|
|
250
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ C("option", {
|
|
251
251
|
value: 10,
|
|
252
252
|
children: "10 results"
|
|
253
253
|
}),
|
|
254
|
-
/* @__PURE__ */
|
|
254
|
+
/* @__PURE__ */ C("option", {
|
|
255
255
|
value: 25,
|
|
256
256
|
children: "25 results"
|
|
257
257
|
}),
|
|
258
|
-
/* @__PURE__ */
|
|
258
|
+
/* @__PURE__ */ C("option", {
|
|
259
259
|
value: 50,
|
|
260
260
|
children: "50 results"
|
|
261
261
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ C("option", {
|
|
263
263
|
value: 100,
|
|
264
264
|
children: "100 results"
|
|
265
265
|
})
|
|
266
266
|
]
|
|
267
|
-
}), /* @__PURE__ */
|
|
268
|
-
label:
|
|
269
|
-
icon: /* @__PURE__ */
|
|
267
|
+
}), /* @__PURE__ */ C(T, { actions: [{
|
|
268
|
+
label: M("channel.refresh", "Refresh"),
|
|
269
|
+
icon: /* @__PURE__ */ C(ne, {
|
|
270
270
|
size: 16,
|
|
271
|
-
className:
|
|
271
|
+
className: L ? "animate-spin" : "",
|
|
272
272
|
"aria-hidden": "true"
|
|
273
273
|
}),
|
|
274
|
-
disabled:
|
|
274
|
+
disabled: L,
|
|
275
275
|
onSelect: () => void Y(W)
|
|
276
|
-
}, ...
|
|
277
|
-
label:
|
|
278
|
-
icon:
|
|
276
|
+
}, ...A ? [{
|
|
277
|
+
label: M("channel.export", "Export"),
|
|
278
|
+
icon: F ? /* @__PURE__ */ C(_, {
|
|
279
279
|
size: 16,
|
|
280
280
|
className: "animate-spin",
|
|
281
281
|
"aria-hidden": "true"
|
|
282
|
-
}) : /* @__PURE__ */
|
|
282
|
+
}) : /* @__PURE__ */ C(te, {
|
|
283
283
|
size: 16,
|
|
284
284
|
"aria-hidden": "true"
|
|
285
285
|
}),
|
|
286
|
-
disabled:
|
|
287
|
-
onSelect: () => void
|
|
286
|
+
disabled: F,
|
|
287
|
+
onSelect: () => void P()
|
|
288
288
|
}] : []] })]
|
|
289
289
|
})]
|
|
290
290
|
}),
|
|
291
|
-
|
|
291
|
+
I && /* @__PURE__ */ w("div", {
|
|
292
292
|
className: "p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3",
|
|
293
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ C(l, {
|
|
294
294
|
size: 20,
|
|
295
295
|
className: "text-status-error-text"
|
|
296
|
-
}), /* @__PURE__ */
|
|
296
|
+
}), /* @__PURE__ */ C("p", {
|
|
297
297
|
className: "text-sm text-status-error-text flex-1",
|
|
298
|
-
children:
|
|
298
|
+
children: I
|
|
299
299
|
})]
|
|
300
300
|
}),
|
|
301
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ C(E, {
|
|
302
302
|
treatment: "insight",
|
|
303
303
|
className: "p-4 border-border-seam",
|
|
304
|
-
children: /* @__PURE__ */
|
|
304
|
+
children: /* @__PURE__ */ C("p", {
|
|
305
305
|
className: "text-sm text-text-secondary",
|
|
306
306
|
children: "Users connected to this channel through OAuth or pairing. Manage notification preferences and DM authorization here."
|
|
307
307
|
})
|
|
308
308
|
}),
|
|
309
|
-
/* @__PURE__ */
|
|
310
|
-
columns:
|
|
311
|
-
data:
|
|
309
|
+
/* @__PURE__ */ C(oe, {
|
|
310
|
+
columns: pe,
|
|
311
|
+
data: z,
|
|
312
312
|
rowKey: (e) => e.id,
|
|
313
|
-
emptyState:
|
|
313
|
+
emptyState: /* @__PURE__ */ C(ae, {
|
|
314
|
+
illustration: "empty-people",
|
|
315
|
+
title: M("channel.noUsersConnected", "No users connected to this channel")
|
|
316
|
+
}),
|
|
314
317
|
className: "bg-surface border border-border-seam rounded-lg"
|
|
315
318
|
}),
|
|
316
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ w("div", {
|
|
317
320
|
className: "flex items-center justify-between",
|
|
318
|
-
children: [/* @__PURE__ */
|
|
321
|
+
children: [/* @__PURE__ */ C("p", {
|
|
319
322
|
className: "text-sm text-text-secondary",
|
|
320
|
-
children:
|
|
321
|
-
|
|
323
|
+
children: U > 0 && /* @__PURE__ */ w(S, { children: [
|
|
324
|
+
M("channel.showing", "Showing"),
|
|
322
325
|
" ",
|
|
323
|
-
/* @__PURE__ */
|
|
326
|
+
/* @__PURE__ */ w("span", {
|
|
324
327
|
className: "font-medium",
|
|
325
328
|
children: [
|
|
326
|
-
W *
|
|
329
|
+
W * V + 1,
|
|
327
330
|
"–",
|
|
328
|
-
Math.min((W + 1) *
|
|
331
|
+
Math.min((W + 1) * V, U)
|
|
329
332
|
]
|
|
330
333
|
}),
|
|
331
334
|
" ",
|
|
332
|
-
|
|
335
|
+
M("channel.of", "of"),
|
|
333
336
|
" ",
|
|
334
|
-
/* @__PURE__ */
|
|
337
|
+
/* @__PURE__ */ C("span", {
|
|
335
338
|
className: "font-medium",
|
|
336
|
-
children:
|
|
339
|
+
children: U
|
|
337
340
|
})
|
|
338
341
|
] })
|
|
339
|
-
}), /* @__PURE__ */
|
|
342
|
+
}), /* @__PURE__ */ w("div", {
|
|
340
343
|
className: "flex items-center gap-2",
|
|
341
344
|
children: [
|
|
342
|
-
/* @__PURE__ */
|
|
345
|
+
/* @__PURE__ */ w("button", {
|
|
343
346
|
type: "button",
|
|
344
347
|
onClick: () => G((e) => Math.max(0, e - 1)),
|
|
345
|
-
disabled: !
|
|
348
|
+
disabled: !le || L,
|
|
346
349
|
className: "flex items-center gap-1 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",
|
|
347
|
-
children: [/* @__PURE__ */
|
|
350
|
+
children: [/* @__PURE__ */ C(m, { size: 16 }), M("channel.previous", "Previous")]
|
|
348
351
|
}),
|
|
349
|
-
/* @__PURE__ */
|
|
352
|
+
/* @__PURE__ */ w("span", {
|
|
350
353
|
className: "text-sm text-text-secondary",
|
|
351
354
|
children: [
|
|
352
355
|
W + 1,
|
|
@@ -354,12 +357,12 @@ function O() {
|
|
|
354
357
|
$
|
|
355
358
|
]
|
|
356
359
|
}),
|
|
357
|
-
/* @__PURE__ */
|
|
360
|
+
/* @__PURE__ */ w("button", {
|
|
358
361
|
type: "button",
|
|
359
362
|
onClick: () => G((e) => Math.min($ - 1, e + 1)),
|
|
360
|
-
disabled: !
|
|
363
|
+
disabled: !ue || L,
|
|
361
364
|
className: "flex items-center gap-1 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",
|
|
362
|
-
children: [
|
|
365
|
+
children: [M("channel.next", "Next"), /* @__PURE__ */ C(h, { size: 16 })]
|
|
363
366
|
})
|
|
364
367
|
]
|
|
365
368
|
})]
|
|
@@ -369,6 +372,6 @@ function O() {
|
|
|
369
372
|
});
|
|
370
373
|
}
|
|
371
374
|
//#endregion
|
|
372
|
-
export {
|
|
375
|
+
export { k as ChannelUsersPage };
|
|
373
376
|
|
|
374
377
|
//# sourceMappingURL=ChannelUsersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelUsersPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelUsersPage.tsx"],"sourcesContent":["/**\n * Channel Users Page\n *\n * Displays user platform connections for a specific channel\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n Clock,\n User,\n UserCheck,\n Bell,\n BellOff,\n RefreshCw,\n Download,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport {\n CTAOverflowMenu,\n GlassCard,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n useChannel,\n type UserPlatformConnection,\n type UserPresenceStatus,\n type PairingStatus,\n} from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useExportChannelUsers } from '../hooks/useExport';\n\nconst PRESENCE_CONFIG: Record<UserPresenceStatus, { label: string; color: string }> = {\n ONLINE: { label: 'Online', color: 'text-status-success-text' },\n OFFLINE: { label: 'Offline', color: 'text-text-secondary' },\n AWAY: { label: 'Away', color: 'text-status-warning-text' },\n DND: { label: 'Do Not Disturb', color: 'text-status-error-text' },\n};\n\nconst PAIRING_CONFIG: Record<\n PairingStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n PENDING: {\n label: 'Pending',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: Clock,\n },\n APPROVED: {\n label: 'Approved',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n EXPIRED: { label: 'Expired', color: 'text-text-secondary bg-bg-sunken', icon: XCircle },\n};\n\nexport function ChannelUsersPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n const canExport = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_USER_EXPORT);\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 const { organizationId } = useChannelContext();\n const {\n triggerExport: exportUsers,\n isExporting: isExportingUsers,\n exportError: exportUsersError,\n } = useExportChannelUsers(organizationId ?? undefined, channelId);\n const [isLoadingUsers, setIsLoadingUsers] = useState(true);\n const [users, setUsers] = useState<UserPlatformConnection[]>([]);\n const [limit, setLimit] = useState(25);\n const [total, setTotal] = useState(0);\n const [currentPage, setCurrentPage] = useState(0);\n\n const { channel, isLoading, error, fetchUserPlatformConnections } = useChannel(channelId);\n\n const loadUsers = useCallback(\n async (page: number = 0) => {\n if (!channelId) return;\n setIsLoadingUsers(true);\n try {\n const result = await fetchUserPlatformConnections(limit, page * limit);\n setUsers(result.items);\n setTotal(result.total);\n } finally {\n setIsLoadingUsers(false);\n }\n },\n [channelId, fetchUserPlatformConnections, limit]\n );\n\n const loadUsersRef = useRef(loadUsers);\n useEffect(() => {\n loadUsersRef.current = loadUsers;\n });\n const isFirstPageRender = useRef(true);\n // Prevents the [currentPage] effect from double-loading when limit changes\n // reset the page back to 0 (the [limit] effect already handles that load).\n const limitChangingRef = useRef(false);\n\n useEffect(() => {\n void Promise.resolve().then(() => {\n // Set the guard before resetting page so the [currentPage] effect skips\n // the redundant load that would otherwise fire when page resets to 0.\n limitChangingRef.current = true;\n setCurrentPage(0);\n void loadUsersRef.current(0).finally(() => {\n limitChangingRef.current = false;\n });\n });\n }, [limit]);\n\n useEffect(() => {\n if (isFirstPageRender.current) {\n isFirstPageRender.current = false;\n return;\n }\n if (limitChangingRef.current) return;\n void Promise.resolve().then(() => loadUsersRef.current(currentPage));\n }, [currentPage]);\n\n const totalPages = Math.max(1, Math.ceil(total / limit));\n const hasPrevPage = currentPage > 0;\n const hasNextPage = currentPage < totalPages - 1;\n\n const getPresenceConfig = (status: UserPresenceStatus) => {\n return PRESENCE_CONFIG[status] || PRESENCE_CONFIG.OFFLINE;\n };\n\n const getPairingConfig = (status: PairingStatus) => {\n return PAIRING_CONFIG[status] || PAIRING_CONFIG.EXPIRED;\n };\n\n const userColumns: ResponsiveTableColumn<UserPlatformConnection>[] = [\n {\n key: 'user',\n header: tr('channel.user', 'User'),\n priority: 'primary',\n cell: (user) => (\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 bg-accent-primary/10 rounded-full flex items-center justify-center\">\n <User size={16} className=\"text-accent-primary\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {user.externalDisplayName || 'Unknown'}\n </p>\n {user.externalUsername && (\n <p className=\"text-xs text-text-secondary\">{user.externalUsername}</p>\n )}\n </div>\n </div>\n ),\n },\n {\n key: 'platformId',\n header: tr('channel.platformId', 'Platform ID'),\n priority: 'secondary',\n cellClassName: 'text-sm font-mono text-text-secondary',\n cell: (user) => user.externalUserId,\n },\n {\n key: 'source',\n header: tr('channel.source', 'Source'),\n priority: 'secondary',\n cell: (user) => (\n <span className=\"text-xs text-text-secondary bg-bg-sunken px-2 py-1 rounded\">\n {user.source}\n </span>\n ),\n },\n {\n key: 'status',\n header: tr('channel.status', 'Status'),\n priority: 'primary',\n cell: (user) => {\n const presenceConfig = getPresenceConfig(user.presenceStatus as UserPresenceStatus);\n return (\n <div className=\"flex items-center gap-2\">\n <span\n className={`w-2 h-2 rounded-full ${presenceConfig.color.replace('text-', 'bg-')}`}\n />\n <span className={`text-sm ${presenceConfig.color}`}>{presenceConfig.label}</span>\n </div>\n );\n },\n },\n {\n key: 'pairing',\n header: tr('channel.pairing', 'Pairing'),\n priority: 'secondary',\n cell: (user) => {\n const pairingConfig = user.pairingStatus\n ? getPairingConfig(user.pairingStatus as PairingStatus)\n : null;\n const PairingIcon = pairingConfig?.icon ?? XCircle;\n return user.pairingStatus && pairingConfig ? (\n <span\n className={`inline-flex items-center gap-1 text-xs px-2 py-1 rounded ${pairingConfig.color}`}\n >\n <PairingIcon size={12} />\n {pairingConfig.label}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">-</span>\n );\n },\n },\n {\n key: 'notifications',\n header: tr('channel.notifications', 'Notifications'),\n priority: 'secondary',\n cell: (user) =>\n user.notificationsEnabled ? (\n <span className=\"inline-flex items-center gap-1\">\n <Bell size={16} className=\"text-status-success-text\" aria-hidden=\"true\" />\n <span className=\"sr-only\">{tr('channel.enabled', 'Enabled')}</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <BellOff size={16} className=\"text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"sr-only\">{tr('channel.disabled', 'Disabled')}</span>\n </span>\n ),\n },\n {\n key: 'dmAllowed',\n header: tr('channel.dmAllowed', 'DM Allowed'),\n priority: 'secondary',\n cell: (user) =>\n user.dmAllowed ? (\n <span className=\"inline-flex items-center gap-1 text-sm text-status-success-text\">\n <UserCheck size={16} />\n {tr('channel.allowed', 'Allowed')}\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 text-sm text-text-secondary\">\n <XCircle size={16} />\n {tr('channel.blocked', 'Blocked')}\n </span>\n ),\n },\n ];\n\n // Loading state (only full-screen for initial channel load)\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || 'Channel not found'}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channelId}`)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n Go Back\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.USER_PLATFORM_CONNECTION_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n {tr('channel.channelUsers', 'Channel Users')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {channel.name} - User connections\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <select\n value={limit}\n onChange={(e) => setLimit(Number(e.target.value))}\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-text-primary\"\n >\n <option value={10}>10 results</option>\n <option value={25}>25 results</option>\n <option value={50}>50 results</option>\n <option value={100}>100 results</option>\n </select>\n <CTAOverflowMenu\n actions={[\n {\n label: tr('channel.refresh', 'Refresh'),\n icon: (\n <RefreshCw\n size={16}\n className={isLoadingUsers ? 'animate-spin' : ''}\n aria-hidden=\"true\"\n />\n ),\n disabled: isLoadingUsers,\n onSelect: () => void loadUsers(currentPage),\n },\n ...(canExport\n ? [\n {\n label: tr('channel.export', 'Export'),\n icon: isExportingUsers ? (\n <Loader2 size={16} className=\"animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Download size={16} aria-hidden=\"true\" />\n ),\n disabled: isExportingUsers,\n onSelect: () => void exportUsers(),\n },\n ]\n : []),\n ]}\n />\n </div>\n </div>\n\n {/* Export Error Banner */}\n {exportUsersError && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <p className=\"text-sm text-status-error-text flex-1\">{exportUsersError}</p>\n </div>\n )}\n\n {/* Info Banner */}\n <GlassCard treatment=\"insight\" className=\"p-4 border-border-seam\">\n <p className=\"text-sm text-text-secondary\">\n Users connected to this channel through OAuth or pairing. Manage notification\n preferences and DM authorization here.\n </p>\n </GlassCard>\n\n {/* Users List */}\n <ResponsiveTable\n columns={userColumns}\n data={users}\n rowKey={(user) => user.id}\n emptyState={tr('channel.noUsersConnected', 'No users connected to this channel')}\n className=\"bg-surface border border-border-seam rounded-lg\"\n />\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-secondary\">\n {total > 0 && (\n <>\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * limit + 1}–{Math.min((currentPage + 1) * limit, total)}\n </span>{' '}\n {tr('channel.of', 'of')} <span className=\"font-medium\">{total}</span>\n </>\n )}\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={!hasPrevPage || isLoadingUsers}\n className=\"flex items-center gap-1 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 <ChevronLeft size={16} />\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={!hasNextPage || isLoadingUsers}\n className=\"flex items-center gap-1 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 <ChevronRight size={16} />\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChannelUsersPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelUsersPage.tsx"],"sourcesContent":["/**\n * Channel Users Page\n *\n * Displays user platform connections for a specific channel\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n Clock,\n User,\n UserCheck,\n Bell,\n BellOff,\n RefreshCw,\n Download,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport {\n CTAOverflowMenu,\n GlassCard,\n ResponsiveTable,\n IllustratedEmptyState,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n useChannel,\n type UserPlatformConnection,\n type UserPresenceStatus,\n type PairingStatus,\n} from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useExportChannelUsers } from '../hooks/useExport';\n\nconst PRESENCE_CONFIG: Record<UserPresenceStatus, { label: string; color: string }> = {\n ONLINE: { label: 'Online', color: 'text-status-success-text' },\n OFFLINE: { label: 'Offline', color: 'text-text-secondary' },\n AWAY: { label: 'Away', color: 'text-status-warning-text' },\n DND: { label: 'Do Not Disturb', color: 'text-status-error-text' },\n};\n\nconst PAIRING_CONFIG: Record<\n PairingStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n PENDING: {\n label: 'Pending',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: Clock,\n },\n APPROVED: {\n label: 'Approved',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n EXPIRED: { label: 'Expired', color: 'text-text-secondary bg-bg-sunken', icon: XCircle },\n};\n\nexport function ChannelUsersPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n const canExport = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_USER_EXPORT);\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 const { organizationId } = useChannelContext();\n const {\n triggerExport: exportUsers,\n isExporting: isExportingUsers,\n exportError: exportUsersError,\n } = useExportChannelUsers(organizationId ?? undefined, channelId);\n const [isLoadingUsers, setIsLoadingUsers] = useState(true);\n const [users, setUsers] = useState<UserPlatformConnection[]>([]);\n const [limit, setLimit] = useState(25);\n const [total, setTotal] = useState(0);\n const [currentPage, setCurrentPage] = useState(0);\n\n const { channel, isLoading, error, fetchUserPlatformConnections } = useChannel(channelId);\n\n const loadUsers = useCallback(\n async (page: number = 0) => {\n if (!channelId) return;\n setIsLoadingUsers(true);\n try {\n const result = await fetchUserPlatformConnections(limit, page * limit);\n setUsers(result.items);\n setTotal(result.total);\n } finally {\n setIsLoadingUsers(false);\n }\n },\n [channelId, fetchUserPlatformConnections, limit]\n );\n\n const loadUsersRef = useRef(loadUsers);\n useEffect(() => {\n loadUsersRef.current = loadUsers;\n });\n const isFirstPageRender = useRef(true);\n // Prevents the [currentPage] effect from double-loading when limit changes\n // reset the page back to 0 (the [limit] effect already handles that load).\n const limitChangingRef = useRef(false);\n\n useEffect(() => {\n void Promise.resolve().then(() => {\n // Set the guard before resetting page so the [currentPage] effect skips\n // the redundant load that would otherwise fire when page resets to 0.\n limitChangingRef.current = true;\n setCurrentPage(0);\n void loadUsersRef.current(0).finally(() => {\n limitChangingRef.current = false;\n });\n });\n }, [limit]);\n\n useEffect(() => {\n if (isFirstPageRender.current) {\n isFirstPageRender.current = false;\n return;\n }\n if (limitChangingRef.current) return;\n void Promise.resolve().then(() => loadUsersRef.current(currentPage));\n }, [currentPage]);\n\n const totalPages = Math.max(1, Math.ceil(total / limit));\n const hasPrevPage = currentPage > 0;\n const hasNextPage = currentPage < totalPages - 1;\n\n const getPresenceConfig = (status: UserPresenceStatus) => {\n return PRESENCE_CONFIG[status] || PRESENCE_CONFIG.OFFLINE;\n };\n\n const getPairingConfig = (status: PairingStatus) => {\n return PAIRING_CONFIG[status] || PAIRING_CONFIG.EXPIRED;\n };\n\n const userColumns: ResponsiveTableColumn<UserPlatformConnection>[] = [\n {\n key: 'user',\n header: tr('channel.user', 'User'),\n priority: 'primary',\n cell: (user) => (\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 bg-accent-primary/10 rounded-full flex items-center justify-center\">\n <User size={16} className=\"text-accent-primary\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {user.externalDisplayName || 'Unknown'}\n </p>\n {user.externalUsername && (\n <p className=\"text-xs text-text-secondary\">{user.externalUsername}</p>\n )}\n </div>\n </div>\n ),\n },\n {\n key: 'platformId',\n header: tr('channel.platformId', 'Platform ID'),\n priority: 'secondary',\n cellClassName: 'text-sm font-mono text-text-secondary',\n cell: (user) => user.externalUserId,\n },\n {\n key: 'source',\n header: tr('channel.source', 'Source'),\n priority: 'secondary',\n cell: (user) => (\n <span className=\"text-xs text-text-secondary bg-bg-sunken px-2 py-1 rounded\">\n {user.source}\n </span>\n ),\n },\n {\n key: 'status',\n header: tr('channel.status', 'Status'),\n priority: 'primary',\n cell: (user) => {\n const presenceConfig = getPresenceConfig(user.presenceStatus as UserPresenceStatus);\n return (\n <div className=\"flex items-center gap-2\">\n <span\n className={`w-2 h-2 rounded-full ${presenceConfig.color.replace('text-', 'bg-')}`}\n />\n <span className={`text-sm ${presenceConfig.color}`}>{presenceConfig.label}</span>\n </div>\n );\n },\n },\n {\n key: 'pairing',\n header: tr('channel.pairing', 'Pairing'),\n priority: 'secondary',\n cell: (user) => {\n const pairingConfig = user.pairingStatus\n ? getPairingConfig(user.pairingStatus as PairingStatus)\n : null;\n const PairingIcon = pairingConfig?.icon ?? XCircle;\n return user.pairingStatus && pairingConfig ? (\n <span\n className={`inline-flex items-center gap-1 text-xs px-2 py-1 rounded ${pairingConfig.color}`}\n >\n <PairingIcon size={12} />\n {pairingConfig.label}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">-</span>\n );\n },\n },\n {\n key: 'notifications',\n header: tr('channel.notifications', 'Notifications'),\n priority: 'secondary',\n cell: (user) =>\n user.notificationsEnabled ? (\n <span className=\"inline-flex items-center gap-1\">\n <Bell size={16} className=\"text-status-success-text\" aria-hidden=\"true\" />\n <span className=\"sr-only\">{tr('channel.enabled', 'Enabled')}</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <BellOff size={16} className=\"text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"sr-only\">{tr('channel.disabled', 'Disabled')}</span>\n </span>\n ),\n },\n {\n key: 'dmAllowed',\n header: tr('channel.dmAllowed', 'DM Allowed'),\n priority: 'secondary',\n cell: (user) =>\n user.dmAllowed ? (\n <span className=\"inline-flex items-center gap-1 text-sm text-status-success-text\">\n <UserCheck size={16} />\n {tr('channel.allowed', 'Allowed')}\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 text-sm text-text-secondary\">\n <XCircle size={16} />\n {tr('channel.blocked', 'Blocked')}\n </span>\n ),\n },\n ];\n\n // Loading state (only full-screen for initial channel load)\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || 'Channel not found'}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channelId}`)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n Go Back\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.USER_PLATFORM_CONNECTION_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n {tr('channel.channelUsers', 'Channel Users')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {channel.name} - User connections\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <select\n value={limit}\n onChange={(e) => setLimit(Number(e.target.value))}\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-text-primary\"\n >\n <option value={10}>10 results</option>\n <option value={25}>25 results</option>\n <option value={50}>50 results</option>\n <option value={100}>100 results</option>\n </select>\n <CTAOverflowMenu\n actions={[\n {\n label: tr('channel.refresh', 'Refresh'),\n icon: (\n <RefreshCw\n size={16}\n className={isLoadingUsers ? 'animate-spin' : ''}\n aria-hidden=\"true\"\n />\n ),\n disabled: isLoadingUsers,\n onSelect: () => void loadUsers(currentPage),\n },\n ...(canExport\n ? [\n {\n label: tr('channel.export', 'Export'),\n icon: isExportingUsers ? (\n <Loader2 size={16} className=\"animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Download size={16} aria-hidden=\"true\" />\n ),\n disabled: isExportingUsers,\n onSelect: () => void exportUsers(),\n },\n ]\n : []),\n ]}\n />\n </div>\n </div>\n\n {/* Export Error Banner */}\n {exportUsersError && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <p className=\"text-sm text-status-error-text flex-1\">{exportUsersError}</p>\n </div>\n )}\n\n {/* Info Banner */}\n <GlassCard treatment=\"insight\" className=\"p-4 border-border-seam\">\n <p className=\"text-sm text-text-secondary\">\n Users connected to this channel through OAuth or pairing. Manage notification\n preferences and DM authorization here.\n </p>\n </GlassCard>\n\n {/* Users List */}\n <ResponsiveTable\n columns={userColumns}\n data={users}\n rowKey={(user) => user.id}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tr('channel.noUsersConnected', 'No users connected to this channel')}\n />\n }\n className=\"bg-surface border border-border-seam rounded-lg\"\n />\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-secondary\">\n {total > 0 && (\n <>\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * limit + 1}–{Math.min((currentPage + 1) * limit, total)}\n </span>{' '}\n {tr('channel.of', 'of')} <span className=\"font-medium\">{total}</span>\n </>\n )}\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={!hasPrevPage || isLoadingUsers}\n className=\"flex items-center gap-1 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 <ChevronLeft size={16} />\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={!hasNextPage || isLoadingUsers}\n className=\"flex items-center gap-1 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 <ChevronRight size={16} />\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|