@burdenoff/microfe-channel 2026.520.1 → 2026.520.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.d.ts.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +226 -224
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +4 -4
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +2 -2
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +65 -57
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +43 -41
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +183 -179
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +2 -2
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +165 -159
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@ import { useChannel as t } from "../../../hooks/useChannels.js";
|
|
|
3
3
|
import { PermissionGuard as n } from "../../../components/PermissionGuard.js";
|
|
4
4
|
import { CHANNEL_PERMISSIONS as r } from "../../../constants/permissions.js";
|
|
5
5
|
import { useExportChannelUsers as i } from "../hooks/useExport.js";
|
|
6
|
-
import { useCallback as a, useEffect as o,
|
|
7
|
-
import { useI18n as
|
|
8
|
-
import { AlertCircle as
|
|
6
|
+
import { useCallback as a, useEffect as o, useRef as s, useState as c } from "react";
|
|
7
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { AlertCircle as u, ArrowLeft as d, Bell as f, BellOff as p, CheckCircle2 as m, ChevronLeft as ee, ChevronRight as h, Clock as g, Download as _, Loader2 as v, RefreshCw as y, User as b, UserCheck as x, XCircle as S } from "lucide-react";
|
|
9
9
|
import { useNavigate as C, useParams as w } from "react-router-dom";
|
|
10
10
|
import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
11
11
|
//#region src/channel/modules/channels/pages/ChannelUsersPage.tsx
|
|
@@ -35,7 +35,7 @@ var O = {
|
|
|
35
35
|
APPROVED: {
|
|
36
36
|
label: "Approved",
|
|
37
37
|
color: "text-status-success-text bg-status-success-bg-subtle",
|
|
38
|
-
icon:
|
|
38
|
+
icon: m
|
|
39
39
|
},
|
|
40
40
|
EXPIRED: {
|
|
41
41
|
label: "Expired",
|
|
@@ -44,55 +44,63 @@ var O = {
|
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
function A() {
|
|
47
|
-
let { channelId:
|
|
47
|
+
let { channelId: m } = w(), g = C(), { t: A } = l(), j = (e, t) => {
|
|
48
48
|
let n = A(e);
|
|
49
49
|
return n === e ? t : n;
|
|
50
|
-
}, { organizationId: M } = e(), { triggerExport:
|
|
51
|
-
if (
|
|
52
|
-
|
|
50
|
+
}, { organizationId: M } = e(), { triggerExport: te, isExporting: N, exportError: P } = i(M ?? void 0, m), [F, I] = c(!0), [L, R] = c([]), [z, B] = c(25), [V, H] = c(0), [U, W] = c(0), { channel: G, isLoading: K, error: q, fetchUserPlatformConnections: J } = t(m), Y = a(async (e = 0) => {
|
|
51
|
+
if (m) {
|
|
52
|
+
I(!0);
|
|
53
53
|
try {
|
|
54
|
-
let t = await
|
|
55
|
-
|
|
54
|
+
let t = await J(z, e * z);
|
|
55
|
+
R(t.items), H(t.total);
|
|
56
56
|
} finally {
|
|
57
|
-
|
|
57
|
+
I(!1);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
}, [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
]);
|
|
61
|
+
m,
|
|
62
|
+
J,
|
|
63
|
+
z
|
|
64
|
+
]), X = s(Y);
|
|
65
|
+
o(() => {
|
|
66
|
+
X.current = Y;
|
|
67
|
+
});
|
|
68
|
+
let Z = s(!0), Q = s(!1);
|
|
65
69
|
o(() => {
|
|
66
70
|
Promise.resolve().then(() => {
|
|
67
|
-
|
|
71
|
+
Q.current = !0, W(0), X.current(0).finally(() => {
|
|
72
|
+
Q.current = !1;
|
|
73
|
+
});
|
|
68
74
|
});
|
|
69
|
-
}, [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
}, [z]), o(() => {
|
|
76
|
+
if (Z.current) {
|
|
77
|
+
Z.current = !1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
Q.current || Promise.resolve().then(() => X.current(U));
|
|
81
|
+
}, [U]);
|
|
82
|
+
let $ = Math.max(1, Math.ceil(V / z)), ne = U > 0, re = U < $ - 1, ie = (e) => O[e] || O.OFFLINE, ae = (e) => k[e] || k.EXPIRED;
|
|
83
|
+
return K ? /* @__PURE__ */ E("div", {
|
|
76
84
|
className: "flex items-center justify-center min-h-screen",
|
|
77
85
|
children: /* @__PURE__ */ E(v, {
|
|
78
86
|
size: 32,
|
|
79
87
|
className: "animate-spin text-accent-primary"
|
|
80
88
|
})
|
|
81
|
-
}) :
|
|
89
|
+
}) : q || !G ? /* @__PURE__ */ E("div", {
|
|
82
90
|
className: "p-6",
|
|
83
91
|
children: /* @__PURE__ */ D("div", {
|
|
84
92
|
className: "flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg",
|
|
85
93
|
children: [
|
|
86
|
-
/* @__PURE__ */ E(
|
|
94
|
+
/* @__PURE__ */ E(u, {
|
|
87
95
|
size: 20,
|
|
88
96
|
className: "text-status-error-text"
|
|
89
97
|
}),
|
|
90
98
|
/* @__PURE__ */ E("div", { children: /* @__PURE__ */ E("p", {
|
|
91
99
|
className: "text-sm font-medium text-status-error-text",
|
|
92
|
-
children:
|
|
100
|
+
children: q || "Channel not found"
|
|
93
101
|
}) }),
|
|
94
102
|
/* @__PURE__ */ E("button", {
|
|
95
|
-
onClick: () => g(`/channels/${
|
|
103
|
+
onClick: () => g(`/channels/${m}`),
|
|
96
104
|
className: "ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors",
|
|
97
105
|
children: "Go Back"
|
|
98
106
|
})
|
|
@@ -108,10 +116,10 @@ function A() {
|
|
|
108
116
|
children: [/* @__PURE__ */ D("div", {
|
|
109
117
|
className: "flex items-center gap-3",
|
|
110
118
|
children: [/* @__PURE__ */ E("button", {
|
|
111
|
-
onClick: () => g(`/channels/${
|
|
119
|
+
onClick: () => g(`/channels/${m}`),
|
|
112
120
|
"aria-label": j("channel.backToChannel", "Back to channel"),
|
|
113
121
|
className: "p-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors",
|
|
114
|
-
children: /* @__PURE__ */ E(
|
|
122
|
+
children: /* @__PURE__ */ E(d, {
|
|
115
123
|
size: 20,
|
|
116
124
|
"aria-hidden": "true"
|
|
117
125
|
})
|
|
@@ -120,14 +128,14 @@ function A() {
|
|
|
120
128
|
children: j("channel.channelUsers", "Channel Users")
|
|
121
129
|
}), /* @__PURE__ */ D("p", {
|
|
122
130
|
className: "text-sm text-muted-foreground mt-1",
|
|
123
|
-
children: [
|
|
131
|
+
children: [G.name, " - User connections"]
|
|
124
132
|
})] })]
|
|
125
133
|
}), /* @__PURE__ */ D("div", {
|
|
126
134
|
className: "flex items-center gap-3",
|
|
127
135
|
children: [
|
|
128
136
|
/* @__PURE__ */ D("select", {
|
|
129
|
-
value:
|
|
130
|
-
onChange: (e) =>
|
|
137
|
+
value: z,
|
|
138
|
+
onChange: (e) => B(Number(e.target.value)),
|
|
131
139
|
className: "px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-foreground",
|
|
132
140
|
children: [
|
|
133
141
|
/* @__PURE__ */ E("option", {
|
|
@@ -149,22 +157,22 @@ function A() {
|
|
|
149
157
|
]
|
|
150
158
|
}),
|
|
151
159
|
/* @__PURE__ */ D("button", {
|
|
152
|
-
onClick: () => void
|
|
153
|
-
disabled:
|
|
160
|
+
onClick: () => void Y(U),
|
|
161
|
+
disabled: F,
|
|
154
162
|
className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50",
|
|
155
163
|
children: [/* @__PURE__ */ E(y, {
|
|
156
164
|
size: 16,
|
|
157
|
-
className:
|
|
165
|
+
className: F ? "animate-spin" : ""
|
|
158
166
|
}), j("channel.refresh", "Refresh")]
|
|
159
167
|
}),
|
|
160
168
|
/* @__PURE__ */ E(n, {
|
|
161
169
|
permission: r.CHANNEL_ADMIN,
|
|
162
170
|
children: /* @__PURE__ */ D("button", {
|
|
163
|
-
onClick: () => void
|
|
164
|
-
disabled:
|
|
171
|
+
onClick: () => void te(),
|
|
172
|
+
disabled: N,
|
|
165
173
|
className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
166
174
|
title: j("channel.exportCSV", "Export to CSV"),
|
|
167
|
-
children: [
|
|
175
|
+
children: [N ? /* @__PURE__ */ E(v, {
|
|
168
176
|
size: 16,
|
|
169
177
|
className: "animate-spin"
|
|
170
178
|
}) : /* @__PURE__ */ E(_, { size: 16 }), j("channel.export", "Export")]
|
|
@@ -173,14 +181,14 @@ function A() {
|
|
|
173
181
|
]
|
|
174
182
|
})]
|
|
175
183
|
}),
|
|
176
|
-
|
|
184
|
+
P && /* @__PURE__ */ D("div", {
|
|
177
185
|
className: "p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3",
|
|
178
|
-
children: [/* @__PURE__ */ E(
|
|
186
|
+
children: [/* @__PURE__ */ E(u, {
|
|
179
187
|
size: 20,
|
|
180
188
|
className: "text-status-error-text"
|
|
181
189
|
}), /* @__PURE__ */ E("p", {
|
|
182
190
|
className: "text-sm text-status-error-text flex-1",
|
|
183
|
-
children:
|
|
191
|
+
children: P
|
|
184
192
|
})]
|
|
185
193
|
}),
|
|
186
194
|
/* @__PURE__ */ E("div", {
|
|
@@ -230,12 +238,12 @@ function A() {
|
|
|
230
238
|
] })
|
|
231
239
|
}), /* @__PURE__ */ E("tbody", {
|
|
232
240
|
className: "divide-y divide-border",
|
|
233
|
-
children:
|
|
241
|
+
children: L.length === 0 ? /* @__PURE__ */ E("tr", { children: /* @__PURE__ */ E("td", {
|
|
234
242
|
colSpan: 7,
|
|
235
243
|
className: "px-4 py-8 text-center text-muted-foreground",
|
|
236
244
|
children: j("channel.noUsersConnected", "No users connected to this channel")
|
|
237
|
-
}) }) :
|
|
238
|
-
let t =
|
|
245
|
+
}) }) : L.map((e) => {
|
|
246
|
+
let t = ie(e.presenceStatus), n = e.pairingStatus ? ae(e.pairingStatus) : null, r = n?.icon ?? S;
|
|
239
247
|
return /* @__PURE__ */ D("tr", {
|
|
240
248
|
className: "hover:bg-muted/30 transition-colors",
|
|
241
249
|
children: [
|
|
@@ -293,7 +301,7 @@ function A() {
|
|
|
293
301
|
className: "px-4 py-3",
|
|
294
302
|
children: e.notificationsEnabled ? /* @__PURE__ */ D("span", {
|
|
295
303
|
className: "inline-flex items-center gap-1",
|
|
296
|
-
children: [/* @__PURE__ */ E(
|
|
304
|
+
children: [/* @__PURE__ */ E(f, {
|
|
297
305
|
size: 16,
|
|
298
306
|
className: "text-status-success-text",
|
|
299
307
|
"aria-hidden": "true"
|
|
@@ -303,7 +311,7 @@ function A() {
|
|
|
303
311
|
})]
|
|
304
312
|
}) : /* @__PURE__ */ D("span", {
|
|
305
313
|
className: "inline-flex items-center gap-1",
|
|
306
|
-
children: [/* @__PURE__ */ E(
|
|
314
|
+
children: [/* @__PURE__ */ E(p, {
|
|
307
315
|
size: 16,
|
|
308
316
|
className: "text-muted-foreground",
|
|
309
317
|
"aria-hidden": "true"
|
|
@@ -334,15 +342,15 @@ function A() {
|
|
|
334
342
|
className: "flex items-center justify-between",
|
|
335
343
|
children: [/* @__PURE__ */ E("p", {
|
|
336
344
|
className: "text-sm text-muted-foreground",
|
|
337
|
-
children:
|
|
345
|
+
children: V > 0 && /* @__PURE__ */ D(T, { children: [
|
|
338
346
|
j("channel.showing", "Showing"),
|
|
339
347
|
" ",
|
|
340
348
|
/* @__PURE__ */ D("span", {
|
|
341
349
|
className: "font-medium",
|
|
342
350
|
children: [
|
|
343
|
-
|
|
351
|
+
U * z + 1,
|
|
344
352
|
"–",
|
|
345
|
-
Math.min((
|
|
353
|
+
Math.min((U + 1) * z, V)
|
|
346
354
|
]
|
|
347
355
|
}),
|
|
348
356
|
" ",
|
|
@@ -350,29 +358,29 @@ function A() {
|
|
|
350
358
|
" ",
|
|
351
359
|
/* @__PURE__ */ E("span", {
|
|
352
360
|
className: "font-medium",
|
|
353
|
-
children:
|
|
361
|
+
children: V
|
|
354
362
|
})
|
|
355
363
|
] })
|
|
356
364
|
}), /* @__PURE__ */ D("div", {
|
|
357
365
|
className: "flex items-center gap-2",
|
|
358
366
|
children: [
|
|
359
367
|
/* @__PURE__ */ D("button", {
|
|
360
|
-
onClick: () =>
|
|
361
|
-
disabled: !
|
|
368
|
+
onClick: () => W((e) => Math.max(0, e - 1)),
|
|
369
|
+
disabled: !ne || F,
|
|
362
370
|
className: "flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
|
|
363
|
-
children: [/* @__PURE__ */ E(
|
|
371
|
+
children: [/* @__PURE__ */ E(ee, { size: 16 }), j("channel.previous", "Previous")]
|
|
364
372
|
}),
|
|
365
373
|
/* @__PURE__ */ D("span", {
|
|
366
374
|
className: "text-sm text-muted-foreground",
|
|
367
375
|
children: [
|
|
368
|
-
|
|
376
|
+
U + 1,
|
|
369
377
|
" / ",
|
|
370
|
-
|
|
378
|
+
$
|
|
371
379
|
]
|
|
372
380
|
}),
|
|
373
381
|
/* @__PURE__ */ D("button", {
|
|
374
|
-
onClick: () =>
|
|
375
|
-
disabled:
|
|
382
|
+
onClick: () => W((e) => Math.min($ - 1, e + 1)),
|
|
383
|
+
disabled: !re || F,
|
|
376
384
|
className: "flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
|
|
377
385
|
children: [j("channel.next", "Next"), /* @__PURE__ */ E(h, { size: 16 })]
|
|
378
386
|
})
|
|
@@ -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 } 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 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-muted-foreground' },\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-muted-foreground bg-muted', icon: XCircle },\n};\n\nexport function ChannelUsersPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\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 useEffect(() => {\n void Promise.resolve().then(() => {\n setCurrentPage(0);\n void loadUsers(0);\n });\n }, [limit]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n void Promise.resolve().then(() => loadUsers(currentPage));\n }, [currentPage]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n void Promise.resolve().then(() => loadUsers(0));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\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 // 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 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 onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted 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-foreground\">\n {tr('channel.channelUsers', 'Channel Users')}\n </h1>\n <p className=\"text-sm text-muted-foreground 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 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-foreground\"\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 <button\n onClick={() => void loadUsers(currentPage)}\n disabled={isLoadingUsers}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50\"\n >\n <RefreshCw size={16} className={isLoadingUsers ? 'animate-spin' : ''} />\n {tr('channel.refresh', 'Refresh')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_ADMIN}>\n <button\n onClick={() => void exportUsers()}\n disabled={isExportingUsers}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tr('channel.exportCSV', 'Export to CSV')}\n >\n {isExportingUsers ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\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 <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-sm text-muted-foreground\">\n Users connected to this channel through OAuth or pairing. Manage notification\n preferences and DM authorization here.\n </p>\n </div>\n\n {/* Users List */}\n <div className=\"bg-surface border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full min-w-[800px]\">\n <thead className=\"bg-muted/50 border-b border-border\">\n <tr>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.user', 'User')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.platformId', 'Platform ID')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.source', 'Source')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.pairing', 'Pairing')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.notifications', 'Notifications')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.dmAllowed', 'DM Allowed')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {users.length === 0 ? (\n <tr>\n <td colSpan={7} className=\"px-4 py-8 text-center text-muted-foreground\">\n {tr('channel.noUsersConnected', 'No users connected to this channel')}\n </td>\n </tr>\n ) : (\n users.map((user) => {\n const presenceConfig = getPresenceConfig(\n user.presenceStatus as UserPresenceStatus\n );\n const pairingConfig = user.pairingStatus\n ? getPairingConfig(user.pairingStatus as PairingStatus)\n : null;\n const PairingIcon = pairingConfig?.icon ?? XCircle;\n return (\n <tr key={user.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-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-foreground\">\n {user.externalDisplayName || 'Unknown'}\n </p>\n {user.externalUsername && (\n <p className=\"text-xs text-muted-foreground\">\n {user.externalUsername}\n </p>\n )}\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-muted-foreground\">\n {user.externalUserId}\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-xs text-muted-foreground bg-muted px-2 py-1 rounded\">\n {user.source}\n </span>\n </td>\n <td className=\"px-4 py-3\">\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}`}>\n {presenceConfig.label}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n {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-muted-foreground\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n {user.notificationsEnabled ? (\n <span className=\"inline-flex items-center gap-1\">\n <Bell\n size={16}\n className=\"text-status-success-text\"\n aria-hidden=\"true\"\n />\n <span className=\"sr-only\">{tr('channel.enabled', 'Enabled')}</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <BellOff\n size={16}\n className=\"text-muted-foreground\"\n aria-hidden=\"true\"\n />\n <span className=\"sr-only\">{tr('channel.disabled', 'Disabled')}</span>\n </span>\n )}\n </td>\n <td className=\"px-4 py-3\">\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-muted-foreground\">\n <XCircle size={16} />\n {tr('channel.blocked', 'Blocked')}\n </span>\n )}\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">\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 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 rounded-lg hover:bg-muted 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-muted-foreground\">\n {currentPage + 1} / {totalPages}\n </span>\n <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 rounded-lg hover:bg-muted 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 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-muted-foreground' },\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-muted-foreground bg-muted', icon: XCircle },\n};\n\nexport function ChannelUsersPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\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 // 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 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 onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted 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-foreground\">\n {tr('channel.channelUsers', 'Channel Users')}\n </h1>\n <p className=\"text-sm text-muted-foreground 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 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary/50 text-foreground\"\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 <button\n onClick={() => void loadUsers(currentPage)}\n disabled={isLoadingUsers}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50\"\n >\n <RefreshCw size={16} className={isLoadingUsers ? 'animate-spin' : ''} />\n {tr('channel.refresh', 'Refresh')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_ADMIN}>\n <button\n onClick={() => void exportUsers()}\n disabled={isExportingUsers}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tr('channel.exportCSV', 'Export to CSV')}\n >\n {isExportingUsers ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\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 <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-sm text-muted-foreground\">\n Users connected to this channel through OAuth or pairing. Manage notification\n preferences and DM authorization here.\n </p>\n </div>\n\n {/* Users List */}\n <div className=\"bg-surface border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full min-w-[800px]\">\n <thead className=\"bg-muted/50 border-b border-border\">\n <tr>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.user', 'User')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.platformId', 'Platform ID')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.source', 'Source')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.pairing', 'Pairing')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.notifications', 'Notifications')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.dmAllowed', 'DM Allowed')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {users.length === 0 ? (\n <tr>\n <td colSpan={7} className=\"px-4 py-8 text-center text-muted-foreground\">\n {tr('channel.noUsersConnected', 'No users connected to this channel')}\n </td>\n </tr>\n ) : (\n users.map((user) => {\n const presenceConfig = getPresenceConfig(\n user.presenceStatus as UserPresenceStatus\n );\n const pairingConfig = user.pairingStatus\n ? getPairingConfig(user.pairingStatus as PairingStatus)\n : null;\n const PairingIcon = pairingConfig?.icon ?? XCircle;\n return (\n <tr key={user.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-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-foreground\">\n {user.externalDisplayName || 'Unknown'}\n </p>\n {user.externalUsername && (\n <p className=\"text-xs text-muted-foreground\">\n {user.externalUsername}\n </p>\n )}\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-muted-foreground\">\n {user.externalUserId}\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-xs text-muted-foreground bg-muted px-2 py-1 rounded\">\n {user.source}\n </span>\n </td>\n <td className=\"px-4 py-3\">\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}`}>\n {presenceConfig.label}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n {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-muted-foreground\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n {user.notificationsEnabled ? (\n <span className=\"inline-flex items-center gap-1\">\n <Bell\n size={16}\n className=\"text-status-success-text\"\n aria-hidden=\"true\"\n />\n <span className=\"sr-only\">{tr('channel.enabled', 'Enabled')}</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <BellOff\n size={16}\n className=\"text-muted-foreground\"\n aria-hidden=\"true\"\n />\n <span className=\"sr-only\">{tr('channel.disabled', 'Disabled')}</span>\n </span>\n )}\n </td>\n <td className=\"px-4 py-3\">\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-muted-foreground\">\n <XCircle size={16} />\n {tr('channel.blocked', 'Blocked')}\n </span>\n )}\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">\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 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 rounded-lg hover:bg-muted 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-muted-foreground\">\n {currentPage + 1} / {totalPages}\n </span>\n <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 rounded-lg hover:bg-muted 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelsListPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelsListPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAwFH,wBAAgB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"ChannelsListPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelsListPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAwFH,wBAAgB,gBAAgB,4CA0sB/B"}
|