@burdenoff/microfe-channel 2026.608.1 → 2026.624.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 (31) hide show
  1. package/dist/channel/components/AdapterCredentialsForm.js +11 -11
  2. package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
  3. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +53 -53
  4. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  5. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +78 -78
  6. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  7. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +26 -26
  8. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  9. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +32 -32
  10. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  11. package/dist/channel/modules/channels/pages/ChannelsListPage.js +41 -41
  12. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  13. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +30 -30
  14. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  15. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +29 -29
  16. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  17. package/dist/channel/modules/channels/pages/CreateChannelPage.js +28 -28
  18. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  19. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +8 -8
  20. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  21. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +28 -28
  22. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  23. package/dist/channel/modules/channels/pages/EditChannelPage.js +25 -25
  24. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  25. package/dist/channel/modules/channels/pages/EditConnectionPage.js +4 -4
  26. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  27. package/dist/channel/modules/channels/pages/SendMessagePage.js +27 -27
  28. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  29. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +29 -29
  30. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  31. package/package.json +1 -1
@@ -63,7 +63,7 @@ var C = {
63
63
  },
64
64
  INACTIVE: {
65
65
  label: "Inactive",
66
- color: "text-muted-foreground bg-muted",
66
+ color: "text-text-secondary bg-bg-sunken",
67
67
  icon: y
68
68
  },
69
69
  ERROR: {
@@ -78,7 +78,7 @@ var C = {
78
78
  },
79
79
  ARCHIVED: {
80
80
  label: "Archived",
81
- color: "text-muted-foreground bg-muted",
81
+ color: "text-text-secondary bg-bg-sunken",
82
82
  icon: y
83
83
  }
84
84
  }, T = {
@@ -99,7 +99,7 @@ var C = {
99
99
  },
100
100
  UNKNOWN: {
101
101
  label: "Unknown",
102
- color: "text-muted-foreground",
102
+ color: "text-text-secondary",
103
103
  icon: g
104
104
  }
105
105
  };
@@ -245,10 +245,10 @@ function _e() {
245
245
  /* @__PURE__ */ S("div", {
246
246
  className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
247
247
  children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("h1", {
248
- className: "text-xl sm:text-2xl font-bold text-foreground",
248
+ className: "text-xl sm:text-2xl font-bold text-text-primary",
249
249
  children: E("channel.channels", "Channels")
250
250
  }), /* @__PURE__ */ S("p", {
251
- className: "text-sm text-muted-foreground mt-1",
251
+ className: "text-sm text-text-secondary mt-1",
252
252
  children: [
253
253
  E("channel.manageChannels", "Manage your communication channels"),
254
254
  " (",
@@ -263,7 +263,7 @@ function _e() {
263
263
  type: "button",
264
264
  onClick: () => void xe(),
265
265
  disabled: N,
266
- 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",
266
+ className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border-subtle hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
267
267
  title: E("channel.exportCSV", "Export to CSV"),
268
268
  "data-tour": "channels-list-export-button",
269
269
  children: [N ? /* @__PURE__ */ x(_, {
@@ -286,20 +286,20 @@ function _e() {
286
286
  className: "relative flex-1",
287
287
  children: [/* @__PURE__ */ x(fe, {
288
288
  size: 16,
289
- className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
289
+ className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary"
290
290
  }), /* @__PURE__ */ x("input", {
291
291
  "aria-label": E("channel.searchChannels", "Search channels..."),
292
292
  type: "text",
293
293
  placeholder: E("channel.searchChannels", "Search channels..."),
294
294
  value: V.search || "",
295
295
  onChange: (e) => H({ search: e.target.value }),
296
- className: "w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
296
+ className: "w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
297
297
  })]
298
298
  }),
299
299
  /* @__PURE__ */ S("select", {
300
300
  value: V.platform || "",
301
301
  onChange: (e) => H({ platform: e.target.value || void 0 }),
302
- className: "px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
302
+ className: "px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
303
303
  children: [/* @__PURE__ */ x("option", {
304
304
  value: "",
305
305
  children: E("channel.allPlatforms", "All Platforms")
@@ -311,7 +311,7 @@ function _e() {
311
311
  /* @__PURE__ */ S("select", {
312
312
  value: V.status || "",
313
313
  onChange: (e) => H({ status: e.target.value || void 0 }),
314
- className: "px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
314
+ className: "px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
315
315
  children: [/* @__PURE__ */ x("option", {
316
316
  value: "",
317
317
  children: E("channel.allStatus", "All Status")
@@ -333,10 +333,10 @@ function _e() {
333
333
  })]
334
334
  }),
335
335
  R.length > 0 && /* @__PURE__ */ S("div", {
336
- className: "flex flex-col gap-3 rounded-lg border border-border bg-surface p-4 sm:flex-row sm:items-center sm:justify-between",
336
+ className: "flex flex-col gap-3 rounded-lg border border-border-subtle bg-surface p-4 sm:flex-row sm:items-center sm:justify-between",
337
337
  "data-tour": "channels-list-bulk-actions",
338
338
  children: [/* @__PURE__ */ S("label", {
339
- className: "flex items-center gap-2 text-sm text-foreground",
339
+ className: "flex items-center gap-2 text-sm text-text-primary",
340
340
  children: [/* @__PURE__ */ x("input", {
341
341
  type: "checkbox",
342
342
  checked: $,
@@ -345,13 +345,13 @@ function _e() {
345
345
  },
346
346
  onChange: (e) => Oe(e.target.checked),
347
347
  "aria-label": "Select all channels",
348
- className: "size-4 rounded border-border text-primary"
348
+ className: "size-4 rounded border-border-subtle text-primary"
349
349
  }), /* @__PURE__ */ x("span", { children: E("channel.selectAllVisible", "Select all visible") })]
350
350
  }), /* @__PURE__ */ S("div", {
351
351
  className: "flex flex-wrap items-center gap-2",
352
352
  children: [
353
353
  /* @__PURE__ */ S("span", {
354
- className: "text-sm text-muted-foreground",
354
+ className: "text-sm text-text-secondary",
355
355
  children: [
356
356
  k.size,
357
357
  " ",
@@ -361,7 +361,7 @@ function _e() {
361
361
  k.size > 0 && /* @__PURE__ */ x("button", {
362
362
  type: "button",
363
363
  onClick: () => A(/* @__PURE__ */ new Set()),
364
- className: "text-sm text-muted-foreground hover:text-foreground",
364
+ className: "text-sm text-text-secondary hover:text-text-primary",
365
365
  children: E("channel.clearSelection", "Clear selection")
366
366
  }),
367
367
  /* @__PURE__ */ x(s, {
@@ -370,7 +370,7 @@ function _e() {
370
370
  type: "button",
371
371
  onClick: Me,
372
372
  disabled: X.length === 0 || j !== null,
373
- className: "px-3 py-2 text-sm font-medium border border-border rounded-md hover:bg-muted transition-colors disabled:opacity-50",
373
+ className: "px-3 py-2 text-sm font-medium border border-border-subtle rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50",
374
374
  children: [E("channel.healthCheck", "Health Check"), X.length > 0 ? ` (${X.length})` : ""]
375
375
  })
376
376
  }),
@@ -441,14 +441,14 @@ function _e() {
441
441
  children: [
442
442
  /* @__PURE__ */ x(ue, {
443
443
  size: 48,
444
- className: "text-muted-foreground mb-4"
444
+ className: "text-text-secondary mb-4"
445
445
  }),
446
446
  /* @__PURE__ */ x("h3", {
447
- className: "text-lg font-semibold text-foreground mb-2",
447
+ className: "text-lg font-semibold text-text-primary mb-2",
448
448
  children: E("channel.noChannelsFound", "No channels found")
449
449
  }),
450
450
  /* @__PURE__ */ x("p", {
451
- className: "text-sm text-muted-foreground max-w-md mb-4",
451
+ className: "text-sm text-text-secondary max-w-md mb-4",
452
452
  children: V.search || V.platform || V.status ? E("channel.adjustFilters", "Try adjusting your filters to see more channels.") : E("channel.createFirstChannelPrompt", "Create your first communication channel to get started.")
453
453
  }),
454
454
  g && !V.search && !V.platform && !V.status && /* @__PURE__ */ S("button", {
@@ -465,7 +465,7 @@ function _e() {
465
465
  children: R.map((e) => {
466
466
  let t = Ne(e.platform), n = Pe(e.status), r = Fe(e.connectionHealth), i = n.icon, a = r.icon, o = ye === e.id;
467
467
  return /* @__PURE__ */ S("div", {
468
- className: `p-4 bg-surface border rounded-lg transition-colors group ${k.has(e.id) ? "ring-2 ring-action-primary-bg border-action-primary-bg/40" : "border-border hover:border-action-primary-bg/40"}`,
468
+ className: `p-4 bg-surface border rounded-lg transition-colors group ${k.has(e.id) ? "ring-2 ring-action-primary-bg border-action-primary-bg/40" : "border-border-subtle hover:border-action-primary-bg/40"}`,
469
469
  children: [
470
470
  /* @__PURE__ */ S("div", {
471
471
  className: "flex items-start justify-between mb-3",
@@ -477,7 +477,7 @@ function _e() {
477
477
  checked: k.has(e.id),
478
478
  onChange: (t) => De(e.id, t.target.checked),
479
479
  "aria-label": `Select channel ${e.name}`,
480
- className: "size-4 rounded border-border text-primary"
480
+ className: "size-4 rounded border-border-subtle text-primary"
481
481
  }),
482
482
  /* @__PURE__ */ x(ue, {
483
483
  size: 20,
@@ -509,19 +509,19 @@ function _e() {
509
509
  className: "cursor-pointer",
510
510
  children: [
511
511
  /* @__PURE__ */ x("h3", {
512
- className: "text-sm font-semibold text-foreground mb-1 truncate",
512
+ className: "text-sm font-semibold text-text-primary mb-1 truncate",
513
513
  children: e.name
514
514
  }),
515
515
  e.description && /* @__PURE__ */ x("p", {
516
- className: "text-xs text-muted-foreground mb-2 line-clamp-2",
516
+ className: "text-xs text-text-secondary mb-2 line-clamp-2",
517
517
  children: e.description
518
518
  }),
519
519
  e.adapterType && /* @__PURE__ */ x("p", {
520
- className: "text-xs text-muted-foreground mb-2 font-mono",
520
+ className: "text-xs text-text-secondary mb-2 font-mono",
521
521
  children: e.adapterType.replace(/_/g, " ")
522
522
  }),
523
523
  /* @__PURE__ */ x("div", {
524
- className: "flex items-center gap-3 text-xs text-muted-foreground mb-2",
524
+ className: "flex items-center gap-3 text-xs text-text-secondary mb-2",
525
525
  children: /* @__PURE__ */ S("span", { children: [
526
526
  E("channel.priority", "Priority"),
527
527
  ": ",
@@ -543,7 +543,7 @@ function _e() {
543
543
  }),
544
544
  /* @__PURE__ */ x("span", { children: "•" }),
545
545
  /* @__PURE__ */ x("span", {
546
- className: e.isConnected ? "text-status-success-text" : "text-muted-foreground",
546
+ className: e.isConnected ? "text-status-success-text" : "text-text-secondary",
547
547
  children: e.isConnected ? E("channel.connected", "Connected") : E("channel.notConnected", "Not Connected")
548
548
  })
549
549
  ]
@@ -558,26 +558,26 @@ function _e() {
558
558
  label: e.tagId,
559
559
  size: "sm"
560
560
  }, e.id)), e.tagAssignments.length > 4 && /* @__PURE__ */ S("span", {
561
- className: "text-xs text-muted-foreground",
561
+ className: "text-xs text-text-secondary",
562
562
  children: ["+", e.tagAssignments.length - 4]
563
563
  })]
564
564
  }),
565
565
  /* @__PURE__ */ S("div", {
566
- className: "mt-3 pt-3 border-t border-border flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity",
566
+ className: "mt-3 pt-3 border-t border-border-subtle flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity",
567
567
  children: [/* @__PURE__ */ S("div", {
568
568
  className: "flex items-center gap-1",
569
569
  children: [
570
570
  /* @__PURE__ */ x("button", {
571
571
  type: "button",
572
572
  onClick: () => m(`/channels/${e.id}`),
573
- className: "p-1.5 text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer",
573
+ className: "p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer",
574
574
  title: E("channel.viewDetails", "View Details"),
575
575
  children: /* @__PURE__ */ x(se, { size: 14 })
576
576
  }),
577
577
  b && /* @__PURE__ */ x("button", {
578
578
  type: "button",
579
579
  onClick: () => m(`/channels/${e.id}/edit`),
580
- className: "p-1.5 text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer",
580
+ className: "p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer",
581
581
  title: E("channel.editChannel", "Edit Channel"),
582
582
  children: /* @__PURE__ */ x(ce, { size: 14 })
583
583
  }),
@@ -587,7 +587,7 @@ function _e() {
587
587
  type: "button",
588
588
  onClick: () => Te(e.id),
589
589
  disabled: o,
590
- className: "p-1.5 text-muted-foreground hover:text-status-info-text hover:bg-status-info-bg-subtle rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed",
590
+ className: "p-1.5 text-text-secondary hover:text-status-info-text hover:bg-status-info-bg-subtle rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed",
591
591
  title: E("channel.runHealthCheck", "Run Health Check"),
592
592
  children: o ? /* @__PURE__ */ x(_, {
593
593
  size: 14,
@@ -601,7 +601,7 @@ function _e() {
601
601
  type: "button",
602
602
  onClick: () => we(e),
603
603
  disabled: o,
604
- className: `p-1.5 rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed ${e.status === "ACTIVE" ? "text-muted-foreground hover:text-status-warning-text hover:bg-status-warning-bg-subtle" : "text-muted-foreground hover:text-status-success-text hover:bg-status-success-bg-subtle"}`,
604
+ className: `p-1.5 rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed ${e.status === "ACTIVE" ? "text-text-secondary hover:text-status-warning-text hover:bg-status-warning-bg-subtle" : "text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle"}`,
605
605
  title: e.status === "ACTIVE" ? E("channel.deactivate", "Deactivate") : E("channel.activate", "Activate"),
606
606
  children: e.status === "ACTIVE" ? /* @__PURE__ */ x(y, { size: 14 }) : /* @__PURE__ */ x(le, { size: 14 })
607
607
  })
@@ -611,7 +611,7 @@ function _e() {
611
611
  className: "flex items-center gap-2",
612
612
  children: [
613
613
  /* @__PURE__ */ x("span", {
614
- className: "text-xs text-muted-foreground",
614
+ className: "text-xs text-text-secondary",
615
615
  children: E("channel.deleteQuestion", "Delete?")
616
616
  }),
617
617
  /* @__PURE__ */ x("button", {
@@ -624,7 +624,7 @@ function _e() {
624
624
  /* @__PURE__ */ x("button", {
625
625
  type: "button",
626
626
  onClick: () => D(null),
627
- className: "px-2 py-1 text-xs text-muted-foreground hover:bg-muted rounded transition-colors cursor-pointer",
627
+ className: "px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken rounded transition-colors cursor-pointer",
628
628
  children: E("channel.no", "No")
629
629
  })
630
630
  ]
@@ -633,7 +633,7 @@ function _e() {
633
633
  children: /* @__PURE__ */ x("button", {
634
634
  type: "button",
635
635
  onClick: () => D(e.id),
636
- className: "p-1.5 text-muted-foreground hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer",
636
+ className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer",
637
637
  title: E("channel.deleteChannel", "Delete Channel"),
638
638
  children: /* @__PURE__ */ x(pe, { size: 14 })
639
639
  })
@@ -646,12 +646,12 @@ function _e() {
646
646
  B > F && /* @__PURE__ */ S("div", {
647
647
  className: "flex items-center justify-between px-1 py-2",
648
648
  children: [/* @__PURE__ */ S("p", {
649
- className: "text-sm text-muted-foreground",
649
+ className: "text-sm text-text-secondary",
650
650
  children: [
651
651
  E("channel.showing", "Showing"),
652
652
  " ",
653
653
  /* @__PURE__ */ S("span", {
654
- className: "font-medium text-foreground",
654
+ className: "font-medium text-text-primary",
655
655
  children: [
656
656
  I * F + 1,
657
657
  "–",
@@ -662,7 +662,7 @@ function _e() {
662
662
  E("channel.of", "of"),
663
663
  " ",
664
664
  /* @__PURE__ */ x("span", {
665
- className: "font-medium text-foreground",
665
+ className: "font-medium text-text-primary",
666
666
  children: B
667
667
  }),
668
668
  " ",
@@ -675,11 +675,11 @@ function _e() {
675
675
  type: "button",
676
676
  onClick: () => L((e) => Math.max(0, e - 1)),
677
677
  disabled: I === 0,
678
- 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",
678
+ className: "px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
679
679
  children: E("channel.previous", "Previous")
680
680
  }),
681
681
  /* @__PURE__ */ S("span", {
682
- className: "text-sm text-muted-foreground",
682
+ className: "text-sm text-text-secondary",
683
683
  children: [
684
684
  I + 1,
685
685
  " / ",
@@ -690,7 +690,7 @@ function _e() {
690
690
  type: "button",
691
691
  onClick: () => L((e) => Math.min(J - 1, e + 1)),
692
692
  disabled: I >= J - 1,
693
- 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",
693
+ className: "px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
694
694
  children: E("channel.next", "Next")
695
695
  })
696
696
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelsListPage.tsx"],"sourcesContent":["/**\n * Channels List Page\n *\n * Displays a list of all channels with CRUD operations\n */\n\nimport { useEffect, useMemo, useState, useRef } 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 { reportShellError, subscribeShellEvent } from '../../../utils/shellBridge';\nimport {\n Radio,\n Plus,\n Search,\n Loader2,\n AlertCircle,\n Trash2,\n RefreshCw,\n Power,\n PowerOff,\n Eye,\n Pencil,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Download,\n} from 'lucide-react';\nimport {\n useChannels,\n type Channel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n} from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\nimport { useExportChannels } from '../hooks/useExport';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\n// Platform display configuration\nconst PLATFORM_CONFIG: Record<ChannelPlatform, { label: string; color: string }> = {\n EMAIL: { label: 'Email', color: 'bg-accent-blue-subtle text-accent-blue' },\n SMS: { label: 'SMS', color: 'bg-status-success-bg-subtle text-status-success-text' },\n PUSH: { label: 'Push', color: 'bg-accent-purple-subtle text-accent-purple' },\n INAPP: { label: 'In-App', color: 'bg-accent-indigo-subtle text-accent-indigo' },\n WEBHOOK: { label: 'Webhook', color: 'bg-accent-orange-subtle text-accent-orange' },\n SLACK: { label: 'Slack', color: 'bg-accent-pink-subtle text-accent-pink' },\n TEAMS: { label: 'Teams', color: 'bg-accent-cyan-subtle text-accent-cyan' },\n DISCORD: { label: 'Discord', color: 'bg-accent-violet-subtle text-accent-violet' },\n WHATSAPP: { label: 'WhatsApp', color: 'bg-accent-emerald-subtle text-accent-emerald' },\n WEBCHAT: { label: 'Webchat', color: 'bg-accent-teal-subtle text-accent-teal' },\n};\n\n// Status display configuration\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n INACTIVE: { label: 'Inactive', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n ERROR: {\n label: 'Error',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: AlertTriangle,\n },\n ARCHIVED: { label: 'Archived', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n};\n\n// Health display configuration\nconst HEALTH_CONFIG: Record<\n ConnectionHealth,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n HEALTHY: { label: 'Healthy', color: 'text-status-success-text', icon: CheckCircle2 },\n DEGRADED: { label: 'Degraded', color: 'text-status-warning-text', icon: AlertTriangle },\n UNHEALTHY: { label: 'Unhealthy', color: 'text-status-error-text', icon: XCircle },\n UNKNOWN: { label: 'Unknown', color: 'text-muted-foreground', icon: HelpCircle },\n};\n\nexport function ChannelsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Channel creation is an admin-only action (channel:create). Hide the\n // affordance for users without it (per MICROFRONTEND-SPEC: {canCreateChannel && ...}).\n const canCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CREATE);\n const canUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_UPDATE);\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 [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [selectedChannelIds, setSelectedChannelIds] = useState<Set<string>>(new Set());\n const [bulkActionLoading, setBulkActionLoading] = useState<string | null>(null);\n\n const { organizationId } = useChannelContext();\n const {\n triggerExport: exportChannels,\n isExporting: isExportingChannels,\n exportError: exportChannelsError,\n } = useExportChannels(organizationId ?? undefined);\n const [pageSize] = useState(12);\n const [currentPage, setCurrentPage] = useState(0);\n\n const {\n channels,\n isLoading,\n error,\n total,\n filter,\n updateFilter,\n deleteChannel,\n activateChannel,\n deactivateChannel,\n runHealthCheck,\n refetch,\n } = useChannels();\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n const refetchRef = useRef(refetch);\n useEffect(() => {\n refetchRef.current = refetch;\n });\n\n // Fetch the current page whenever page or page size changes\n useEffect(() => {\n void refetchRef.current({ offset: currentPage * pageSize, limit: pageSize });\n }, [currentPage, pageSize]);\n\n // Reset to page 0 when filter changes\n useEffect(() => {\n void Promise.resolve().then(() => setCurrentPage(0));\n }, [filter.search, filter.platform, filter.status]);\n\n // Cross-MFE refresh: refetch the list whenever channel/connection events\n // are emitted by other tabs or microfrontends.\n useEffect(() => {\n const events = [\n 'channel.created',\n 'channel.updated',\n 'channel.deleted',\n 'connection.created',\n 'connection.updated',\n 'connection.deleted',\n ];\n const unsubs = events.map((name) => subscribeShellEvent(name, () => void refetch()));\n return () => unsubs.forEach((u) => u());\n }, [refetch]);\n\n const handleDelete = async (id: string) => {\n void nativeImpact('medium');\n setActionLoading(id);\n try {\n await deleteChannel(id);\n void nativeNotify('success');\n setDeleteConfirm(null);\n } catch (err) {\n void nativeNotify('error');\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to delete channel' } },\n '[channel] Failed to delete channel'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleStatus = async (channel: Channel) => {\n setActionLoading(channel.id);\n try {\n if (channel.status === 'ACTIVE') {\n await deactivateChannel(channel.id);\n } else {\n await activateChannel(channel.id);\n }\n } catch (err) {\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to toggle channel status' } },\n '[channel] Failed to toggle channel status'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleHealthCheck = async (id: string) => {\n setActionLoading(id);\n try {\n await runHealthCheck(id);\n } catch (err) {\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to run health check' } },\n '[channel] Failed to run health check'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const selectedChannels = useMemo(\n () => channels.filter((channel) => selectedChannelIds.has(channel.id)),\n [channels, selectedChannelIds]\n );\n const selectedActiveChannels = useMemo(\n () => selectedChannels.filter((channel) => channel.status === 'ACTIVE'),\n [selectedChannels]\n );\n const selectedInactiveChannels = useMemo(\n () => selectedChannels.filter((channel) => channel.status !== 'ACTIVE'),\n [selectedChannels]\n );\n const allVisibleSelected =\n channels.length > 0 && channels.every((channel) => selectedChannelIds.has(channel.id));\n const partiallySelected =\n channels.some((channel) => selectedChannelIds.has(channel.id)) && !allVisibleSelected;\n\n const toggleChannelSelection = (channelId: string, checked: boolean) => {\n setSelectedChannelIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(channelId);\n } else {\n next.delete(channelId);\n }\n return next;\n });\n };\n\n const toggleAllChannels = (checked: boolean) => {\n setSelectedChannelIds((prev) => {\n const next = new Set(prev);\n channels.forEach((channel) => {\n if (checked) {\n next.add(channel.id);\n } else {\n next.delete(channel.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkDelete = async () => {\n if (selectedChannels.length === 0) return;\n void nativeImpact('medium');\n const message = tr(\n selectedChannels.length === 1\n ? 'channel.bulkDeleteConfirmSingle'\n : 'channel.bulkDeleteConfirmMultiple',\n selectedChannels.length === 1\n ? 'Delete 1 selected channel?'\n : `Delete ${selectedChannels.length} selected channels?`\n );\n const native = await nativeConfirm({\n title: 'Delete channels',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n setBulkActionLoading('delete');\n try {\n await Promise.allSettled(selectedChannels.map((channel) => deleteChannel(channel.id)));\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkActivate = async () => {\n if (selectedInactiveChannels.length === 0) return;\n setBulkActionLoading('activate');\n try {\n await Promise.allSettled(\n selectedInactiveChannels.map((channel) => activateChannel(channel.id))\n );\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkDeactivate = async () => {\n if (selectedActiveChannels.length === 0) return;\n setBulkActionLoading('deactivate');\n try {\n await Promise.allSettled(\n selectedActiveChannels.map((channel) => deactivateChannel(channel.id))\n );\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkHealthCheck = async () => {\n if (selectedChannels.length === 0) return;\n setBulkActionLoading('health');\n try {\n await Promise.allSettled(selectedChannels.map((channel) => runHealthCheck(channel.id)));\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] || {\n label: platform,\n color: 'bg-bg-sunken text-text-secondary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\n };\n\n const getStatusConfig = (status: ChannelStatus) => {\n const config = STATUS_CONFIG[status] || STATUS_CONFIG.INACTIVE;\n return {\n ...config,\n label: translateLabel(status, STATUS_I18N, tr),\n };\n };\n\n const getHealthConfig = (health: ConnectionHealth) => {\n const config = HEALTH_CONFIG[health] || HEALTH_CONFIG.UNKNOWN;\n return {\n ...config,\n label: translateLabel(health, HEALTH_I18N, tr),\n };\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-4 sm: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>\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">\n {tr('channel.channels', 'Channels')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr('channel.manageChannels', 'Manage your communication channels')} ({total} total)\n </p>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXPORT}>\n <button\n type=\"button\"\n onClick={() => void exportChannels()}\n disabled={isExportingChannels}\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 data-tour=\"channels-list-export-button\"\n >\n {isExportingChannels ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={16} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n )}\n </div>\n </div>\n\n {/* Search and Filters */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\"\n />\n <input\n aria-label={tr('channel.searchChannels', 'Search channels...')}\n type=\"text\"\n placeholder={tr('channel.searchChannels', 'Search channels...')}\n value={filter.search || ''}\n onChange={(e) => updateFilter({ search: e.target.value })}\n className=\"w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <select\n value={filter.platform || ''}\n onChange={(e) =>\n updateFilter({ platform: (e.target.value as ChannelPlatform) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {Object.entries(PLATFORM_CONFIG).map(([key, config]) => (\n <option key={key} value={key}>\n {config.label}\n </option>\n ))}\n </select>\n\n <select\n value={filter.status || ''}\n onChange={(e) =>\n updateFilter({ status: (e.target.value as ChannelStatus) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">{tr('channel.allStatus', 'All Status')}</option>\n {Object.entries(STATUS_CONFIG).map(([key, config]) => (\n <option key={key} value={key}>\n {config.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Export Error */}\n {exportChannelsError && (\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 <p className=\"text-sm text-status-error-text flex-1\">{exportChannelsError}</p>\n </div>\n )}\n\n {channels.length > 0 && (\n <div\n className=\"flex flex-col gap-3 rounded-lg border border-border bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\"\n data-tour=\"channels-list-bulk-actions\"\n >\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllChannels(event.target.checked)}\n aria-label=\"Select all channels\"\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>{tr('channel.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedChannelIds.size} {tr('channel.selectedCountLabel', 'selected')}\n </span>\n {selectedChannelIds.size > 0 && (\n <button\n type=\"button\"\n onClick={() => setSelectedChannelIds(new Set())}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n {tr('channel.clearSelection', 'Clear selection')}\n </button>\n )}\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={handleBulkHealthCheck}\n disabled={selectedChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-border rounded-md hover:bg-muted transition-colors disabled:opacity-50\"\n >\n {tr('channel.healthCheck', 'Health Check')}\n {selectedChannels.length > 0 ? ` (${selectedChannels.length})` : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={handleBulkActivate}\n disabled={selectedInactiveChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-success-border/30 text-status-success-text rounded-md hover:bg-status-success-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.activateSelected', 'Activate Selected')}\n {selectedInactiveChannels.length > 0\n ? ` (${selectedInactiveChannels.length})`\n : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={handleBulkDeactivate}\n disabled={selectedActiveChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-warning-border text-status-warning-text rounded-md hover:bg-status-warning-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.deactivateSelected', 'Deactivate Selected')}\n {selectedActiveChannels.length > 0 ? ` (${selectedActiveChannels.length})` : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-error-border/30 text-status-error-text rounded-md hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.deleteSelected', 'Delete Selected')}\n {selectedChannels.length > 0 ? ` (${selectedChannels.length})` : ''}\n </button>\n </PermissionGuard>\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\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 {tr('channel.loadFailed', 'Failed to load channels')}\n </p>\n <p className=\"text-xs text-status-error-text\">{error}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => refetch()}\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 {tr('channel.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && channels.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && channels.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Radio size={48} className=\"text-muted-foreground mb-4\" />\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('channel.noChannelsFound', 'No channels found')}\n </h3>\n <p className=\"text-sm text-muted-foreground max-w-md mb-4\">\n {filter.search || filter.platform || filter.status\n ? tr('channel.adjustFilters', 'Try adjusting your filters to see more channels.')\n : tr(\n 'channel.createFirstChannelPrompt',\n 'Create your first communication channel to get started.'\n )}\n </p>\n {canCreate && !filter.search && !filter.platform && !filter.status && (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={16} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n )}\n </div>\n )}\n\n {/* Channels List */}\n {channels.length > 0 && (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\" data-tour=\"channels-list-grid\">\n {channels.map((channel: Channel) => {\n const platformConfig = getPlatformConfig(channel.platform);\n const statusConfig = getStatusConfig(channel.status);\n const healthConfig = getHealthConfig(channel.connectionHealth);\n const StatusIcon = statusConfig.icon;\n const HealthIcon = healthConfig.icon;\n const isActionLoading = actionLoading === channel.id;\n\n return (\n <div\n key={channel.id}\n className={`p-4 bg-surface border rounded-lg transition-colors group ${selectedChannelIds.has(channel.id) ? 'ring-2 ring-action-primary-bg border-action-primary-bg/40' : 'border-border hover:border-action-primary-bg/40'}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n checked={selectedChannelIds.has(channel.id)}\n onChange={(event) =>\n toggleChannelSelection(channel.id, event.target.checked)\n }\n aria-label={`Select channel ${channel.name}`}\n className=\"size-4 rounded border-border text-primary\"\n />\n <Radio size={20} className=\"text-accent-primary\" />\n <span\n className={`px-2 py-0.5 text-xs font-semibold rounded-full ${platformConfig.color}`}\n >\n {platformConfig.label}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <StatusIcon size={14} className={statusConfig.color.split(' ')[0]} />\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded-full ${statusConfig.color}`}\n >\n {statusConfig.label}\n </span>\n </div>\n </div>\n\n {/* Content - Clickable area */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/channels/${channel.id}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`/channels/${channel.id}`);\n }\n }}\n className=\"cursor-pointer\"\n >\n <h3 className=\"text-sm font-semibold text-foreground mb-1 truncate\">\n {channel.name}\n </h3>\n\n {channel.description && (\n <p className=\"text-xs text-muted-foreground mb-2 line-clamp-2\">\n {channel.description}\n </p>\n )}\n\n {/* Adapter */}\n {channel.adapterType && (\n <p className=\"text-xs text-muted-foreground mb-2 font-mono\">\n {channel.adapterType.replace(/_/g, ' ')}\n </p>\n )}\n\n {/* Stats Row */}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground mb-2\">\n <span>\n {tr('channel.priority', 'Priority')}: {channel.priority}\n </span>\n </div>\n\n {/* Health & Connection Status */}\n <div className=\"flex items-center gap-3 text-xs\">\n <div className=\"flex items-center gap-1\">\n <HealthIcon size={12} className={healthConfig.color} />\n <span className={healthConfig.color}>{healthConfig.label}</span>\n </div>\n <span>•</span>\n <span\n className={\n channel.isConnected ? 'text-status-success-text' : 'text-muted-foreground'\n }\n >\n {channel.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.notConnected', 'Not Connected')}\n </span>\n </div>\n </div>\n\n {/* Tags */}\n {channel.tagAssignments && channel.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mt-2\" aria-label=\"Channel tags\">\n {channel.tagAssignments.slice(0, 4).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {channel.tagAssignments.length > 4 && (\n <span className=\"text-xs text-muted-foreground\">\n +{channel.tagAssignments.length - 4}\n </span>\n )}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"mt-3 pt-3 border-t border-border flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channel.id}`)}\n className=\"p-1.5 text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer\"\n title={tr('channel.viewDetails', 'View Details')}\n >\n <Eye size={14} />\n </button>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channel.id}/edit`)}\n className=\"p-1.5 text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer\"\n title={tr('channel.editChannel', 'Edit Channel')}\n >\n <Pencil size={14} />\n </button>\n )}\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={() => handleHealthCheck(channel.id)}\n disabled={isActionLoading}\n className=\"p-1.5 text-muted-foreground hover:text-status-info-text hover:bg-status-info-bg-subtle rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n title={tr('channel.runHealthCheck', 'Run Health Check')}\n >\n {isActionLoading ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={14} />\n )}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={() => handleToggleStatus(channel)}\n disabled={isActionLoading}\n className={`p-1.5 rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed ${\n channel.status === 'ACTIVE'\n ? 'text-muted-foreground hover:text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-muted-foreground hover:text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n title={\n channel.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')\n }\n >\n {channel.status === 'ACTIVE' ? (\n <PowerOff size={14} />\n ) : (\n <Power size={14} />\n )}\n </button>\n </PermissionGuard>\n </div>\n\n {/* Delete */}\n {deleteConfirm === channel.id ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">\n {tr('channel.deleteQuestion', 'Delete?')}\n </span>\n <button\n type=\"button\"\n onClick={() => handleDelete(channel.id)}\n disabled={isActionLoading}\n className=\"px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer disabled:cursor-not-allowed\"\n >\n {isActionLoading ? '...' : tr('channel.yes', 'Yes')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(null)}\n className=\"px-2 py-1 text-xs text-muted-foreground hover:bg-muted rounded transition-colors cursor-pointer\"\n >\n {tr('channel.no', 'No')}\n </button>\n </div>\n ) : (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(channel.id)}\n className=\"p-1.5 text-muted-foreground hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer\"\n title={tr('channel.deleteChannel', 'Delete Channel')}\n >\n <Trash2 size={14} />\n </button>\n </PermissionGuard>\n )}\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Pagination */}\n {total > pageSize && (\n <div className=\"flex items-center justify-between px-1 py-2\">\n <p className=\"text-sm text-muted-foreground\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium text-foreground\">\n {currentPage * pageSize + 1}–{Math.min((currentPage + 1) * pageSize, total)}\n </span>{' '}\n {tr('channel.of', 'of')} <span className=\"font-medium text-foreground\">{total}</span>{' '}\n {tr('channel.channelsLower', 'channels')}\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 </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChannelsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelsListPage.tsx"],"sourcesContent":["/**\n * Channels List Page\n *\n * Displays a list of all channels with CRUD operations\n */\n\nimport { useEffect, useMemo, useState, useRef } 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 { reportShellError, subscribeShellEvent } from '../../../utils/shellBridge';\nimport {\n Radio,\n Plus,\n Search,\n Loader2,\n AlertCircle,\n Trash2,\n RefreshCw,\n Power,\n PowerOff,\n Eye,\n Pencil,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Download,\n} from 'lucide-react';\nimport {\n useChannels,\n type Channel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n} from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\nimport { useExportChannels } from '../hooks/useExport';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\n// Platform display configuration\nconst PLATFORM_CONFIG: Record<ChannelPlatform, { label: string; color: string }> = {\n EMAIL: { label: 'Email', color: 'bg-accent-blue-subtle text-accent-blue' },\n SMS: { label: 'SMS', color: 'bg-status-success-bg-subtle text-status-success-text' },\n PUSH: { label: 'Push', color: 'bg-accent-purple-subtle text-accent-purple' },\n INAPP: { label: 'In-App', color: 'bg-accent-indigo-subtle text-accent-indigo' },\n WEBHOOK: { label: 'Webhook', color: 'bg-accent-orange-subtle text-accent-orange' },\n SLACK: { label: 'Slack', color: 'bg-accent-pink-subtle text-accent-pink' },\n TEAMS: { label: 'Teams', color: 'bg-accent-cyan-subtle text-accent-cyan' },\n DISCORD: { label: 'Discord', color: 'bg-accent-violet-subtle text-accent-violet' },\n WHATSAPP: { label: 'WhatsApp', color: 'bg-accent-emerald-subtle text-accent-emerald' },\n WEBCHAT: { label: 'Webchat', color: 'bg-accent-teal-subtle text-accent-teal' },\n};\n\n// Status display configuration\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n INACTIVE: { label: 'Inactive', color: 'text-text-secondary bg-bg-sunken', icon: PowerOff },\n ERROR: {\n label: 'Error',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: AlertTriangle,\n },\n ARCHIVED: { label: 'Archived', color: 'text-text-secondary bg-bg-sunken', icon: PowerOff },\n};\n\n// Health display configuration\nconst HEALTH_CONFIG: Record<\n ConnectionHealth,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n HEALTHY: { label: 'Healthy', color: 'text-status-success-text', icon: CheckCircle2 },\n DEGRADED: { label: 'Degraded', color: 'text-status-warning-text', icon: AlertTriangle },\n UNHEALTHY: { label: 'Unhealthy', color: 'text-status-error-text', icon: XCircle },\n UNKNOWN: { label: 'Unknown', color: 'text-text-secondary', icon: HelpCircle },\n};\n\nexport function ChannelsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Channel creation is an admin-only action (channel:create). Hide the\n // affordance for users without it (per MICROFRONTEND-SPEC: {canCreateChannel && ...}).\n const canCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CREATE);\n const canUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_UPDATE);\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 [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [selectedChannelIds, setSelectedChannelIds] = useState<Set<string>>(new Set());\n const [bulkActionLoading, setBulkActionLoading] = useState<string | null>(null);\n\n const { organizationId } = useChannelContext();\n const {\n triggerExport: exportChannels,\n isExporting: isExportingChannels,\n exportError: exportChannelsError,\n } = useExportChannels(organizationId ?? undefined);\n const [pageSize] = useState(12);\n const [currentPage, setCurrentPage] = useState(0);\n\n const {\n channels,\n isLoading,\n error,\n total,\n filter,\n updateFilter,\n deleteChannel,\n activateChannel,\n deactivateChannel,\n runHealthCheck,\n refetch,\n } = useChannels();\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n const refetchRef = useRef(refetch);\n useEffect(() => {\n refetchRef.current = refetch;\n });\n\n // Fetch the current page whenever page or page size changes\n useEffect(() => {\n void refetchRef.current({ offset: currentPage * pageSize, limit: pageSize });\n }, [currentPage, pageSize]);\n\n // Reset to page 0 when filter changes\n useEffect(() => {\n void Promise.resolve().then(() => setCurrentPage(0));\n }, [filter.search, filter.platform, filter.status]);\n\n // Cross-MFE refresh: refetch the list whenever channel/connection events\n // are emitted by other tabs or microfrontends.\n useEffect(() => {\n const events = [\n 'channel.created',\n 'channel.updated',\n 'channel.deleted',\n 'connection.created',\n 'connection.updated',\n 'connection.deleted',\n ];\n const unsubs = events.map((name) => subscribeShellEvent(name, () => void refetch()));\n return () => unsubs.forEach((u) => u());\n }, [refetch]);\n\n const handleDelete = async (id: string) => {\n void nativeImpact('medium');\n setActionLoading(id);\n try {\n await deleteChannel(id);\n void nativeNotify('success');\n setDeleteConfirm(null);\n } catch (err) {\n void nativeNotify('error');\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to delete channel' } },\n '[channel] Failed to delete channel'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleStatus = async (channel: Channel) => {\n setActionLoading(channel.id);\n try {\n if (channel.status === 'ACTIVE') {\n await deactivateChannel(channel.id);\n } else {\n await activateChannel(channel.id);\n }\n } catch (err) {\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to toggle channel status' } },\n '[channel] Failed to toggle channel status'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleHealthCheck = async (id: string) => {\n setActionLoading(id);\n try {\n await runHealthCheck(id);\n } catch (err) {\n reportShellError(\n err,\n { module: 'channel', context: { action: 'Failed to run health check' } },\n '[channel] Failed to run health check'\n );\n } finally {\n setActionLoading(null);\n }\n };\n\n const selectedChannels = useMemo(\n () => channels.filter((channel) => selectedChannelIds.has(channel.id)),\n [channels, selectedChannelIds]\n );\n const selectedActiveChannels = useMemo(\n () => selectedChannels.filter((channel) => channel.status === 'ACTIVE'),\n [selectedChannels]\n );\n const selectedInactiveChannels = useMemo(\n () => selectedChannels.filter((channel) => channel.status !== 'ACTIVE'),\n [selectedChannels]\n );\n const allVisibleSelected =\n channels.length > 0 && channels.every((channel) => selectedChannelIds.has(channel.id));\n const partiallySelected =\n channels.some((channel) => selectedChannelIds.has(channel.id)) && !allVisibleSelected;\n\n const toggleChannelSelection = (channelId: string, checked: boolean) => {\n setSelectedChannelIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(channelId);\n } else {\n next.delete(channelId);\n }\n return next;\n });\n };\n\n const toggleAllChannels = (checked: boolean) => {\n setSelectedChannelIds((prev) => {\n const next = new Set(prev);\n channels.forEach((channel) => {\n if (checked) {\n next.add(channel.id);\n } else {\n next.delete(channel.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkDelete = async () => {\n if (selectedChannels.length === 0) return;\n void nativeImpact('medium');\n const message = tr(\n selectedChannels.length === 1\n ? 'channel.bulkDeleteConfirmSingle'\n : 'channel.bulkDeleteConfirmMultiple',\n selectedChannels.length === 1\n ? 'Delete 1 selected channel?'\n : `Delete ${selectedChannels.length} selected channels?`\n );\n const native = await nativeConfirm({\n title: 'Delete channels',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n setBulkActionLoading('delete');\n try {\n await Promise.allSettled(selectedChannels.map((channel) => deleteChannel(channel.id)));\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkActivate = async () => {\n if (selectedInactiveChannels.length === 0) return;\n setBulkActionLoading('activate');\n try {\n await Promise.allSettled(\n selectedInactiveChannels.map((channel) => activateChannel(channel.id))\n );\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkDeactivate = async () => {\n if (selectedActiveChannels.length === 0) return;\n setBulkActionLoading('deactivate');\n try {\n await Promise.allSettled(\n selectedActiveChannels.map((channel) => deactivateChannel(channel.id))\n );\n setSelectedChannelIds(new Set());\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const handleBulkHealthCheck = async () => {\n if (selectedChannels.length === 0) return;\n setBulkActionLoading('health');\n try {\n await Promise.allSettled(selectedChannels.map((channel) => runHealthCheck(channel.id)));\n } finally {\n setBulkActionLoading(null);\n }\n };\n\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] || {\n label: platform,\n color: 'bg-bg-sunken text-text-secondary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\n };\n\n const getStatusConfig = (status: ChannelStatus) => {\n const config = STATUS_CONFIG[status] || STATUS_CONFIG.INACTIVE;\n return {\n ...config,\n label: translateLabel(status, STATUS_I18N, tr),\n };\n };\n\n const getHealthConfig = (health: ConnectionHealth) => {\n const config = HEALTH_CONFIG[health] || HEALTH_CONFIG.UNKNOWN;\n return {\n ...config,\n label: translateLabel(health, HEALTH_I18N, tr),\n };\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-4 sm: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>\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n {tr('channel.channels', 'Channels')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('channel.manageChannels', 'Manage your communication channels')} ({total} total)\n </p>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXPORT}>\n <button\n type=\"button\"\n onClick={() => void exportChannels()}\n disabled={isExportingChannels}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border-subtle hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tr('channel.exportCSV', 'Export to CSV')}\n data-tour=\"channels-list-export-button\"\n >\n {isExportingChannels ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={16} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n )}\n </div>\n </div>\n\n {/* Search and Filters */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n aria-label={tr('channel.searchChannels', 'Search channels...')}\n type=\"text\"\n placeholder={tr('channel.searchChannels', 'Search channels...')}\n value={filter.search || ''}\n onChange={(e) => updateFilter({ search: e.target.value })}\n className=\"w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <select\n value={filter.platform || ''}\n onChange={(e) =>\n updateFilter({ platform: (e.target.value as ChannelPlatform) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {Object.entries(PLATFORM_CONFIG).map(([key, config]) => (\n <option key={key} value={key}>\n {config.label}\n </option>\n ))}\n </select>\n\n <select\n value={filter.status || ''}\n onChange={(e) =>\n updateFilter({ status: (e.target.value as ChannelStatus) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-bg-surface text-text-primary border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">{tr('channel.allStatus', 'All Status')}</option>\n {Object.entries(STATUS_CONFIG).map(([key, config]) => (\n <option key={key} value={key}>\n {config.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Export Error */}\n {exportChannelsError && (\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 <p className=\"text-sm text-status-error-text flex-1\">{exportChannelsError}</p>\n </div>\n )}\n\n {channels.length > 0 && (\n <div\n className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\"\n data-tour=\"channels-list-bulk-actions\"\n >\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllChannels(event.target.checked)}\n aria-label=\"Select all channels\"\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>{tr('channel.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedChannelIds.size} {tr('channel.selectedCountLabel', 'selected')}\n </span>\n {selectedChannelIds.size > 0 && (\n <button\n type=\"button\"\n onClick={() => setSelectedChannelIds(new Set())}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('channel.clearSelection', 'Clear selection')}\n </button>\n )}\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={handleBulkHealthCheck}\n disabled={selectedChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-border-subtle rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {tr('channel.healthCheck', 'Health Check')}\n {selectedChannels.length > 0 ? ` (${selectedChannels.length})` : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={handleBulkActivate}\n disabled={selectedInactiveChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-success-border/30 text-status-success-text rounded-md hover:bg-status-success-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.activateSelected', 'Activate Selected')}\n {selectedInactiveChannels.length > 0\n ? ` (${selectedInactiveChannels.length})`\n : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={handleBulkDeactivate}\n disabled={selectedActiveChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-warning-border text-status-warning-text rounded-md hover:bg-status-warning-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.deactivateSelected', 'Deactivate Selected')}\n {selectedActiveChannels.length > 0 ? ` (${selectedActiveChannels.length})` : ''}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedChannels.length === 0 || bulkActionLoading !== null}\n className=\"px-3 py-2 text-sm font-medium border border-status-error-border/30 text-status-error-text rounded-md hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n {tr('channel.deleteSelected', 'Delete Selected')}\n {selectedChannels.length > 0 ? ` (${selectedChannels.length})` : ''}\n </button>\n </PermissionGuard>\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\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 {tr('channel.loadFailed', 'Failed to load channels')}\n </p>\n <p className=\"text-xs text-status-error-text\">{error}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => refetch()}\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 {tr('channel.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && channels.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && channels.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Radio size={48} className=\"text-text-secondary mb-4\" />\n <h3 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('channel.noChannelsFound', 'No channels found')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mb-4\">\n {filter.search || filter.platform || filter.status\n ? tr('channel.adjustFilters', 'Try adjusting your filters to see more channels.')\n : tr(\n 'channel.createFirstChannelPrompt',\n 'Create your first communication channel to get started.'\n )}\n </p>\n {canCreate && !filter.search && !filter.platform && !filter.status && (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={16} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n )}\n </div>\n )}\n\n {/* Channels List */}\n {channels.length > 0 && (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\" data-tour=\"channels-list-grid\">\n {channels.map((channel: Channel) => {\n const platformConfig = getPlatformConfig(channel.platform);\n const statusConfig = getStatusConfig(channel.status);\n const healthConfig = getHealthConfig(channel.connectionHealth);\n const StatusIcon = statusConfig.icon;\n const HealthIcon = healthConfig.icon;\n const isActionLoading = actionLoading === channel.id;\n\n return (\n <div\n key={channel.id}\n className={`p-4 bg-surface border rounded-lg transition-colors group ${selectedChannelIds.has(channel.id) ? 'ring-2 ring-action-primary-bg border-action-primary-bg/40' : 'border-border-subtle hover:border-action-primary-bg/40'}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n checked={selectedChannelIds.has(channel.id)}\n onChange={(event) =>\n toggleChannelSelection(channel.id, event.target.checked)\n }\n aria-label={`Select channel ${channel.name}`}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <Radio size={20} className=\"text-accent-primary\" />\n <span\n className={`px-2 py-0.5 text-xs font-semibold rounded-full ${platformConfig.color}`}\n >\n {platformConfig.label}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <StatusIcon size={14} className={statusConfig.color.split(' ')[0]} />\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded-full ${statusConfig.color}`}\n >\n {statusConfig.label}\n </span>\n </div>\n </div>\n\n {/* Content - Clickable area */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/channels/${channel.id}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`/channels/${channel.id}`);\n }\n }}\n className=\"cursor-pointer\"\n >\n <h3 className=\"text-sm font-semibold text-text-primary mb-1 truncate\">\n {channel.name}\n </h3>\n\n {channel.description && (\n <p className=\"text-xs text-text-secondary mb-2 line-clamp-2\">\n {channel.description}\n </p>\n )}\n\n {/* Adapter */}\n {channel.adapterType && (\n <p className=\"text-xs text-text-secondary mb-2 font-mono\">\n {channel.adapterType.replace(/_/g, ' ')}\n </p>\n )}\n\n {/* Stats Row */}\n <div className=\"flex items-center gap-3 text-xs text-text-secondary mb-2\">\n <span>\n {tr('channel.priority', 'Priority')}: {channel.priority}\n </span>\n </div>\n\n {/* Health & Connection Status */}\n <div className=\"flex items-center gap-3 text-xs\">\n <div className=\"flex items-center gap-1\">\n <HealthIcon size={12} className={healthConfig.color} />\n <span className={healthConfig.color}>{healthConfig.label}</span>\n </div>\n <span>•</span>\n <span\n className={\n channel.isConnected ? 'text-status-success-text' : 'text-text-secondary'\n }\n >\n {channel.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.notConnected', 'Not Connected')}\n </span>\n </div>\n </div>\n\n {/* Tags */}\n {channel.tagAssignments && channel.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mt-2\" aria-label=\"Channel tags\">\n {channel.tagAssignments.slice(0, 4).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {channel.tagAssignments.length > 4 && (\n <span className=\"text-xs text-text-secondary\">\n +{channel.tagAssignments.length - 4}\n </span>\n )}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"mt-3 pt-3 border-t border-border-subtle flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channel.id}`)}\n className=\"p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer\"\n title={tr('channel.viewDetails', 'View Details')}\n >\n <Eye size={14} />\n </button>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${channel.id}/edit`)}\n className=\"p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors cursor-pointer\"\n title={tr('channel.editChannel', 'Edit Channel')}\n >\n <Pencil size={14} />\n </button>\n )}\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={() => handleHealthCheck(channel.id)}\n disabled={isActionLoading}\n className=\"p-1.5 text-text-secondary hover:text-status-info-text hover:bg-status-info-bg-subtle rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n title={tr('channel.runHealthCheck', 'Run Health Check')}\n >\n {isActionLoading ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={14} />\n )}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={() => handleToggleStatus(channel)}\n disabled={isActionLoading}\n className={`p-1.5 rounded transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed ${\n channel.status === 'ACTIVE'\n ? 'text-text-secondary hover:text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n title={\n channel.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')\n }\n >\n {channel.status === 'ACTIVE' ? (\n <PowerOff size={14} />\n ) : (\n <Power size={14} />\n )}\n </button>\n </PermissionGuard>\n </div>\n\n {/* Delete */}\n {deleteConfirm === channel.id ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-secondary\">\n {tr('channel.deleteQuestion', 'Delete?')}\n </span>\n <button\n type=\"button\"\n onClick={() => handleDelete(channel.id)}\n disabled={isActionLoading}\n className=\"px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer disabled:cursor-not-allowed\"\n >\n {isActionLoading ? '...' : tr('channel.yes', 'Yes')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(null)}\n className=\"px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken rounded transition-colors cursor-pointer\"\n >\n {tr('channel.no', 'No')}\n </button>\n </div>\n ) : (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(channel.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors cursor-pointer\"\n title={tr('channel.deleteChannel', 'Delete Channel')}\n >\n <Trash2 size={14} />\n </button>\n </PermissionGuard>\n )}\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Pagination */}\n {total > pageSize && (\n <div className=\"flex items-center justify-between px-1 py-2\">\n <p className=\"text-sm text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium text-text-primary\">\n {currentPage * pageSize + 1}–{Math.min((currentPage + 1) * pageSize, total)}\n </span>{' '}\n {tr('channel.of', 'of')} <span className=\"font-medium text-text-primary\">{total}</span>{' '}\n {tr('channel.channelsLower', 'channels')}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {currentPage + 1} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={currentPage >= totalPages - 1}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}