@burdenoff/microfe-channel 2026.1013.2 → 2026.1107.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 (161) hide show
  1. package/dist/channel/ChannelRoot.d.ts +0 -2
  2. package/dist/channel/ChannelRoot.js +9 -6
  3. package/dist/channel/ChannelRoot.js.map +1 -1
  4. package/dist/channel/ChannelRoutes.d.ts +1 -2
  5. package/dist/channel/ChannelRoutes.js +5 -6
  6. package/dist/channel/ChannelRoutes.js.map +1 -1
  7. package/dist/channel/components/AdapterCredentialsForm.d.ts +2 -4
  8. package/dist/channel/components/AdapterCredentialsForm.js +1 -2
  9. package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
  10. package/dist/channel/components/FeatureNotEnabled.d.ts +1 -2
  11. package/dist/channel/components/FeatureNotEnabled.js +1 -2
  12. package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
  13. package/dist/channel/components/MarkdownEditor.d.ts +1 -2
  14. package/dist/channel/components/MarkdownEditor.js +2 -5
  15. package/dist/channel/components/MarkdownEditor.js.map +1 -1
  16. package/dist/channel/components/PermissionGuard.d.ts +0 -2
  17. package/dist/channel/components/PermissionGuard.js +1 -2
  18. package/dist/channel/components/PermissionGuard.js.map +1 -1
  19. package/dist/channel/config/adapterCredentials.d.ts +0 -2
  20. package/dist/channel/config/adapterCredentials.js +3 -7
  21. package/dist/channel/config/adapterCredentials.js.map +1 -1
  22. package/dist/channel/constants/permissions.d.ts +0 -1
  23. package/dist/channel/constants/permissions.js +1 -2
  24. package/dist/channel/constants/permissions.js.map +1 -1
  25. package/dist/channel/graphql/global-client.d.ts +0 -2
  26. package/dist/channel/graphql/global-client.js +5 -6
  27. package/dist/channel/graphql/global-client.js.map +1 -1
  28. package/dist/channel/hooks/useChannelOrgSettings.d.ts +0 -2
  29. package/dist/channel/hooks/useChannelOrgSettings.js +20 -21
  30. package/dist/channel/hooks/useChannelOrgSettings.js.map +1 -1
  31. package/dist/channel/hooks/useChannelSubscription.d.ts +0 -1
  32. package/dist/channel/hooks/useChannelSubscription.js +7 -12
  33. package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
  34. package/dist/channel/hooks/useChannels.d.ts +20 -30
  35. package/dist/channel/hooks/useChannels.js +100 -101
  36. package/dist/channel/hooks/useChannels.js.map +1 -1
  37. package/dist/channel/hooks/useTr.d.ts +0 -1
  38. package/dist/channel/hooks/useTr.js +1 -2
  39. package/dist/channel/hooks/useTr.js.map +1 -1
  40. package/dist/channel/index.d.ts +0 -1
  41. package/dist/channel/index.js +3 -2
  42. package/dist/channel/modules/channels/hooks/useExport.d.ts +0 -2
  43. package/dist/channel/modules/channels/hooks/useExport.js +77 -80
  44. package/dist/channel/modules/channels/hooks/useExport.js.map +1 -1
  45. package/dist/channel/modules/channels/i18n.d.ts +0 -2
  46. package/dist/channel/modules/channels/i18n.js +1 -2
  47. package/dist/channel/modules/channels/i18n.js.map +1 -1
  48. package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts +1 -2
  49. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +10 -11
  50. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  51. package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts +1 -2
  52. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +400 -401
  53. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  54. package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts +1 -2
  55. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +8 -7
  56. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  57. package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts +1 -2
  58. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +5 -10
  59. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  60. package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts +1 -2
  61. package/dist/channel/modules/channels/pages/ChannelsListPage.js +287 -288
  62. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  63. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts +1 -2
  64. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +11 -12
  65. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  66. package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts +1 -2
  67. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +1 -2
  68. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  69. package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts +1 -2
  70. package/dist/channel/modules/channels/pages/CreateChannelPage.js +4 -5
  71. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  72. package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts +1 -2
  73. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +37 -38
  74. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  75. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts +1 -2
  76. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +7 -12
  77. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  78. package/dist/channel/modules/channels/pages/EditChannelPage.d.ts +1 -2
  79. package/dist/channel/modules/channels/pages/EditChannelPage.js +105 -103
  80. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  81. package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts +1 -2
  82. package/dist/channel/modules/channels/pages/EditConnectionPage.js +22 -23
  83. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  84. package/dist/channel/modules/channels/pages/SendMessagePage.d.ts +1 -2
  85. package/dist/channel/modules/channels/pages/SendMessagePage.js +180 -187
  86. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  87. package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts +1 -2
  88. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +144 -149
  89. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  90. package/dist/channel/pages/ChannelLandingPage.d.ts +1 -2
  91. package/dist/channel/pages/ChannelLandingPage.js +20 -21
  92. package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
  93. package/dist/channel/providers/ChannelProvider.d.ts +0 -2
  94. package/dist/channel/providers/ChannelProvider.js +1 -2
  95. package/dist/channel/providers/ChannelProvider.js.map +1 -1
  96. package/dist/channel/types/index.d.ts +0 -2
  97. package/dist/channel/utils/shellBridge.d.ts +0 -1
  98. package/dist/channel/utils/shellBridge.js +1 -2
  99. package/dist/channel/utils/shellBridge.js.map +1 -1
  100. package/dist/dev/App.d.ts +1 -2
  101. package/dist/dev/main.d.ts +0 -2
  102. package/dist/generated/global-operations.d.ts +885 -336
  103. package/dist/generated/global-operations.js +2 -3
  104. package/dist/generated/global-operations.js.map +1 -1
  105. package/dist/generated/global-types.d.ts +2 -22
  106. package/dist/generated/global-types.js +1 -2
  107. package/dist/generated/global-types.js.map +1 -1
  108. package/dist/generated/wspace-operations.d.ts +4 -2
  109. package/dist/generated/wspace-types.d.ts +4 -24
  110. package/dist/index.d.ts +0 -1
  111. package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js +1 -2
  112. package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js.map +1 -1
  113. package/dist/node_modules/@apollo/client/errors/utils.js +1 -2
  114. package/dist/node_modules/@apollo/client/errors/utils.js.map +1 -1
  115. package/dist/translations/en.d.ts +0 -1
  116. package/dist/utils/nativeBridge.d.ts +0 -1
  117. package/dist/utils/nativeBridge.js +1 -2
  118. package/package.json +65 -43
  119. package/dist/channel/ChannelRoot.d.ts.map +0 -1
  120. package/dist/channel/ChannelRoutes.d.ts.map +0 -1
  121. package/dist/channel/components/AdapterCredentialsForm.d.ts.map +0 -1
  122. package/dist/channel/components/FeatureNotEnabled.d.ts.map +0 -1
  123. package/dist/channel/components/MarkdownEditor.d.ts.map +0 -1
  124. package/dist/channel/components/PermissionGuard.d.ts.map +0 -1
  125. package/dist/channel/config/adapterCredentials.d.ts.map +0 -1
  126. package/dist/channel/constants/permissions.d.ts.map +0 -1
  127. package/dist/channel/graphql/global-client.d.ts.map +0 -1
  128. package/dist/channel/hooks/useChannelOrgSettings.d.ts.map +0 -1
  129. package/dist/channel/hooks/useChannelSubscription.d.ts.map +0 -1
  130. package/dist/channel/hooks/useChannels.d.ts.map +0 -1
  131. package/dist/channel/hooks/useTr.d.ts.map +0 -1
  132. package/dist/channel/index.d.ts.map +0 -1
  133. package/dist/channel/modules/channels/hooks/useExport.d.ts.map +0 -1
  134. package/dist/channel/modules/channels/i18n.d.ts.map +0 -1
  135. package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts.map +0 -1
  136. package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts.map +0 -1
  137. package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts.map +0 -1
  138. package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts.map +0 -1
  139. package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts.map +0 -1
  140. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts.map +0 -1
  141. package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts.map +0 -1
  142. package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts.map +0 -1
  143. package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts.map +0 -1
  144. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts.map +0 -1
  145. package/dist/channel/modules/channels/pages/EditChannelPage.d.ts.map +0 -1
  146. package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts.map +0 -1
  147. package/dist/channel/modules/channels/pages/SendMessagePage.d.ts.map +0 -1
  148. package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts.map +0 -1
  149. package/dist/channel/pages/ChannelLandingPage.d.ts.map +0 -1
  150. package/dist/channel/providers/ChannelProvider.d.ts.map +0 -1
  151. package/dist/channel/types/index.d.ts.map +0 -1
  152. package/dist/channel/utils/shellBridge.d.ts.map +0 -1
  153. package/dist/dev/App.d.ts.map +0 -1
  154. package/dist/dev/main.d.ts.map +0 -1
  155. package/dist/generated/global-operations.d.ts.map +0 -1
  156. package/dist/generated/global-types.d.ts.map +0 -1
  157. package/dist/generated/wspace-operations.d.ts.map +0 -1
  158. package/dist/generated/wspace-types.d.ts.map +0 -1
  159. package/dist/index.d.ts.map +0 -1
  160. package/dist/translations/en.d.ts.map +0 -1
  161. package/dist/utils/nativeBridge.d.ts.map +0 -1
@@ -1,6 +1,5 @@
1
1
  import { FC } from 'react';
2
2
  import { ChannelRootProps } from './types';
3
-
4
3
  /**
5
4
  * Root component for the Channel microfrontend.
6
5
  *
@@ -29,4 +28,3 @@ import { ChannelRootProps } from './types';
29
28
  * ```
30
29
  */
31
30
  export declare const ChannelRoot: FC<ChannelRootProps>;
32
- //# sourceMappingURL=ChannelRoot.d.ts.map
@@ -5,11 +5,12 @@ import { useEffect as r } from "react";
5
5
  import { ErrorBoundary as i } from "@burdenoff/fe-libs/shared/components";
6
6
  import { Cable as a, Radio as o, Send as s, Settings as c } from "lucide-react";
7
7
  import { jsx as l } from "react/jsx-runtime";
8
- //#region src/channel/ChannelRoot.tsx
8
+ // src/channel/ChannelRoot.tsx
9
9
  var u = (u) => {
10
10
  let { registerNavItems: d, registerFooterItems: f, basePath: p = "/" } = u, m = e(), h = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
11
11
  return r(() => {
12
- if (d) return d([
12
+ if (!d) return;
13
+ let e = [
13
14
  {
14
15
  id: "channel-channels",
15
16
  label: m("channel.channels", "Channels"),
@@ -42,19 +43,22 @@ var u = (u) => {
42
43
  order: 130,
43
44
  group: "channel"
44
45
  }
45
- ]);
46
+ ];
47
+ return d(e);
46
48
  }, [
47
49
  d,
48
50
  p,
49
51
  m
50
52
  ]), r(() => {
51
- if (f) return f([{
53
+ if (!f) return;
54
+ let e = [{
52
55
  id: "channel-status",
53
56
  label: m("channel.channelsReady", "Channels Ready"),
54
57
  type: "status",
55
58
  order: 10,
56
59
  section: "left"
57
- }]);
60
+ }];
61
+ return f(e);
58
62
  }, [f, m]), /* @__PURE__ */ l(i, {
59
63
  fallback: /* @__PURE__ */ l("div", {
60
64
  className: "p-6 text-center text-text-secondary",
@@ -66,7 +70,6 @@ var u = (u) => {
66
70
  })
67
71
  });
68
72
  };
69
- //#endregion
70
73
  export { u as ChannelRoot };
71
74
 
72
75
  //# sourceMappingURL=ChannelRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelRoot.js","names":[],"sources":["../../src/channel/ChannelRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useCallback } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from './hooks/useTr';\nimport { Radio, Cable, Send, Settings } from 'lucide-react';\nimport { ChannelProvider } from './providers/ChannelProvider';\nimport type { NavItem, ChannelRootProps } from './types';\nimport { ChannelRoutes } from './ChannelRoutes';\n\n/**\n * Root component for the Channel microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { ChannelRoot, type ChannelRootProps } from '@burdenoff/microfe-channel';\n *\n * function App() {\n * const props: ChannelRootProps = {\n * basePath: '/channels',\n * navigate: (path) => router.push(path),\n * currentUser: { id: '1', email: 'user@example.com', firstName: 'John', lastName: 'Doe' },\n * workspaceId: 'ws-123',\n * organizationId: 'org-123',\n * apiGatewayUrl: 'https://api.example.com/workspaces/graphql',\n * authToken: 'token',\n * registerNavItems: (items) => { return () => {}; },\n * };\n *\n * return <ChannelRoot {...props} />;\n * }\n * ```\n */\nexport const ChannelRoot: FC<ChannelRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const tr = useTr();\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'channel-channels',\n label: tr('channel.channels', 'Channels'),\n path: basePath === '/' ? '/' : joinPath(basePath, 'list'),\n icon: <Radio size={20} />,\n order: 100,\n group: 'channel',\n },\n {\n id: 'channel-connections',\n label: tr('channel.connections', 'Connections'),\n path: joinPath(basePath, 'connections'),\n icon: <Cable size={20} />,\n order: 110,\n group: 'channel',\n },\n {\n id: 'channel-send',\n label: tr('channel.sendMessage', 'Send Message'),\n path: joinPath(basePath, 'send'),\n icon: <Send size={20} />,\n order: 120,\n group: 'channel',\n },\n {\n id: 'channel-settings',\n label: tr('channel.settings', 'Settings'),\n path: joinPath(basePath, 'settings'),\n icon: <Settings size={20} />,\n order: 130,\n group: 'channel',\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n // Register footer items (example: Channel status indicator in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'channel-status',\n label: tr('channel.channelsReady', 'Channels Ready'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <ChannelProvider {...props}>\n <ChannelRoutes />\n </ChannelProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;AAoCA,IAAa,KAAqC,MAAU;CAC1D,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQ,GAC5D,IAAK,GAAO,EAGZ,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AAoEtD,QA/DA,QAAgB;AACT,QAyCL,QAHgB,EApCY;GAC1B;IACE,IAAI;IACJ,OAAO,EAAG,oBAAoB,WAAW;IACzC,MAAM,MAAa,MAAM,MAAM,EAAS,GAAU,OAAO;IACzD,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAG,uBAAuB,cAAc;IAC/C,MAAM,EAAS,GAAU,cAAc;IACvC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAG,uBAAuB,eAAe;IAChD,MAAM,EAAS,GAAU,OAAO;IAChC,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAG,oBAAoB,WAAW;IACzC,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR;GACF,CAGyC;IAIzC;EAAC;EAAkB;EAAU;EAAG,CAAC,EAGpC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAG,yBAAyB,iBAAiB;GACpD,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAG,CAAC,EAG3B,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aAAsC;GAE/C,CAAA;YAGR,kBAAC,GAAD;GAAiB,GAAI;aACnB,kBAAC,GAAD,EAAiB,CAAA;GACD,CAAA;EACJ,CAAA"}
1
+ {"version":3,"file":"ChannelRoot.js","names":["ChannelRoot","props","tr","useTr","joinPath","base","path","useEffect","registerNavItems","navItems","basePath","Radio","Cable","Send","Settings","registerFooterItems","footerItems","ErrorBoundary","ChannelProvider","ChannelRoutes"],"sources":["../../src/channel/ChannelRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from './hooks/useTr';\nimport { Radio, Cable, Send, Settings } from 'lucide-react';\nimport { ChannelProvider } from './providers/ChannelProvider';\nimport type { NavItem, ChannelRootProps } from './types';\nimport { ChannelRoutes } from './ChannelRoutes';\n\n/**\n * Root component for the Channel microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { ChannelRoot, type ChannelRootProps } from '@burdenoff/microfe-channel';\n *\n * function App() {\n * const props: ChannelRootProps = {\n * basePath: '/channels',\n * navigate: (path) => router.push(path),\n * currentUser: { id: '1', email: 'user@example.com', firstName: 'John', lastName: 'Doe' },\n * workspaceId: 'ws-123',\n * organizationId: 'org-123',\n * apiGatewayUrl: 'https://api.example.com/workspaces/graphql',\n * authToken: 'token',\n * registerNavItems: (items) => { return () => {}; },\n * };\n *\n * return <ChannelRoot {...props} />;\n * }\n * ```\n */\nexport const ChannelRoot: FC<ChannelRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const tr = useTr();\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'channel-channels',\n label: tr('channel.channels', 'Channels'),\n path: basePath === '/' ? '/' : joinPath(basePath, 'list'),\n icon: <Radio size={20} />,\n order: 100,\n group: 'channel',\n },\n {\n id: 'channel-connections',\n label: tr('channel.connections', 'Connections'),\n path: joinPath(basePath, 'connections'),\n icon: <Cable size={20} />,\n order: 110,\n group: 'channel',\n },\n {\n id: 'channel-send',\n label: tr('channel.sendMessage', 'Send Message'),\n path: joinPath(basePath, 'send'),\n icon: <Send size={20} />,\n order: 120,\n group: 'channel',\n },\n {\n id: 'channel-settings',\n label: tr('channel.settings', 'Settings'),\n path: joinPath(basePath, 'settings'),\n icon: <Settings size={20} />,\n order: 130,\n group: 'channel',\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n // Register footer items (example: Channel status indicator in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'channel-status',\n label: tr('channel.channelsReady', 'Channels Ready'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <ChannelProvider {...props}>\n <ChannelRoutes />\n </ChannelProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;AAoCA,IAAaA,KAAqC,MAAU;CAC1D,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQC,GAC5DC,IAAKC,EAAM,GAGXC,KAAY,GAAc,OACZC,EAAK,SAAS,GAAG,IAAIA,EAAK,MAAM,GAAG,EAAE,IAAIA,MACzCC,EAAK,WAAW,GAAG,IAAIA,IAAO,IAAIA;CAoEtD,OA/DAC,QAAgB;EACd,IAAI,CAACC,GAAkB;EAEvB,IAAMC,IAAsB;GAC1B;IACE,IAAI;IACJ,OAAOP,EAAG,oBAAoB,UAAU;IACxC,MAAMQ,MAAa,MAAM,MAAMN,EAASM,GAAU,MAAM;IACxD,MAAM,kBAACC,GAAD,EAAO,MAAM,GAAK,CAAA;IACxB,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOT,EAAG,uBAAuB,aAAa;IAC9C,MAAME,EAASM,GAAU,aAAa;IACtC,MAAM,kBAACE,GAAD,EAAO,MAAM,GAAK,CAAA;IACxB,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOV,EAAG,uBAAuB,cAAc;IAC/C,MAAME,EAASM,GAAU,MAAM;IAC/B,MAAM,kBAACG,GAAD,EAAM,MAAM,GAAK,CAAA;IACvB,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOX,EAAG,oBAAoB,UAAU;IACxC,MAAME,EAASM,GAAU,UAAU;IACnC,MAAM,kBAACI,GAAD,EAAU,MAAM,GAAK,CAAA;IAC3B,OAAO;IACP,OAAO;GACT;EACF;EAMA,OAHgBN,EAAiBC,CAG1B;CACT,GAAG;EAACD;EAAkBE;EAAUR;CAAE,CAAC,GAGnCK,QAAgB;EACd,IAAI,CAACQ,GAAqB;EAE1B,IAAMC,IAAc,CAClB;GACE,IAAI;GACJ,OAAOd,EAAG,yBAAyB,gBAAgB;GACnD,MAAM;GACN,OAAO;GACP,SAAS;EACX,CACF;EAGA,OADgBa,EAAoBC,CAC7B;CACT,GAAG,CAACD,GAAqBb,CAAE,CAAC,GAG1B,kBAACe,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;GAAsC,UAAA;EAEhD,CAAA;EAGP,UAAA,kBAACC,GAAD;GAAiB,GAAIjB;GACnB,UAAA,kBAACkB,GAAD,CAAgB,CAAA;EACD,CAAA;CACJ,CAAA;AAEnB"}
@@ -6,5 +6,4 @@
6
6
  * be rolled out or kill-switched independently. Flag names follow
7
7
  * `channels.<route-name>` (see vibecontrols-channel-stability tracker).
8
8
  */
9
- export declare function ChannelRoutes(): import("react").JSX.Element;
10
- //# sourceMappingURL=ChannelRoutes.d.ts.map
9
+ export declare function ChannelRoutes(): import('react').JSX.Element;
@@ -12,17 +12,17 @@ import { ChannelSettingsPage as u } from "./modules/channels/pages/ChannelSettin
12
12
  import { DeliveryHistoryPage as d } from "./modules/channels/pages/DeliveryHistoryPage.js";
13
13
  import { WebhookEventsPage as f } from "./modules/channels/pages/WebhookEventsPage.js";
14
14
  import { ChannelUsersPage as p } from "./modules/channels/pages/ChannelUsersPage.js";
15
- import { FeatureNotEnabled as m } from "./components/FeatureNotEnabled.js";
16
- import { ChannelLandingPage as h } from "./pages/ChannelLandingPage.js";
15
+ import { ChannelLandingPage as m } from "./pages/ChannelLandingPage.js";
16
+ import { FeatureNotEnabled as h } from "./components/FeatureNotEnabled.js";
17
17
  import { Navigate as g, Route as _, Routes as v } from "react-router";
18
18
  import { jsx as y, jsxs as b } from "react/jsx-runtime";
19
19
  import { FeatureGate as x } from "@burdenoff/fe-libs/shared/feature-flags";
20
- //#region src/channel/ChannelRoutes.tsx
20
+ // src/channel/ChannelRoutes.tsx
21
21
  function S(e, t) {
22
22
  return /* @__PURE__ */ y(x, {
23
23
  flag: e,
24
24
  defaultValue: !0,
25
- fallback: /* @__PURE__ */ y(m, { flag: e }),
25
+ fallback: /* @__PURE__ */ y(h, { flag: e }),
26
26
  children: t
27
27
  });
28
28
  }
@@ -30,7 +30,7 @@ function C() {
30
30
  return /* @__PURE__ */ b(v, { children: [
31
31
  /* @__PURE__ */ y(_, {
32
32
  index: !0,
33
- element: S("channels.dashboard", /* @__PURE__ */ y(h, {}))
33
+ element: S("channels.dashboard", /* @__PURE__ */ y(m, {}))
34
34
  }),
35
35
  /* @__PURE__ */ y(_, {
36
36
  path: "dashboard",
@@ -97,7 +97,6 @@ function C() {
97
97
  })
98
98
  ] });
99
99
  }
100
- //#endregion
101
100
  export { C as ChannelRoutes };
102
101
 
103
102
  //# sourceMappingURL=ChannelRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelRoutes.js","names":[],"sources":["../../src/channel/ChannelRoutes.tsx"],"sourcesContent":["import { Navigate, Route, Routes } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ChannelDashboardPage } from './modules/channels/pages/ChannelDashboardPage';\nimport { ChannelsListPage } from './modules/channels/pages/ChannelsListPage';\nimport { ChannelDetailsPage } from './modules/channels/pages/ChannelDetailsPage';\nimport { CreateChannelPage } from './modules/channels/pages/CreateChannelPage';\nimport { EditChannelPage } from './modules/channels/pages/EditChannelPage';\nimport { SendMessagePage } from './modules/channels/pages/SendMessagePage';\nimport { ConnectionsListPage } from './modules/channels/pages/ConnectionsListPage';\nimport { ConnectionDetailsPage } from './modules/channels/pages/ConnectionDetailsPage';\nimport { CreateConnectionPage } from './modules/channels/pages/CreateConnectionPage';\nimport { EditConnectionPage } from './modules/channels/pages/EditConnectionPage';\nimport { ChannelSettingsPage } from './modules/channels/pages/ChannelSettingsPage';\nimport { DeliveryHistoryPage } from './modules/channels/pages/DeliveryHistoryPage';\nimport { WebhookEventsPage } from './modules/channels/pages/WebhookEventsPage';\nimport { ChannelUsersPage } from './modules/channels/pages/ChannelUsersPage';\nimport { ChannelLandingPage } from './pages/ChannelLandingPage';\nimport { FeatureNotEnabled } from './components/FeatureNotEnabled';\nimport type { ReactNode } from 'react';\n\n/**\n * Wraps a route element in a FeatureGate. The channel module is shipped & stable\n * and now lives in the admin shell, and every `channels.*` flag is seeded ON in\n * global-tenant-svc — so absent flags default to ENABLED (defaultValue=true) to\n * match the backend and avoid a hard \"Feature not enabled\" lockout when the\n * evaluations map hasn't loaded (no-workspace / pre-bootstrap / fetch error).\n * An explicit `channels.<x> = false` still acts as a kill-switch (flag value wins).\n */\nfunction gated(flag: string, element: ReactNode): ReactNode {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureNotEnabled flag={flag} />}>\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Internal router for the Channel microfrontend.\n *\n * The shell mounts this at /channels/* so all paths here are relative to /channels/.\n * Every concrete route is wrapped with a FeatureGate so individual surfaces can\n * be rolled out or kill-switched independently. Flag names follow\n * `channels.<route-name>` (see vibecontrols-channel-stability tracker).\n */\nexport function ChannelRoutes() {\n return (\n <Routes>\n {/* Tabbed landing — gated under the dashboard flag */}\n <Route index element={gated('channels.dashboard', <ChannelLandingPage />)} />\n\n {/* Dashboard */}\n <Route path=\"dashboard\" element={gated('channels.dashboard', <ChannelDashboardPage />)} />\n\n {/* Channel CRUD — flat under /channels/ */}\n <Route path=\"list\" element={gated('channels.list', <ChannelsListPage />)} />\n <Route path=\"create\" element={gated('channels.create', <CreateChannelPage />)} />\n <Route path=\":channelId\" element={gated('channels.detail', <ChannelDetailsPage />)} />\n <Route path=\":channelId/edit\" element={gated('channels.edit', <EditChannelPage />)} />\n <Route\n path=\":channelId/deliveries\"\n element={gated('channels.deliveries', <DeliveryHistoryPage />)}\n />\n <Route\n path=\":channelId/webhooks\"\n element={gated('channels.webhooks', <WebhookEventsPage />)}\n />\n <Route path=\":channelId/users\" element={gated('channels.users', <ChannelUsersPage />)} />\n\n {/* Connections */}\n <Route\n path=\"connections\"\n element={gated('channels.connections.list', <ConnectionsListPage />)}\n />\n <Route\n path=\"connections/create\"\n element={gated('channels.connections.create', <CreateConnectionPage />)}\n />\n <Route\n path=\"connections/:connectionId\"\n element={gated('channels.connections.detail', <ConnectionDetailsPage />)}\n />\n <Route\n path=\"connections/:connectionId/edit\"\n element={gated('channels.connections.edit', <EditConnectionPage />)}\n />\n\n {/* Send Message */}\n <Route path=\"send\" element={gated('channels.send', <SendMessagePage />)} />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated('channels.settings', <ChannelSettingsPage />)} />\n\n {/* Catch-all → dashboard */}\n <Route path=\"*\" element={<Navigate to=\"dashboard\" replace />} />\n </Routes>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,EAAM,GAAc,GAA+B;AAC1D,QACE,kBAAC,GAAD;EAAmB;EAAM,cAAc;EAAM,UAAU,kBAAC,GAAD,EAAyB,SAAQ,CAAA;YACrF;EACW,CAAA;;AAYlB,SAAgB,IAAgB;AAC9B,QACE,kBAAC,GAAD,EAAA,UAAA;EAEE,kBAAC,GAAD;GAAO,OAAA;GAAM,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAsB,CAAA,CAAC;GAAI,CAAA;EAG7E,kBAAC,GAAD;GAAO,MAAK;GAAY,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAwB,CAAA,CAAC;GAAI,CAAA;EAG1F,kBAAC,GAAD;GAAO,MAAK;GAAO,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAoB,CAAA,CAAC;GAAI,CAAA;EAC5E,kBAAC,GAAD;GAAO,MAAK;GAAS,SAAS,EAAM,mBAAmB,kBAAC,GAAD,EAAqB,CAAA,CAAC;GAAI,CAAA;EACjF,kBAAC,GAAD;GAAO,MAAK;GAAa,SAAS,EAAM,mBAAmB,kBAAC,GAAD,EAAsB,CAAA,CAAC;GAAI,CAAA;EACtF,kBAAC,GAAD;GAAO,MAAK;GAAkB,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAmB,CAAA,CAAC;GAAI,CAAA;EACtF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,uBAAuB,kBAAC,GAAD,EAAuB,CAAA,CAAC;GAC9D,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,qBAAqB,kBAAC,GAAD,EAAqB,CAAA,CAAC;GAC1D,CAAA;EACF,kBAAC,GAAD;GAAO,MAAK;GAAmB,SAAS,EAAM,kBAAkB,kBAAC,GAAD,EAAoB,CAAA,CAAC;GAAI,CAAA;EAGzF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,6BAA6B,kBAAC,GAAD,EAAuB,CAAA,CAAC;GACpE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,+BAA+B,kBAAC,GAAD,EAAwB,CAAA,CAAC;GACvE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,+BAA+B,kBAAC,GAAD,EAAyB,CAAA,CAAC;GACxE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,6BAA6B,kBAAC,GAAD,EAAsB,CAAA,CAAC;GACnE,CAAA;EAGF,kBAAC,GAAD;GAAO,MAAK;GAAO,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAmB,CAAA,CAAC;GAAI,CAAA;EAG3E,kBAAC,GAAD;GAAO,MAAK;GAAW,SAAS,EAAM,qBAAqB,kBAAC,GAAD,EAAuB,CAAA,CAAC;GAAI,CAAA;EAGvF,kBAAC,GAAD;GAAO,MAAK;GAAI,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EACzD,EAAA,CAAA"}
1
+ {"version":3,"file":"ChannelRoutes.js","names":["gated","FeatureGate","FeatureNotEnabled","element","ChannelRoutes","Routes","Route","ChannelLandingPage","ChannelDashboardPage","ChannelsListPage","CreateChannelPage","ChannelDetailsPage","EditChannelPage","DeliveryHistoryPage","WebhookEventsPage","ChannelUsersPage","ConnectionsListPage","CreateConnectionPage","ConnectionDetailsPage","EditConnectionPage","SendMessagePage","ChannelSettingsPage","Navigate"],"sources":["../../src/channel/ChannelRoutes.tsx"],"sourcesContent":["import { Navigate, Route, Routes } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ChannelDashboardPage } from './modules/channels/pages/ChannelDashboardPage';\nimport { ChannelsListPage } from './modules/channels/pages/ChannelsListPage';\nimport { ChannelDetailsPage } from './modules/channels/pages/ChannelDetailsPage';\nimport { CreateChannelPage } from './modules/channels/pages/CreateChannelPage';\nimport { EditChannelPage } from './modules/channels/pages/EditChannelPage';\nimport { SendMessagePage } from './modules/channels/pages/SendMessagePage';\nimport { ConnectionsListPage } from './modules/channels/pages/ConnectionsListPage';\nimport { ConnectionDetailsPage } from './modules/channels/pages/ConnectionDetailsPage';\nimport { CreateConnectionPage } from './modules/channels/pages/CreateConnectionPage';\nimport { EditConnectionPage } from './modules/channels/pages/EditConnectionPage';\nimport { ChannelSettingsPage } from './modules/channels/pages/ChannelSettingsPage';\nimport { DeliveryHistoryPage } from './modules/channels/pages/DeliveryHistoryPage';\nimport { WebhookEventsPage } from './modules/channels/pages/WebhookEventsPage';\nimport { ChannelUsersPage } from './modules/channels/pages/ChannelUsersPage';\nimport { ChannelLandingPage } from './pages/ChannelLandingPage';\nimport { FeatureNotEnabled } from './components/FeatureNotEnabled';\nimport type { ReactNode } from 'react';\n\n/**\n * Wraps a route element in a FeatureGate. The channel module is shipped & stable\n * and now lives in the admin shell, and every `channels.*` flag is seeded ON in\n * global-tenant-svc — so absent flags default to ENABLED (defaultValue=true) to\n * match the backend and avoid a hard \"Feature not enabled\" lockout when the\n * evaluations map hasn't loaded (no-workspace / pre-bootstrap / fetch error).\n * An explicit `channels.<x> = false` still acts as a kill-switch (flag value wins).\n */\nfunction gated(flag: string, element: ReactNode): ReactNode {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureNotEnabled flag={flag} />}>\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Internal router for the Channel microfrontend.\n *\n * The shell mounts this at /channels/* so all paths here are relative to /channels/.\n * Every concrete route is wrapped with a FeatureGate so individual surfaces can\n * be rolled out or kill-switched independently. Flag names follow\n * `channels.<route-name>` (see vibecontrols-channel-stability tracker).\n */\nexport function ChannelRoutes() {\n return (\n <Routes>\n {/* Tabbed landing — gated under the dashboard flag */}\n <Route index element={gated('channels.dashboard', <ChannelLandingPage />)} />\n\n {/* Dashboard */}\n <Route path=\"dashboard\" element={gated('channels.dashboard', <ChannelDashboardPage />)} />\n\n {/* Channel CRUD — flat under /channels/ */}\n <Route path=\"list\" element={gated('channels.list', <ChannelsListPage />)} />\n <Route path=\"create\" element={gated('channels.create', <CreateChannelPage />)} />\n <Route path=\":channelId\" element={gated('channels.detail', <ChannelDetailsPage />)} />\n <Route path=\":channelId/edit\" element={gated('channels.edit', <EditChannelPage />)} />\n <Route\n path=\":channelId/deliveries\"\n element={gated('channels.deliveries', <DeliveryHistoryPage />)}\n />\n <Route\n path=\":channelId/webhooks\"\n element={gated('channels.webhooks', <WebhookEventsPage />)}\n />\n <Route path=\":channelId/users\" element={gated('channels.users', <ChannelUsersPage />)} />\n\n {/* Connections */}\n <Route\n path=\"connections\"\n element={gated('channels.connections.list', <ConnectionsListPage />)}\n />\n <Route\n path=\"connections/create\"\n element={gated('channels.connections.create', <CreateConnectionPage />)}\n />\n <Route\n path=\"connections/:connectionId\"\n element={gated('channels.connections.detail', <ConnectionDetailsPage />)}\n />\n <Route\n path=\"connections/:connectionId/edit\"\n element={gated('channels.connections.edit', <EditConnectionPage />)}\n />\n\n {/* Send Message */}\n <Route path=\"send\" element={gated('channels.send', <SendMessagePage />)} />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated('channels.settings', <ChannelSettingsPage />)} />\n\n {/* Catch-all → dashboard */}\n <Route path=\"*\" element={<Navigate to=\"dashboard\" replace />} />\n </Routes>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,SAASA,EAAM,GAAc,GAA+B;CAC1D,OACE,kBAACC,GAAD;EAAmB;EAAM,cAAc;EAAM,UAAU,kBAACC,GAAD,EAAyB,QAAO,CAAA;EACpF,UAAAC;CACU,CAAA;AAEjB;AAUA,SAAgBC,IAAgB;CAC9B,OACE,kBAACC,GAAD,EAAA,UAAA;EAEE,kBAACC,GAAD;GAAO,OAAA;GAAM,SAASN,EAAM,sBAAsB,kBAACO,GAAD,CAAqB,CAAA,CAAC;EAAI,CAAA;EAG5E,kBAACD,GAAD;GAAO,MAAK;GAAY,SAASN,EAAM,sBAAsB,kBAACQ,GAAD,CAAuB,CAAA,CAAC;EAAI,CAAA;EAGzF,kBAACF,GAAD;GAAO,MAAK;GAAO,SAASN,EAAM,iBAAiB,kBAACS,GAAD,CAAmB,CAAA,CAAC;EAAI,CAAA;EAC3E,kBAACH,GAAD;GAAO,MAAK;GAAS,SAASN,EAAM,mBAAmB,kBAACU,GAAD,CAAoB,CAAA,CAAC;EAAI,CAAA;EAChF,kBAACJ,GAAD;GAAO,MAAK;GAAa,SAASN,EAAM,mBAAmB,kBAACW,GAAD,CAAqB,CAAA,CAAC;EAAI,CAAA;EACrF,kBAACL,GAAD;GAAO,MAAK;GAAkB,SAASN,EAAM,iBAAiB,kBAACY,GAAD,CAAkB,CAAA,CAAC;EAAI,CAAA;EACrF,kBAACN,GAAD;GACE,MAAK;GACL,SAASN,EAAM,uBAAuB,kBAACa,GAAD,CAAsB,CAAA,CAAC;EAC9D,CAAA;EACD,kBAACP,GAAD;GACE,MAAK;GACL,SAASN,EAAM,qBAAqB,kBAACc,GAAD,CAAoB,CAAA,CAAC;EAC1D,CAAA;EACD,kBAACR,GAAD;GAAO,MAAK;GAAmB,SAASN,EAAM,kBAAkB,kBAACe,GAAD,CAAmB,CAAA,CAAC;EAAI,CAAA;EAGxF,kBAACT,GAAD;GACE,MAAK;GACL,SAASN,EAAM,6BAA6B,kBAACgB,GAAD,CAAsB,CAAA,CAAC;EACpE,CAAA;EACD,kBAACV,GAAD;GACE,MAAK;GACL,SAASN,EAAM,+BAA+B,kBAACiB,GAAD,CAAuB,CAAA,CAAC;EACvE,CAAA;EACD,kBAACX,GAAD;GACE,MAAK;GACL,SAASN,EAAM,+BAA+B,kBAACkB,GAAD,CAAwB,CAAA,CAAC;EACxE,CAAA;EACD,kBAACZ,GAAD;GACE,MAAK;GACL,SAASN,EAAM,6BAA6B,kBAACmB,GAAD,CAAqB,CAAA,CAAC;EACnE,CAAA;EAGD,kBAACb,GAAD;GAAO,MAAK;GAAO,SAASN,EAAM,iBAAiB,kBAACoB,GAAD,CAAkB,CAAA,CAAC;EAAI,CAAA;EAG1E,kBAACd,GAAD;GAAO,MAAK;GAAW,SAASN,EAAM,qBAAqB,kBAACqB,GAAD,CAAsB,CAAA,CAAC;EAAI,CAAA;EAGtF,kBAACf,GAAD;GAAO,MAAK;GAAI,SAAS,kBAACgB,GAAD;IAAU,IAAG;IAAY,SAAA;GAAS,CAAA;EAAI,CAAA;CACzD,EAAA,CAAA;AAEZ"}
@@ -1,5 +1,4 @@
1
1
  import { AdapterType } from '../hooks/useChannels';
2
-
3
2
  interface AdapterCredentialsFormProps {
4
3
  adapterType: AdapterType | undefined;
5
4
  credentials: Record<string, unknown>;
@@ -8,13 +7,12 @@ interface AdapterCredentialsFormProps {
8
7
  disabled?: boolean;
9
8
  showPlatformMetadata?: boolean;
10
9
  }
11
- export declare function AdapterCredentialsForm({ adapterType, credentials, onChange, onValidationChange, disabled, showPlatformMetadata, }: AdapterCredentialsFormProps): import("react").JSX.Element;
10
+ export declare function AdapterCredentialsForm({ adapterType, credentials, onChange, onValidationChange, disabled, showPlatformMetadata, }: AdapterCredentialsFormProps): import('react').JSX.Element;
12
11
  /**
13
12
  * Compact validation status indicator
14
13
  */
15
14
  export declare function CredentialValidationStatus({ adapterType, credentials, }: {
16
15
  adapterType: AdapterType | undefined;
17
16
  credentials: Record<string, unknown>;
18
- }): import("react").JSX.Element | null;
17
+ }): import('react').JSX.Element | null;
19
18
  export {};
20
- //# sourceMappingURL=AdapterCredentialsForm.d.ts.map
@@ -3,7 +3,7 @@ import { getAdapterCredentialSchema as t, getDefaultCredentials as n, validateCr
3
3
  import { useEffect as i, useState as a } from "react";
4
4
  import { AlertCircle as o, CheckCircle2 as s, ExternalLink as c, Eye as l, EyeOff as u } from "lucide-react";
5
5
  import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
6
- //#region src/channel/components/AdapterCredentialsForm.tsx
6
+ // src/channel/components/AdapterCredentialsForm.tsx
7
7
  function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g, disabled: _ = !1, showPlatformMetadata: v = !0 }) {
8
8
  let y = e(), [b, x] = a({}), [S, C] = a({}), w = t(s);
9
9
  if (i(() => {
@@ -230,7 +230,6 @@ function h({ adapterType: t, credentials: n }) {
230
230
  })]
231
231
  });
232
232
  }
233
- //#endregion
234
233
  export { m as AdapterCredentialsForm, h as CredentialValidationStatus };
235
234
 
236
235
  //# sourceMappingURL=AdapterCredentialsForm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdapterCredentialsForm.js","names":[],"sources":["../../../src/channel/components/AdapterCredentialsForm.tsx"],"sourcesContent":["/**\n * Adapter Credentials Form\n *\n * Dynamic form that renders credential fields based on the selected adapter type.\n * Field names match exactly what the backend adapters expect.\n */\n\nimport { useState, useEffect } from 'react';\nimport { ExternalLink, Eye, EyeOff, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useTr } from '../hooks/useTr';\nimport type { AdapterType } from '../hooks/useChannels';\nimport {\n getAdapterCredentialSchema,\n getDefaultCredentials,\n validateCredentials,\n type CredentialField,\n} from '../config/adapterCredentials';\n\ninterface AdapterCredentialsFormProps {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n onChange: (credentials: Record<string, unknown>) => void;\n onValidationChange?: (isValid: boolean, errors: string[]) => void;\n disabled?: boolean;\n showPlatformMetadata?: boolean;\n}\n\nexport function AdapterCredentialsForm({\n adapterType,\n credentials,\n onChange,\n onValidationChange,\n disabled = false,\n showPlatformMetadata = true,\n}: AdapterCredentialsFormProps) {\n const tr = useTr();\n const [showPasswords, setShowPasswords] = useState<Record<string, boolean>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n const schema = getAdapterCredentialSchema(adapterType);\n\n // Initialize with default values when adapter type changes\n useEffect(() => {\n if (adapterType) {\n const defaults = getDefaultCredentials(adapterType);\n const merged = { ...defaults, ...credentials };\n if (JSON.stringify(merged) !== JSON.stringify(credentials)) {\n onChange(merged);\n }\n }\n }, [adapterType]);\n\n // Validate on change\n useEffect(() => {\n if (onValidationChange && adapterType) {\n const { valid, errors } = validateCredentials(adapterType, credentials);\n onValidationChange(valid, errors);\n }\n }, [credentials, adapterType, onValidationChange]);\n\n if (!adapterType) {\n return (\n <div className=\"text-center py-6 text-text-secondary\">\n <p className=\"text-sm\">\n {tr('channel.selectAdapterType', 'Select an adapter type to configure credentials')}\n </p>\n </div>\n );\n }\n\n if (!schema) {\n return (\n <div className=\"p-4 bg-status-warning-bg-subtle border border-warning-300 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-status-warning-text mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-warning-700\">\n {tr('channel.noCredentialSchema', 'No credential schema defined for')} {adapterType}\n </p>\n <p className=\"text-xs text-warning-600 mt-1\">\n {tr(\n 'channel.enterCredentialsAsJson',\n 'You can still enter credentials as JSON in the raw format below.'\n )}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n const handleFieldChange = (key: string, value: unknown) => {\n onChange({ ...credentials, [key]: value });\n };\n\n const handleFieldBlur = (key: string) => {\n setTouched((prev) => ({ ...prev, [key]: true }));\n };\n\n const togglePasswordVisibility = (key: string) => {\n setShowPasswords((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const renderField = (field: CredentialField) => {\n const value = credentials[field.key];\n const fieldError = touched[field.key] && field.required && !value;\n\n const commonInputClasses = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${\n fieldError ? 'border-status-error-border' : 'border-border-subtle'\n }`;\n\n const fieldId = `adapter-cred-${field.key}`;\n return (\n <div key={field.key}>\n <label htmlFor={fieldId} className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {field.displayName}\n {field.required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n\n {field.type === 'text' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'password' && (\n <div className=\"relative\">\n <input\n id={fieldId}\n aria-label={field.displayName}\n type={showPasswords[field.key] ? 'text' : 'password'}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={`${commonInputClasses} pr-10`}\n />\n <button\n type=\"button\"\n onClick={() => togglePasswordVisibility(field.key)}\n aria-label={\n showPasswords[field.key]\n ? tr('channel.hideField', 'Hide {{field}}', { field: field.displayName })\n : tr('channel.showField', 'Show {{field}}', { field: field.displayName })\n }\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-text-secondary hover:text-text-primary\"\n tabIndex={-1}\n >\n {showPasswords[field.key] ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n )}\n\n {field.type === 'textarea' && (\n <textarea\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n rows={4}\n className={`${commonInputClasses} resize-none font-mono text-xs`}\n />\n )}\n\n {field.type === 'number' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"number\"\n value={(value as number) ?? field.defaultValue ?? ''}\n onChange={(e) =>\n handleFieldChange(field.key, e.target.value ? Number(e.target.value) : undefined)\n }\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'boolean' && (\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={(value as boolean) ?? field.defaultValue ?? false}\n onChange={(e) => handleFieldChange(field.key, e.target.checked)}\n disabled={disabled}\n className=\"size-4 rounded border-border-subtle text-accent-primary focus:ring-accent-primary\"\n />\n <span className=\"text-sm text-text-secondary\">{field.description}</span>\n </label>\n )}\n\n {field.type === 'select' && field.options && (\n <select\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) ?? field.defaultValue ?? ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n className={`${commonInputClasses} bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary`}\n >\n <option value=\"\">\n {tr('channel.select', 'Select')} {field.displayName.toLowerCase()}\n </option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )}\n\n {field.type !== 'boolean' && (\n <p className=\"mt-1 text-xs text-text-secondary\">{field.description}</p>\n )}\n\n {fieldError && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {tr('channel.fieldRequired', 'This field is required')}\n </p>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"space-y-5\">\n {/* Header with adapter info */}\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{schema.displayName}</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">{schema.description}</p>\n </div>\n {schema.docUrl && (\n <a\n href={schema.docUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-accent-primary hover:text-accent-primary-hover\"\n >\n <span>{tr('channel.docs', 'Docs')}</span>\n <ExternalLink size={12} />\n </a>\n )}\n </div>\n\n {/* Credential fields */}\n <div className=\"space-y-4\">\n {schema.credentials.length > 0 && (\n <div className=\"space-y-4\">{schema.credentials.map(renderField)}</div>\n )}\n\n {/* Platform metadata fields (optional settings) */}\n {showPlatformMetadata && schema.platformMetadata && schema.platformMetadata.length > 0 && (\n <>\n <div className=\"border-t border-border-subtle pt-4 mt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-3\">\n {tr('channel.optionalSettings', 'Optional Settings')}\n </p>\n <div className=\"space-y-4\">{schema.platformMetadata.map(renderField)}</div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Compact validation status indicator\n */\nexport function CredentialValidationStatus({\n adapterType,\n credentials,\n}: {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n}) {\n const tr = useTr();\n\n if (!adapterType) return null;\n\n const { valid, errors } = validateCredentials(adapterType, credentials);\n\n if (valid) {\n return (\n <div className=\"flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 size={14} />\n <span className=\"text-xs\">\n {tr('channel.allRequiredFieldsFilled', 'All required fields filled')}\n </span>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center gap-1.5 text-status-warning-text\">\n <AlertCircle size={14} />\n <span className=\"text-xs\">\n {errors.length} {tr('channel.fieldsNeedAttention', 'field(s) need attention')}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdapterCredentialsForm.js","names":["_Fragment","AdapterCredentialsForm","tr","useTr","showPasswords","setShowPasswords","useState","touched","setTouched","schema","getAdapterCredentialSchema","adapterType","useEffect","merged","getDefaultCredentials","credentials","onChange","onValidationChange","validateCredentials","valid","errors","AlertCircle","handleFieldChange","key","value","handleFieldBlur","prev","togglePasswordVisibility","renderField","field","fieldError","commonInputClasses","fieldId","e","EyeOff","Eye","opt","ExternalLink","showPlatformMetadata","Fragment","CredentialValidationStatus","CheckCircle2"],"sources":["../../../src/channel/components/AdapterCredentialsForm.tsx"],"sourcesContent":["/**\n * Adapter Credentials Form\n *\n * Dynamic form that renders credential fields based on the selected adapter type.\n * Field names match exactly what the backend adapters expect.\n */\n\nimport { useState, useEffect } from 'react';\nimport { ExternalLink, Eye, EyeOff, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useTr } from '../hooks/useTr';\nimport type { AdapterType } from '../hooks/useChannels';\nimport {\n getAdapterCredentialSchema,\n getDefaultCredentials,\n validateCredentials,\n type CredentialField,\n} from '../config/adapterCredentials';\n\ninterface AdapterCredentialsFormProps {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n onChange: (credentials: Record<string, unknown>) => void;\n onValidationChange?: (isValid: boolean, errors: string[]) => void;\n disabled?: boolean;\n showPlatformMetadata?: boolean;\n}\n\nexport function AdapterCredentialsForm({\n adapterType,\n credentials,\n onChange,\n onValidationChange,\n disabled = false,\n showPlatformMetadata = true,\n}: AdapterCredentialsFormProps) {\n const tr = useTr();\n const [showPasswords, setShowPasswords] = useState<Record<string, boolean>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n const schema = getAdapterCredentialSchema(adapterType);\n\n // Initialize with default values when adapter type changes\n useEffect(() => {\n if (adapterType) {\n const defaults = getDefaultCredentials(adapterType);\n const merged = { ...defaults, ...credentials };\n if (JSON.stringify(merged) !== JSON.stringify(credentials)) {\n onChange(merged);\n }\n }\n }, [adapterType]);\n\n // Validate on change\n useEffect(() => {\n if (onValidationChange && adapterType) {\n const { valid, errors } = validateCredentials(adapterType, credentials);\n onValidationChange(valid, errors);\n }\n }, [credentials, adapterType, onValidationChange]);\n\n if (!adapterType) {\n return (\n <div className=\"text-center py-6 text-text-secondary\">\n <p className=\"text-sm\">\n {tr('channel.selectAdapterType', 'Select an adapter type to configure credentials')}\n </p>\n </div>\n );\n }\n\n if (!schema) {\n return (\n <div className=\"p-4 bg-status-warning-bg-subtle border border-warning-300 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-status-warning-text mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-warning-700\">\n {tr('channel.noCredentialSchema', 'No credential schema defined for')} {adapterType}\n </p>\n <p className=\"text-xs text-warning-600 mt-1\">\n {tr(\n 'channel.enterCredentialsAsJson',\n 'You can still enter credentials as JSON in the raw format below.'\n )}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n const handleFieldChange = (key: string, value: unknown) => {\n onChange({ ...credentials, [key]: value });\n };\n\n const handleFieldBlur = (key: string) => {\n setTouched((prev) => ({ ...prev, [key]: true }));\n };\n\n const togglePasswordVisibility = (key: string) => {\n setShowPasswords((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const renderField = (field: CredentialField) => {\n const value = credentials[field.key];\n const fieldError = touched[field.key] && field.required && !value;\n\n const commonInputClasses = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${\n fieldError ? 'border-status-error-border' : 'border-border-subtle'\n }`;\n\n const fieldId = `adapter-cred-${field.key}`;\n return (\n <div key={field.key}>\n <label htmlFor={fieldId} className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {field.displayName}\n {field.required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n\n {field.type === 'text' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'password' && (\n <div className=\"relative\">\n <input\n id={fieldId}\n aria-label={field.displayName}\n type={showPasswords[field.key] ? 'text' : 'password'}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={`${commonInputClasses} pr-10`}\n />\n <button\n type=\"button\"\n onClick={() => togglePasswordVisibility(field.key)}\n aria-label={\n showPasswords[field.key]\n ? tr('channel.hideField', 'Hide {{field}}', { field: field.displayName })\n : tr('channel.showField', 'Show {{field}}', { field: field.displayName })\n }\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-text-secondary hover:text-text-primary\"\n tabIndex={-1}\n >\n {showPasswords[field.key] ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n )}\n\n {field.type === 'textarea' && (\n <textarea\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n rows={4}\n className={`${commonInputClasses} resize-none font-mono text-xs`}\n />\n )}\n\n {field.type === 'number' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"number\"\n value={(value as number) ?? field.defaultValue ?? ''}\n onChange={(e) =>\n handleFieldChange(field.key, e.target.value ? Number(e.target.value) : undefined)\n }\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'boolean' && (\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={(value as boolean) ?? field.defaultValue ?? false}\n onChange={(e) => handleFieldChange(field.key, e.target.checked)}\n disabled={disabled}\n className=\"size-4 rounded border-border-subtle text-accent-primary focus:ring-accent-primary\"\n />\n <span className=\"text-sm text-text-secondary\">{field.description}</span>\n </label>\n )}\n\n {field.type === 'select' && field.options && (\n <select\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) ?? field.defaultValue ?? ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n className={`${commonInputClasses} bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary`}\n >\n <option value=\"\">\n {tr('channel.select', 'Select')} {field.displayName.toLowerCase()}\n </option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )}\n\n {field.type !== 'boolean' && (\n <p className=\"mt-1 text-xs text-text-secondary\">{field.description}</p>\n )}\n\n {fieldError && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {tr('channel.fieldRequired', 'This field is required')}\n </p>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"space-y-5\">\n {/* Header with adapter info */}\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{schema.displayName}</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">{schema.description}</p>\n </div>\n {schema.docUrl && (\n <a\n href={schema.docUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-accent-primary hover:text-accent-primary-hover\"\n >\n <span>{tr('channel.docs', 'Docs')}</span>\n <ExternalLink size={12} />\n </a>\n )}\n </div>\n\n {/* Credential fields */}\n <div className=\"space-y-4\">\n {schema.credentials.length > 0 && (\n <div className=\"space-y-4\">{schema.credentials.map(renderField)}</div>\n )}\n\n {/* Platform metadata fields (optional settings) */}\n {showPlatformMetadata && schema.platformMetadata && schema.platformMetadata.length > 0 && (\n <>\n <div className=\"border-t border-border-subtle pt-4 mt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-3\">\n {tr('channel.optionalSettings', 'Optional Settings')}\n </p>\n <div className=\"space-y-4\">{schema.platformMetadata.map(renderField)}</div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Compact validation status indicator\n */\nexport function CredentialValidationStatus({\n adapterType,\n credentials,\n}: {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n}) {\n const tr = useTr();\n\n if (!adapterType) return null;\n\n const { valid, errors } = validateCredentials(adapterType, credentials);\n\n if (valid) {\n return (\n <div className=\"flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 size={14} />\n <span className=\"text-xs\">\n {tr('channel.allRequiredFieldsFilled', 'All required fields filled')}\n </span>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center gap-1.5 text-status-warning-text\">\n <AlertCircle size={14} />\n <span className=\"text-xs\">\n {errors.length} {tr('channel.fieldsNeedAttention', 'field(s) need attention')}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -8,6 +8,5 @@ interface FeatureNotEnabledProps {
8
8
  * - The flag key is only shown to super admins (developer-facing detail).
9
9
  * - The CTA links to Feature Previews so admins can enable the flag directly.
10
10
  */
11
- export declare function FeatureNotEnabled({ flag }: FeatureNotEnabledProps): import("react").JSX.Element;
11
+ export declare function FeatureNotEnabled({ flag }: FeatureNotEnabledProps): import('react').JSX.Element;
12
12
  export {};
13
- //# sourceMappingURL=FeatureNotEnabled.d.ts.map
@@ -2,7 +2,7 @@ import { useTr as e } from "../hooks/useTr.js";
2
2
  import { Lock as t } from "lucide-react";
3
3
  import { usePermissions as n } from "@burdenoff/fe-libs/shared/providers/shell";
4
4
  import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
- //#region src/channel/components/FeatureNotEnabled.tsx
5
+ // src/channel/components/FeatureNotEnabled.tsx
6
6
  function a({ flag: a }) {
7
7
  let { hasRole: o } = n(), s = o("SUPER_ADMIN"), c = e();
8
8
  return /* @__PURE__ */ i("div", {
@@ -49,7 +49,6 @@ function a({ flag: a }) {
49
49
  })]
50
50
  });
51
51
  }
52
- //#endregion
53
52
  export { a as FeatureNotEnabled };
54
53
 
55
54
  //# sourceMappingURL=FeatureNotEnabled.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FeatureNotEnabled.js","names":[],"sources":["../../../src/channel/components/FeatureNotEnabled.tsx"],"sourcesContent":["import { Lock } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useTr } from '../hooks/useTr';\n\ninterface FeatureNotEnabledProps {\n flag: string;\n}\n\n/**\n * Default fallback rendered when a feature flag is disabled. Behaves as a\n * \"404-style\" panel — never throws, never crashes the route.\n *\n * - The flag key is only shown to super admins (developer-facing detail).\n * - The CTA links to Feature Previews so admins can enable the flag directly.\n */\nexport function FeatureNotEnabled({ flag }: FeatureNotEnabledProps) {\n const { hasRole } = usePermissions();\n const isSuperAdmin = hasRole('SUPER_ADMIN');\n const tr = useTr();\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-bg-elevated\">\n <Lock className=\"size-8 text-fg-muted\" />\n </div>\n <div className=\"space-y-2 max-w-md\">\n <h3 className=\"text-lg font-semibold text-fg\">\n {tr('channel.featureNotEnabled.title', 'Feature not enabled')}\n </h3>\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'channel.featureNotEnabled.description',\n 'This feature is currently disabled for your workspace.'\n )}\n </p>\n {isSuperAdmin ? (\n <p className=\"text-sm text-fg-muted\">\n {tr('channel.featureNotEnabled.adminPrefix', 'You can enable it in')}{' '}\n <a\n href=\"/feature-previews\"\n className=\"text-action-primary-text underline underline-offset-2 hover:text-action-primary-text-hover\"\n >\n {tr('channel.featureNotEnabled.featurePreviews', 'Feature Previews')}\n </a>\n .\n </p>\n ) : (\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'channel.featureNotEnabled.contactAdmin',\n 'Contact your administrator if you believe this is in error.'\n )}\n </p>\n )}\n {isSuperAdmin && (\n <p className=\"text-xs text-fg-subtle font-mono mt-1\">\n {tr('channel.featureNotEnabled.flagLabel', 'flag')}: {flag}\n </p>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAeA,SAAgB,EAAkB,EAAE,WAAgC;CAClE,IAAM,EAAE,eAAY,GAAgB,EAC9B,IAAe,EAAQ,cAAc,EACrC,IAAK,GAAO;AAElB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA;GACrC,CAAA,EACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,mCAAmC,sBAAsB;KAC1D,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,yCACA,yDACD;KACC,CAAA;IACH,IACC,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAG,yCAAyC,uBAAuB;MAAE;MACtE,kBAAC,KAAD;OACE,MAAK;OACL,WAAU;iBAET,EAAG,6CAA6C,mBAAmB;OAClE,CAAA;;MAEF;SAEJ,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,0CACA,8DACD;KACC,CAAA;IAEL,KACC,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAG,uCAAuC,OAAO;MAAC;MAAG;MACpD;;IAEF;KACF"}
1
+ {"version":3,"file":"FeatureNotEnabled.js","names":["FeatureNotEnabled","usePermissions","isSuperAdmin","hasRole","tr","useTr","Lock","flag"],"sources":["../../../src/channel/components/FeatureNotEnabled.tsx"],"sourcesContent":["import { Lock } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useTr } from '../hooks/useTr';\n\ninterface FeatureNotEnabledProps {\n flag: string;\n}\n\n/**\n * Default fallback rendered when a feature flag is disabled. Behaves as a\n * \"404-style\" panel — never throws, never crashes the route.\n *\n * - The flag key is only shown to super admins (developer-facing detail).\n * - The CTA links to Feature Previews so admins can enable the flag directly.\n */\nexport function FeatureNotEnabled({ flag }: FeatureNotEnabledProps) {\n const { hasRole } = usePermissions();\n const isSuperAdmin = hasRole('SUPER_ADMIN');\n const tr = useTr();\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-bg-elevated\">\n <Lock className=\"size-8 text-fg-muted\" />\n </div>\n <div className=\"space-y-2 max-w-md\">\n <h3 className=\"text-lg font-semibold text-fg\">\n {tr('channel.featureNotEnabled.title', 'Feature not enabled')}\n </h3>\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'channel.featureNotEnabled.description',\n 'This feature is currently disabled for your workspace.'\n )}\n </p>\n {isSuperAdmin ? (\n <p className=\"text-sm text-fg-muted\">\n {tr('channel.featureNotEnabled.adminPrefix', 'You can enable it in')}{' '}\n <a\n href=\"/feature-previews\"\n className=\"text-action-primary-text underline underline-offset-2 hover:text-action-primary-text-hover\"\n >\n {tr('channel.featureNotEnabled.featurePreviews', 'Feature Previews')}\n </a>\n .\n </p>\n ) : (\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'channel.featureNotEnabled.contactAdmin',\n 'Contact your administrator if you believe this is in error.'\n )}\n </p>\n )}\n {isSuperAdmin && (\n <p className=\"text-xs text-fg-subtle font-mono mt-1\">\n {tr('channel.featureNotEnabled.flagLabel', 'flag')}: {flag}\n </p>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAeA,SAAgBA,EAAkB,EAAE,WAAgC;CAClE,IAAM,EAAE,eAAYC,EAAe,GAC7BC,IAAeC,EAAQ,aAAa,GACpCC,IAAKC,EAAM;CAEjB,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;GACb,UAAA,kBAACC,GAAD,EAAM,WAAU,uBAAwB,CAAA;EACrC,CAAA,GACL,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;KACX,UAAAF,EAAG,mCAAmC,qBAAqB;IAC1D,CAAA;IACJ,kBAAC,KAAD;KAAG,WAAU;KACV,UAAAA,EACC,yCACA,wDACF;IACC,CAAA;IACFF,IACC,kBAAC,KAAD;KAAG,WAAU;KAAb,UAAA;MACGE,EAAG,yCAAyC,sBAAsB;MAAG;MACtE,kBAAC,KAAD;OACE,MAAK;OACL,WAAU;OAET,UAAAA,EAAG,6CAA6C,kBAAkB;MAClE,CAAA;MAAC;KAEH;IAEH,CAAA,IAAA,kBAAC,KAAD;KAAG,WAAU;KACV,UAAAA,EACC,0CACA,6DACF;IACC,CAAA;IAEJF,KACC,kBAAC,KAAD;KAAG,WAAU;KAAb,UAAA;MACGE,EAAG,uCAAuC,MAAM;MAAE;MAAGG;KACrD;;GAEF;EACF,CAAA,CAAA;;AAET"}
@@ -18,6 +18,5 @@ interface MarkdownEditorProps {
18
18
  /** Unique id, forwarded to the textarea. */
19
19
  id?: string;
20
20
  }
21
- export declare function MarkdownEditor({ value, onChange, label, placeholder, rows, 'aria-label': ariaLabel, className, textareaClassName, id, }: MarkdownEditorProps): import("react").JSX.Element;
21
+ export declare function MarkdownEditor({ value, onChange, label, placeholder, rows, 'aria-label': ariaLabel, className, textareaClassName, id, }: MarkdownEditorProps): import('react').JSX.Element;
22
22
  export {};
23
- //# sourceMappingURL=MarkdownEditor.d.ts.map
@@ -3,7 +3,7 @@ import { useRef as t, useState as n } from "react";
3
3
  import { Bold as r, Code as i, Italic as a, Link as o, List as s } from "lucide-react";
4
4
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
5
5
  import { Markdown as u } from "@burdenoff/fe-libs/shared/components/Markdown";
6
- //#region src/channel/components/MarkdownEditor.tsx
6
+ // src/channel/components/MarkdownEditor.tsx
7
7
  var d = (e, t, n) => (r, i, a) => {
8
8
  let o = r.slice(0, i), s = r.slice(i, a), c = r.slice(a);
9
9
  if (s.length === 0) {
@@ -66,9 +66,7 @@ function m({ value: m, onChange: h, label: g, placeholder: _, rows: v = 5, "aria
66
66
  case "code":
67
67
  D(d("`", "`", "code"));
68
68
  break;
69
- case "list":
70
- D(p);
71
- break;
69
+ case "list": D(p);
72
70
  }
73
71
  };
74
72
  return /* @__PURE__ */ l("div", {
@@ -167,7 +165,6 @@ function m({ value: m, onChange: h, label: g, placeholder: _, rows: v = 5, "aria
167
165
  })]
168
166
  });
169
167
  }
170
- //#endregion
171
168
  export { m as MarkdownEditor };
172
169
 
173
170
  //# sourceMappingURL=MarkdownEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../src/channel/components/MarkdownEditor.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components/Markdown';\nimport { useTr } from '../hooks/useTr';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface MarkdownEditorProps {\n /** Current markdown value. */\n value: string;\n /** Called whenever the markdown value changes. */\n onChange: (value: string) => void;\n /** Optional visible label. */\n label?: string;\n /** Placeholder for the write tab textarea. */\n placeholder?: string;\n /** Approximate visible rows for the textarea. */\n rows?: number;\n /** Accessible label for the textarea (used when no visible label). */\n 'aria-label'?: string;\n /** Additional classes for the outer container. */\n className?: string;\n /** Additional classes for the textarea. */\n textareaClassName?: string;\n /** Unique id, forwarded to the textarea. */\n id?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\ntype InsertAction = (\n value: string,\n selectionStart: number,\n selectionEnd: number\n) => { nextValue: string; cursorStart: number; cursorEnd: number };\n\nconst wrapWith = (prefix: string, suffix: string, fallback: string): InsertAction => {\n return (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = `${prefix}${fallback}${suffix}`;\n const cursorStart = start + prefix.length;\n const cursorEnd = cursorStart + fallback.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n }\n\n const wrapped = `${prefix}${selected}${suffix}`;\n const cursorStart = start + wrapped.length;\n return { nextValue: `${before}${wrapped}${after}`, cursorStart, cursorEnd: cursorStart };\n };\n};\n\nconst insertLink: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n const linkText = selected.length > 0 ? selected : 'text';\n const insertion = `[${linkText}](https://example.com)`;\n const cursorStart = start + insertion.length - 1;\n const cursorEnd = cursorStart + 1;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n};\n\nconst insertList: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = '- ';\n const cursorStart = start + insertion.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd: cursorStart };\n }\n\n const lines = selected.split('\\n');\n const listified = lines.map((line) => (line.startsWith('- ') ? line : `- ${line}`)).join('\\n');\n const nextValue = `${before}${listified}${after}`;\n const cursorStart = start;\n const cursorEnd = start + listified.length;\n return { nextValue, cursorStart, cursorEnd };\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function MarkdownEditor({\n value,\n onChange,\n label,\n placeholder,\n rows = 5,\n 'aria-label': ariaLabel,\n className = '',\n textareaClassName = '',\n id,\n}: MarkdownEditorProps) {\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const tr = useTr();\n\n const apply = (action: InsertAction) => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n const start = textarea.selectionStart ?? 0;\n const end = textarea.selectionEnd ?? 0;\n const { nextValue, cursorStart, cursorEnd } = action(value, start, end);\n\n onChange(nextValue);\n\n // Re-focus and restore selection after the next render has applied the value.\n requestAnimationFrame(() => {\n textarea.focus();\n textarea.setSelectionRange(cursorStart, cursorEnd);\n });\n };\n\n const handleToolbarAction = (action: 'bold' | 'italic' | 'link' | 'code' | 'list') => {\n switch (action) {\n case 'bold':\n apply(wrapWith('**', '**', 'bold text'));\n break;\n case 'italic':\n apply(wrapWith('*', '*', 'italic text'));\n break;\n case 'link':\n apply(insertLink);\n break;\n case 'code':\n apply(wrapWith('`', '`', 'code'));\n break;\n case 'list':\n apply(insertList);\n break;\n }\n };\n\n const containerBase =\n 'border border-border-subtle rounded-lg overflow-hidden bg-bg-surface focus-within:ring-2 focus-within:ring-primary';\n const textareaBase =\n 'w-full px-4 py-2 bg-bg-surface text-text-primary placeholder:text-text-secondary resize-none focus:outline-none';\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"text-sm font-medium text-text-primary\">\n {label}\n </label>\n )}\n <div className={`${containerBase} ${className}`}>\n <div className=\"flex items-center justify-between border-b border-border-subtle bg-bg-sunken px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('bold')}\n aria-label={tr('channel.markdown.bold', 'Bold')}\n title={tr('channel.markdown.bold', 'Bold')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Bold className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('italic')}\n aria-label={tr('channel.markdown.italic', 'Italic')}\n title={tr('channel.markdown.italic', 'Italic')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Italic className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('link')}\n aria-label={tr('channel.markdown.link', 'Link')}\n title={tr('channel.markdown.link', 'Link')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Link className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('code')}\n aria-label={tr('channel.markdown.code', 'Code')}\n title={tr('channel.markdown.code', 'Code')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Code className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('list')}\n aria-label={tr('channel.markdown.bulletList', 'Bullet list')}\n title={tr('channel.markdown.bulletList', 'Bullet list')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <List className=\"size-4\" />\n </button>\n </div>\n\n <div\n className=\"flex items-center gap-1\"\n role=\"tablist\"\n aria-label={tr('channel.markdown.tabsLabel', 'Markdown editor tabs')}\n >\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'write'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n {tr('channel.markdown.write', 'Write')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'preview'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n {tr('channel.markdown.preview', 'Preview')}\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n {activeTab === 'write' ? (\n <textarea\n ref={textareaRef}\n id={id}\n aria-label={ariaLabel}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n rows={rows}\n className={`${textareaBase} ${textareaClassName}`}\n />\n ) : (\n <div className=\"min-h-[theme(spacing.32)] max-h-96 overflow-auto px-4 py-2 text-sm text-text-primary\">\n {value.trim() ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">\n {placeholder ||\n tr('channel.markdown.nothingToPreview', 'Nothing to preview yet.')}\n </p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownEditor.js","names":["wrapWith","before","value","start","selected","end","after","insertion","prefix","fallback","suffix","cursorStart","cursorEnd","wrapped","insertLink","insertList","listified","line","MarkdownEditor","ariaLabel","activeTab","setActiveTab","useState","textareaRef","useRef","tr","useTr","apply","textarea","action","onChange","nextValue","handleToolbarAction","label","id","className","Bold","Italic","Link","Code","List","textareaClassName","Markdown","placeholder"],"sources":["../../../src/channel/components/MarkdownEditor.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components/Markdown';\nimport { useTr } from '../hooks/useTr';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface MarkdownEditorProps {\n /** Current markdown value. */\n value: string;\n /** Called whenever the markdown value changes. */\n onChange: (value: string) => void;\n /** Optional visible label. */\n label?: string;\n /** Placeholder for the write tab textarea. */\n placeholder?: string;\n /** Approximate visible rows for the textarea. */\n rows?: number;\n /** Accessible label for the textarea (used when no visible label). */\n 'aria-label'?: string;\n /** Additional classes for the outer container. */\n className?: string;\n /** Additional classes for the textarea. */\n textareaClassName?: string;\n /** Unique id, forwarded to the textarea. */\n id?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\ntype InsertAction = (\n value: string,\n selectionStart: number,\n selectionEnd: number\n) => { nextValue: string; cursorStart: number; cursorEnd: number };\n\nconst wrapWith = (prefix: string, suffix: string, fallback: string): InsertAction => {\n return (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = `${prefix}${fallback}${suffix}`;\n const cursorStart = start + prefix.length;\n const cursorEnd = cursorStart + fallback.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n }\n\n const wrapped = `${prefix}${selected}${suffix}`;\n const cursorStart = start + wrapped.length;\n return { nextValue: `${before}${wrapped}${after}`, cursorStart, cursorEnd: cursorStart };\n };\n};\n\nconst insertLink: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n const linkText = selected.length > 0 ? selected : 'text';\n const insertion = `[${linkText}](https://example.com)`;\n const cursorStart = start + insertion.length - 1;\n const cursorEnd = cursorStart + 1;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd };\n};\n\nconst insertList: InsertAction = (value, start, end) => {\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n if (selected.length === 0) {\n const insertion = '- ';\n const cursorStart = start + insertion.length;\n return { nextValue: `${before}${insertion}${after}`, cursorStart, cursorEnd: cursorStart };\n }\n\n const lines = selected.split('\\n');\n const listified = lines.map((line) => (line.startsWith('- ') ? line : `- ${line}`)).join('\\n');\n const nextValue = `${before}${listified}${after}`;\n const cursorStart = start;\n const cursorEnd = start + listified.length;\n return { nextValue, cursorStart, cursorEnd };\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function MarkdownEditor({\n value,\n onChange,\n label,\n placeholder,\n rows = 5,\n 'aria-label': ariaLabel,\n className = '',\n textareaClassName = '',\n id,\n}: MarkdownEditorProps) {\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const tr = useTr();\n\n const apply = (action: InsertAction) => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n const start = textarea.selectionStart ?? 0;\n const end = textarea.selectionEnd ?? 0;\n const { nextValue, cursorStart, cursorEnd } = action(value, start, end);\n\n onChange(nextValue);\n\n // Re-focus and restore selection after the next render has applied the value.\n requestAnimationFrame(() => {\n textarea.focus();\n textarea.setSelectionRange(cursorStart, cursorEnd);\n });\n };\n\n const handleToolbarAction = (action: 'bold' | 'italic' | 'link' | 'code' | 'list') => {\n switch (action) {\n case 'bold':\n apply(wrapWith('**', '**', 'bold text'));\n break;\n case 'italic':\n apply(wrapWith('*', '*', 'italic text'));\n break;\n case 'link':\n apply(insertLink);\n break;\n case 'code':\n apply(wrapWith('`', '`', 'code'));\n break;\n case 'list':\n apply(insertList);\n break;\n }\n };\n\n const containerBase =\n 'border border-border-subtle rounded-lg overflow-hidden bg-bg-surface focus-within:ring-2 focus-within:ring-primary';\n const textareaBase =\n 'w-full px-4 py-2 bg-bg-surface text-text-primary placeholder:text-text-secondary resize-none focus:outline-none';\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"text-sm font-medium text-text-primary\">\n {label}\n </label>\n )}\n <div className={`${containerBase} ${className}`}>\n <div className=\"flex items-center justify-between border-b border-border-subtle bg-bg-sunken px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('bold')}\n aria-label={tr('channel.markdown.bold', 'Bold')}\n title={tr('channel.markdown.bold', 'Bold')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Bold className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('italic')}\n aria-label={tr('channel.markdown.italic', 'Italic')}\n title={tr('channel.markdown.italic', 'Italic')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Italic className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('link')}\n aria-label={tr('channel.markdown.link', 'Link')}\n title={tr('channel.markdown.link', 'Link')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Link className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('code')}\n aria-label={tr('channel.markdown.code', 'Code')}\n title={tr('channel.markdown.code', 'Code')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <Code className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleToolbarAction('list')}\n aria-label={tr('channel.markdown.bulletList', 'Bullet list')}\n title={tr('channel.markdown.bulletList', 'Bullet list')}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-text-primary hover:bg-bg-surface transition-colors\"\n >\n <List className=\"size-4\" />\n </button>\n </div>\n\n <div\n className=\"flex items-center gap-1\"\n role=\"tablist\"\n aria-label={tr('channel.markdown.tabsLabel', 'Markdown editor tabs')}\n >\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'write'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n {tr('channel.markdown.write', 'Write')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${\n activeTab === 'preview'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-surface'\n }`}\n >\n {tr('channel.markdown.preview', 'Preview')}\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n {activeTab === 'write' ? (\n <textarea\n ref={textareaRef}\n id={id}\n aria-label={ariaLabel}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n rows={rows}\n className={`${textareaBase} ${textareaClassName}`}\n />\n ) : (\n <div className=\"min-h-[theme(spacing.32)] max-h-96 overflow-auto px-4 py-2 text-sm text-text-primary\">\n {value.trim() ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">\n {placeholder ||\n tr('channel.markdown.nothingToPreview', 'Nothing to preview yet.')}\n </p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  import { FC, ReactNode } from 'react';
2
2
  import { ChannelPermission } from '../constants/permissions';
3
-
4
3
  interface PermissionGuardProps {
5
4
  permission: ChannelPermission;
6
5
  children: ReactNode;
@@ -12,4 +11,3 @@ interface PermissionGuardProps {
12
11
  */
13
12
  export declare const PermissionGuard: FC<PermissionGuardProps>;
14
13
  export {};
15
- //# sourceMappingURL=PermissionGuard.d.ts.map
@@ -3,7 +3,7 @@ import { AccessDenied as t } from "@burdenoff/fe-libs/shared/components";
3
3
  import { Loader2 as n } from "lucide-react";
4
4
  import { usePermissions as r } from "@burdenoff/fe-libs/shared/providers/shell";
5
5
  import { Fragment as i, jsx as a } from "react/jsx-runtime";
6
- //#region src/channel/components/PermissionGuard.tsx
6
+ // src/channel/components/PermissionGuard.tsx
7
7
  var o = ({ permission: o, children: s }) => {
8
8
  let { hasPermission: c, isLoading: l } = r(), u = e();
9
9
  return l ? /* @__PURE__ */ a("div", {
@@ -14,7 +14,6 @@ var o = ({ permission: o, children: s }) => {
14
14
  description: u("channel.noPermissionView", "You don't have permission to view this page.")
15
15
  });
16
16
  };
17
- //#endregion
18
17
  export { o as PermissionGuard };
19
18
 
20
19
  //# sourceMappingURL=PermissionGuard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/channel/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from '../hooks/useTr';\nimport type { ChannelPermission } from '../constants/permissions';\n\ninterface PermissionGuardProps {\n permission: ChannelPermission;\n children: ReactNode;\n}\n\n/**\n * Wraps page content with a permission check.\n * Shows a loading spinner while permissions resolve, then either\n * renders children (if permitted) or an AccessDenied screen.\n */\nexport const PermissionGuard: FC<PermissionGuardProps> = ({ permission, children }) => {\n const { hasPermission, isLoading } = usePermissions();\n const tr = useTr();\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-bg-canvas\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n </div>\n );\n }\n\n if (!hasPermission(permission)) {\n return (\n <AccessDenied\n title={tr('channel.accessDenied', 'Access Denied')}\n description={tr('channel.noPermissionView', \"You don't have permission to view this page.\")}\n />\n );\n }\n\n return <>{children}</>;\n};\n"],"mappings":";;;;;;AAiBA,IAAa,KAA6C,EAAE,eAAY,kBAAe;CACrF,IAAM,EAAE,kBAAe,iBAAc,GAAgB,EAC/C,IAAK,GAAO;AAmBlB,QAjBI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,oCAAqC,CAAA;EACpD,CAAA,GAIL,EAAc,EAAW,GASvB,kBAAA,GAAA,EAAG,aAAY,CAAA,GAPlB,kBAAC,GAAD;EACE,OAAO,EAAG,wBAAwB,gBAAgB;EAClD,aAAa,EAAG,4BAA4B,+CAA+C;EAC3F,CAAA"}
1
+ {"version":3,"file":"PermissionGuard.js","names":["_Fragment","PermissionGuard","usePermissions","tr","useTr","isLoading","Loader2","hasPermission","permission","Fragment","AccessDenied"],"sources":["../../../src/channel/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from '../hooks/useTr';\nimport type { ChannelPermission } from '../constants/permissions';\n\ninterface PermissionGuardProps {\n permission: ChannelPermission;\n children: ReactNode;\n}\n\n/**\n * Wraps page content with a permission check.\n * Shows a loading spinner while permissions resolve, then either\n * renders children (if permitted) or an AccessDenied screen.\n */\nexport const PermissionGuard: FC<PermissionGuardProps> = ({ permission, children }) => {\n const { hasPermission, isLoading } = usePermissions();\n const tr = useTr();\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-bg-canvas\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n </div>\n );\n }\n\n if (!hasPermission(permission)) {\n return (\n <AccessDenied\n title={tr('channel.accessDenied', 'Access Denied')}\n description={tr('channel.noPermissionView', \"You don't have permission to view this page.\")}\n />\n );\n }\n\n return <>{children}</>;\n};\n"],"mappings":";;;;;;AAiBA,IAAaC,KAA6C,EAAE,eAAY,kBAAe;CACrF,IAAM,EAAE,kBAAe,iBAAcC,EAAe,GAC9CC,IAAKC,EAAM;CAmBjB,OAjBIC,IAEA,kBAAC,OAAD;EAAK,WAAU;EACb,UAAA,kBAACC,GAAD,EAAS,WAAU,mCAAoC,CAAA;CACpD,CAAA,IAIJC,EAAcC,CAAU,IAStB,kBAAAR,GAAA,EAAG,YAAW,CAAA,IAPjB,kBAACU,GAAD;EACE,OAAOP,EAAG,wBAAwB,eAAe;EACjD,aAAaA,EAAG,4BAA4B,8CAA8C;CAC3F,CAAA;AAKP"}
@@ -1,5 +1,4 @@
1
1
  import { AdapterType } from '../hooks/useChannels';
2
-
3
2
  export interface CredentialField {
4
3
  key: string;
5
4
  displayName: string;
@@ -41,4 +40,3 @@ export declare function validateCredentials(adapterType: AdapterType | undefined
41
40
  * Get default values for an adapter's credentials
42
41
  */
43
42
  export declare function getDefaultCredentials(adapterType: AdapterType | undefined): Record<string, unknown>;
44
- //# sourceMappingURL=adapterCredentials.d.ts.map
@@ -1,4 +1,4 @@
1
- //#region src/channel/config/adapterCredentials.ts
1
+ // src/channel/config/adapterCredentials.ts
2
2
  var e = {
3
3
  TWILIO_SMS: {
4
4
  displayName: "Twilio SMS",
@@ -927,11 +927,8 @@ function i(e, n) {
927
927
  let a = [];
928
928
  for (let e of i.credentials) {
929
929
  let t = n[e.key];
930
- if (e.required && (t == null || t === "")) {
931
- a.push(`${e.displayName} is required`);
932
- continue;
933
- }
934
- if (!(!e.required && (t == null || t === "")) && (e.validation?.pattern && typeof t == "string" && (r(e.validation.pattern).test(t) || a.push(`${e.displayName} has an invalid format`)), typeof t == "string")) {
930
+ if (e.required && (t == null || t === "")) a.push(`${e.displayName} is required`);
931
+ else if ((e.required || t != null && t !== "") && (e.validation?.pattern && typeof t == "string" && (r(e.validation.pattern).test(t) || a.push(`${e.displayName} has an invalid format`)), typeof t == "string")) {
935
932
  let n = e.validation?.minLength, r = e.validation?.maxLength;
936
933
  n && t.length < n && a.push(`${e.displayName} must be at least ${n} characters`), r && t.length > r && a.push(`${e.displayName} must be at most ${r} characters`);
937
934
  }
@@ -948,7 +945,6 @@ function a(e) {
948
945
  for (let e of [...n.credentials, ...n.platformMetadata || []]) e.defaultValue !== void 0 && (r[e.key] = e.defaultValue);
949
946
  return r;
950
947
  }
951
- //#endregion
952
948
  export { t as getAdapterCredentialSchema, a as getDefaultCredentials, i as validateCredentials };
953
949
 
954
950
  //# sourceMappingURL=adapterCredentials.js.map