@burdenoff/microfe-channel 2026.624.3 → 2026.625.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 (29) hide show
  1. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +108 -98
  2. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  3. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +287 -277
  4. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  5. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +140 -131
  6. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  7. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +170 -173
  8. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  9. package/dist/channel/modules/channels/pages/ChannelsListPage.js +127 -120
  10. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  11. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +161 -155
  12. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  13. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +70 -68
  14. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  15. package/dist/channel/modules/channels/pages/CreateChannelPage.js +27 -24
  16. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  17. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +161 -156
  18. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  19. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +179 -182
  20. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  21. package/dist/channel/modules/channels/pages/EditChannelPage.js +130 -122
  22. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  23. package/dist/channel/modules/channels/pages/EditConnectionPage.js +137 -132
  24. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  25. package/dist/channel/modules/channels/pages/SendMessagePage.js +87 -84
  26. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  27. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +193 -194
  28. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  29. package/package.json +2 -2
@@ -6,8 +6,9 @@ import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
6
6
  import { AlertCircle as s, ArrowLeft as c, Bell as l, CheckCircle2 as u, Clock as d, Database as f, Loader2 as p, Save as m, Settings as h, Shield as g } from "lucide-react";
7
7
  import { useNavigate as _ } from "react-router-dom";
8
8
  import { jsx as v, jsxs as y } from "react/jsx-runtime";
9
+ import { GlassCard as b, ShowMore as x } from "@burdenoff/fe-libs/ui";
9
10
  //#region src/channel/modules/channels/pages/ChannelSettingsPage.tsx
10
- var b = {
11
+ var S = {
11
12
  defaultPriority: 2,
12
13
  rateLimitPerMinute: 100,
13
14
  rateLimitPerHour: 5e3,
@@ -16,52 +17,52 @@ var b = {
16
17
  enableWebhookVerification: !0,
17
18
  sessionTimeoutMinutes: 30
18
19
  };
19
- function x() {
20
- let x = _(), { t: S } = o(), C = (e, t) => {
21
- let n = S(e);
20
+ function C() {
21
+ let C = _(), { t: w } = o(), T = (e, t) => {
22
+ let n = w(e);
22
23
  return n === e ? t : n;
23
- }, { settings: w, isLoading: T, error: E, saveSettings: D } = n(), [O, k] = a(b), [A, j] = a(!1), [M, N] = a(!1), [P, F] = a(null), I = i(null);
24
+ }, { settings: E, isLoading: D, error: O, saveSettings: k } = n(), [A, j] = a(S), [M, N] = a(!1), [P, F] = a(!1), [I, L] = a(null), R = i(null);
24
25
  r(() => {
25
- let e = I;
26
+ let e = R;
26
27
  return () => {
27
28
  e.current && clearTimeout(e.current);
28
29
  };
29
30
  }, []);
30
- let L = i(null);
31
+ let z = i(null);
31
32
  r(() => {
32
- if (!w) return;
33
- let e = `${w.organizationId}:${w.updatedAt}`;
34
- e !== L.current && (L.current = e, Promise.resolve().then(() => {
35
- k({
36
- defaultPriority: w.defaultPriority,
37
- rateLimitPerMinute: w.rateLimitPerMinute,
38
- rateLimitPerHour: w.rateLimitPerHour,
39
- healthCheckIntervalMinutes: w.healthCheckIntervalMinutes,
40
- enableAuditLogging: w.enableAuditLogging,
41
- enableWebhookVerification: w.enableWebhookVerification,
42
- sessionTimeoutMinutes: w.sessionTimeoutMinutes
33
+ if (!E) return;
34
+ let e = `${E.organizationId}:${E.updatedAt}`;
35
+ e !== z.current && (z.current = e, Promise.resolve().then(() => {
36
+ j({
37
+ defaultPriority: E.defaultPriority,
38
+ rateLimitPerMinute: E.rateLimitPerMinute,
39
+ rateLimitPerHour: E.rateLimitPerHour,
40
+ healthCheckIntervalMinutes: E.healthCheckIntervalMinutes,
41
+ enableAuditLogging: E.enableAuditLogging,
42
+ enableWebhookVerification: E.enableWebhookVerification,
43
+ sessionTimeoutMinutes: E.sessionTimeoutMinutes
43
44
  });
44
45
  }));
45
- }, [w]);
46
- let R = async () => {
47
- j(!0), F(null), N(!1);
46
+ }, [E]);
47
+ let B = async () => {
48
+ N(!0), L(null), F(!1);
48
49
  try {
49
- await D({
50
- defaultPriority: O.defaultPriority,
51
- rateLimitPerMinute: O.rateLimitPerMinute,
52
- rateLimitPerHour: O.rateLimitPerHour,
53
- healthCheckIntervalMinutes: O.healthCheckIntervalMinutes,
54
- enableAuditLogging: O.enableAuditLogging,
55
- enableWebhookVerification: O.enableWebhookVerification,
56
- sessionTimeoutMinutes: O.sessionTimeoutMinutes
57
- }) ? (N(!0), I.current && clearTimeout(I.current), I.current = setTimeout(() => N(!1), 3e3)) : F(C("channel.settingsSaveNotPersisted", "Settings could not be saved — organization context is unavailable."));
50
+ await k({
51
+ defaultPriority: A.defaultPriority,
52
+ rateLimitPerMinute: A.rateLimitPerMinute,
53
+ rateLimitPerHour: A.rateLimitPerHour,
54
+ healthCheckIntervalMinutes: A.healthCheckIntervalMinutes,
55
+ enableAuditLogging: A.enableAuditLogging,
56
+ enableWebhookVerification: A.enableWebhookVerification,
57
+ sessionTimeoutMinutes: A.sessionTimeoutMinutes
58
+ }) ? (F(!0), R.current && clearTimeout(R.current), R.current = setTimeout(() => F(!1), 3e3)) : L(T("channel.settingsSaveNotPersisted", "Settings could not be saved — organization context is unavailable."));
58
59
  } catch (e) {
59
- F(e instanceof Error ? e.message : "Failed to save settings");
60
+ L(e instanceof Error ? e.message : "Failed to save settings");
60
61
  } finally {
61
- j(!1);
62
+ N(!1);
62
63
  }
63
- }, z = (e, t) => {
64
- k((n) => ({
64
+ }, V = (e, t) => {
65
+ j((n) => ({
65
66
  ...n,
66
67
  [e]: t
67
68
  }));
@@ -80,7 +81,7 @@ function x() {
80
81
  className: "flex items-center gap-4",
81
82
  children: [/* @__PURE__ */ v("button", {
82
83
  type: "button",
83
- onClick: () => x("./list"),
84
+ onClick: () => C("./list"),
84
85
  className: "p-2 hover:bg-bg-sunken rounded-lg transition-colors",
85
86
  children: /* @__PURE__ */ v(c, { className: "size-5 " })
86
87
  }), /* @__PURE__ */ y("div", {
@@ -90,10 +91,10 @@ function x() {
90
91
  children: /* @__PURE__ */ v(h, { className: "size-5 text-primary" })
91
92
  }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
92
93
  className: "text-2xl font-bold",
93
- children: C("channel.channelSettings", "Channel Settings")
94
+ children: T("channel.channelSettings", "Channel Settings")
94
95
  }), /* @__PURE__ */ v("p", {
95
96
  className: "text-sm text-text-secondary",
96
- children: C("channel.channelSettingsSubtitle", "Configure global channel preferences")
97
+ children: T("channel.channelSettingsSubtitle", "Configure global channel preferences")
97
98
  })] })]
98
99
  })]
99
100
  })
@@ -102,52 +103,53 @@ function x() {
102
103
  }), /* @__PURE__ */ y("div", {
103
104
  className: "container mx-auto px-6 py-8 max-w-2xl space-y-8",
104
105
  children: [
105
- T && /* @__PURE__ */ y("div", {
106
+ D && /* @__PURE__ */ y("div", {
106
107
  className: "flex items-center gap-2 text-text-secondary",
107
108
  children: [/* @__PURE__ */ v(p, { className: "size-4 animate-spin" }), /* @__PURE__ */ v("span", {
108
109
  className: "text-sm",
109
110
  children: "Loading settings…"
110
111
  })]
111
112
  }),
112
- E && /* @__PURE__ */ y("div", {
113
+ O && /* @__PURE__ */ y("div", {
113
114
  className: "p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3",
114
115
  children: [/* @__PURE__ */ v(s, { className: "size-5 text-status-error-text shrink-0" }), /* @__PURE__ */ v("p", {
115
116
  className: "text-sm text-status-error-text",
116
- children: C("channel.settingsLoadError", "Failed to load settings — saving is disabled to prevent overwriting existing configuration.")
117
+ children: T("channel.settingsLoadError", "Failed to load settings — saving is disabled to prevent overwriting existing configuration.")
117
118
  })]
118
119
  }),
119
- M && /* @__PURE__ */ y("div", {
120
+ P && /* @__PURE__ */ y("div", {
120
121
  className: "p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3",
121
122
  children: [/* @__PURE__ */ v(u, { className: "size-5 text-status-success-text shrink-0" }), /* @__PURE__ */ v("p", {
122
123
  className: "text-sm text-status-success-text",
123
- children: C("channel.settingsSaved", "Settings saved successfully")
124
+ children: T("channel.settingsSaved", "Settings saved successfully")
124
125
  })]
125
126
  }),
126
- P && /* @__PURE__ */ y("div", {
127
+ I && /* @__PURE__ */ y("div", {
127
128
  className: "p-4 rounded-lg bg-status-error-bg/10 border border-status-error-border/20 flex items-center gap-3",
128
129
  children: [/* @__PURE__ */ v(s, { className: "size-5 text-status-error-text shrink-0" }), /* @__PURE__ */ v("p", {
129
130
  className: "text-sm text-status-error-text",
130
- children: P
131
+ children: I
131
132
  })]
132
133
  }),
133
- /* @__PURE__ */ y("div", {
134
- className: "border rounded-lg p-6 space-y-4",
134
+ /* @__PURE__ */ y(b, {
135
+ treatment: "plain",
136
+ className: "border-border-seam p-6 space-y-4",
135
137
  children: [/* @__PURE__ */ y("div", {
136
138
  className: "flex items-center gap-2",
137
139
  children: [/* @__PURE__ */ v(l, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
138
140
  className: "text-lg font-semibold",
139
- children: C("channel.deliverySettings", "Delivery Settings")
141
+ children: T("channel.deliverySettings", "Delivery Settings")
140
142
  })]
141
143
  }), /* @__PURE__ */ y("div", {
142
144
  className: "space-y-4",
143
145
  children: [/* @__PURE__ */ y("div", { children: [
144
146
  /* @__PURE__ */ v("label", {
145
147
  className: "text-sm font-medium block mb-1",
146
- children: C("channel.defaultPriority", "Default Priority")
148
+ children: T("channel.defaultPriority", "Default Priority")
147
149
  }),
148
150
  /* @__PURE__ */ y("select", {
149
- value: O.defaultPriority,
150
- onChange: (e) => z("defaultPriority", Number(e.target.value)),
151
+ value: A.defaultPriority,
152
+ onChange: (e) => V("defaultPriority", Number(e.target.value)),
151
153
  className: "w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
152
154
  children: [
153
155
  /* @__PURE__ */ v("option", {
@@ -181,26 +183,26 @@ function x() {
181
183
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
182
184
  htmlFor: "channel-settings-rate-limit-min",
183
185
  className: "text-sm font-medium block mb-1",
184
- children: C("channel.rateLimitPerMin", "Rate Limit (per minute)")
186
+ children: T("channel.rateLimitPerMin", "Rate Limit (per minute)")
185
187
  }), /* @__PURE__ */ v("input", {
186
188
  id: "channel-settings-rate-limit-min",
187
- "aria-label": C("channel.rateLimitPerMin", "Rate Limit (per minute)"),
189
+ "aria-label": T("channel.rateLimitPerMin", "Rate Limit (per minute)"),
188
190
  type: "number",
189
- value: O.rateLimitPerMinute,
190
- onChange: (e) => z("rateLimitPerMinute", Number(e.target.value)),
191
+ value: A.rateLimitPerMinute,
192
+ onChange: (e) => V("rateLimitPerMinute", Number(e.target.value)),
191
193
  min: 1,
192
194
  max: 1e3,
193
195
  className: "w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary"
194
196
  })] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
195
197
  htmlFor: "channel-settings-rate-limit-hour",
196
198
  className: "text-sm font-medium block mb-1",
197
- children: C("channel.rateLimitPerHour", "Rate Limit (per hour)")
199
+ children: T("channel.rateLimitPerHour", "Rate Limit (per hour)")
198
200
  }), /* @__PURE__ */ v("input", {
199
201
  id: "channel-settings-rate-limit-hour",
200
- "aria-label": C("channel.rateLimitPerHour", "Rate Limit (per hour)"),
202
+ "aria-label": T("channel.rateLimitPerHour", "Rate Limit (per hour)"),
201
203
  type: "number",
202
- value: O.rateLimitPerHour,
203
- onChange: (e) => z("rateLimitPerHour", Number(e.target.value)),
204
+ value: A.rateLimitPerHour,
205
+ onChange: (e) => V("rateLimitPerHour", Number(e.target.value)),
204
206
  min: 1,
205
207
  max: 5e4,
206
208
  className: "w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary"
@@ -208,13 +210,14 @@ function x() {
208
210
  })]
209
211
  })]
210
212
  }),
211
- /* @__PURE__ */ y("div", {
212
- className: "border rounded-lg p-6 space-y-4",
213
+ /* @__PURE__ */ y(b, {
214
+ treatment: "plain",
215
+ className: "border-border-seam p-6 space-y-4",
213
216
  children: [/* @__PURE__ */ y("div", {
214
217
  className: "flex items-center gap-2",
215
218
  children: [/* @__PURE__ */ v(d, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
216
219
  className: "text-lg font-semibold",
217
- children: C("channel.healthMonitoring", "Health Monitoring")
220
+ children: T("channel.healthMonitoring", "Health Monitoring")
218
221
  })]
219
222
  }), /* @__PURE__ */ y("div", { children: [
220
223
  /* @__PURE__ */ v("label", {
@@ -224,8 +227,8 @@ function x() {
224
227
  }),
225
228
  /* @__PURE__ */ y("select", {
226
229
  id: "channel-settings-health-interval",
227
- value: O.healthCheckIntervalMinutes,
228
- onChange: (e) => z("healthCheckIntervalMinutes", Number(e.target.value)),
230
+ value: A.healthCheckIntervalMinutes,
231
+ onChange: (e) => V("healthCheckIntervalMinutes", Number(e.target.value)),
229
232
  className: "w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
230
233
  children: [
231
234
  /* @__PURE__ */ v("option", {
@@ -260,13 +263,14 @@ function x() {
260
263
  })
261
264
  ] })]
262
265
  }),
263
- /* @__PURE__ */ y("div", {
264
- className: "border rounded-lg p-6 space-y-4",
266
+ /* @__PURE__ */ y(b, {
267
+ treatment: "plain",
268
+ className: "border-border-seam p-6 space-y-4",
265
269
  children: [/* @__PURE__ */ y("div", {
266
270
  className: "flex items-center gap-2",
267
271
  children: [/* @__PURE__ */ v(g, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
268
272
  className: "text-lg font-semibold",
269
- children: C("channel.security", "Security")
273
+ children: T("channel.security", "Security")
270
274
  })]
271
275
  }), /* @__PURE__ */ y("div", {
272
276
  className: "space-y-4",
@@ -275,15 +279,15 @@ function x() {
275
279
  className: "flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer",
276
280
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
277
281
  className: "font-medium",
278
- children: C("channel.webhookSignatureVerification", "Webhook Signature Verification")
282
+ children: T("channel.webhookSignatureVerification", "Webhook Signature Verification")
279
283
  }), /* @__PURE__ */ v("p", {
280
284
  className: "text-sm text-text-secondary",
281
285
  children: "Verify HMAC signatures on incoming webhooks"
282
286
  })] }), /* @__PURE__ */ v("input", {
283
287
  type: "checkbox",
284
- "aria-label": C("channel.webhookSignatureVerification", "Webhook Signature Verification"),
285
- checked: O.enableWebhookVerification,
286
- onChange: (e) => z("enableWebhookVerification", e.target.checked),
288
+ "aria-label": T("channel.webhookSignatureVerification", "Webhook Signature Verification"),
289
+ checked: A.enableWebhookVerification,
290
+ onChange: (e) => V("enableWebhookVerification", e.target.checked),
287
291
  className: "size-5 rounded border-gray-300 text-primary focus:ring-primary"
288
292
  })]
289
293
  }),
@@ -291,24 +295,24 @@ function x() {
291
295
  className: "flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer",
292
296
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
293
297
  className: "font-medium",
294
- children: C("channel.auditLogging", "Audit Logging")
298
+ children: T("channel.auditLogging", "Audit Logging")
295
299
  }), /* @__PURE__ */ v("p", {
296
300
  className: "text-sm text-text-secondary",
297
301
  children: "Log all channel operations for audit trail"
298
302
  })] }), /* @__PURE__ */ v("input", {
299
303
  type: "checkbox",
300
- "aria-label": C("channel.auditLogging", "Audit Logging"),
301
- checked: O.enableAuditLogging,
302
- onChange: (e) => z("enableAuditLogging", e.target.checked),
304
+ "aria-label": T("channel.auditLogging", "Audit Logging"),
305
+ checked: A.enableAuditLogging,
306
+ onChange: (e) => V("enableAuditLogging", e.target.checked),
303
307
  className: "size-5 rounded border-gray-300 text-primary focus:ring-primary"
304
308
  })]
305
309
  }),
306
310
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
307
311
  className: "text-sm font-medium block mb-1",
308
- children: C("channel.sessionTimeout", "Session Timeout (minutes)")
312
+ children: T("channel.sessionTimeout", "Session Timeout (minutes)")
309
313
  }), /* @__PURE__ */ y("select", {
310
- value: O.sessionTimeoutMinutes,
311
- onChange: (e) => z("sessionTimeoutMinutes", Number(e.target.value)),
314
+ value: A.sessionTimeoutMinutes,
315
+ onChange: (e) => V("sessionTimeoutMinutes", Number(e.target.value)),
312
316
  className: "w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary",
313
317
  children: [
314
318
  /* @__PURE__ */ v("option", {
@@ -336,76 +340,81 @@ function x() {
336
340
  ]
337
341
  })]
338
342
  }),
339
- /* @__PURE__ */ y("div", {
340
- className: "border rounded-lg p-6 space-y-4",
343
+ /* @__PURE__ */ y(b, {
344
+ treatment: "plain",
345
+ className: "border-border-seam p-6 space-y-4",
341
346
  children: [/* @__PURE__ */ y("div", {
342
347
  className: "flex items-center gap-2",
343
348
  children: [/* @__PURE__ */ v(f, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
344
349
  className: "text-lg font-semibold",
345
- children: C("channel.dataManagement", "Data Management")
350
+ children: T("channel.dataManagement", "Data Management")
346
351
  })]
347
- }), /* @__PURE__ */ y("div", {
348
- className: "space-y-4",
349
- children: [
350
- /* @__PURE__ */ y("div", {
351
- className: "p-4 rounded-lg border",
352
- children: [/* @__PURE__ */ y("div", {
353
- className: "flex items-center justify-between mb-2",
354
- children: [/* @__PURE__ */ v("p", {
355
- className: "font-medium",
356
- children: C("channel.webhookEventRetention", "Webhook Event Retention")
357
- }), /* @__PURE__ */ v("span", {
352
+ }), /* @__PURE__ */ v(x, {
353
+ collapsedHeight: 96,
354
+ label: T("channel.showRetentionPolicies", "Show retention policies"),
355
+ children: /* @__PURE__ */ y("div", {
356
+ className: "space-y-4",
357
+ children: [
358
+ /* @__PURE__ */ y("div", {
359
+ className: "p-4 rounded-lg border",
360
+ children: [/* @__PURE__ */ y("div", {
361
+ className: "flex items-center justify-between mb-2",
362
+ children: [/* @__PURE__ */ v("p", {
363
+ className: "font-medium",
364
+ children: T("channel.webhookEventRetention", "Webhook Event Retention")
365
+ }), /* @__PURE__ */ v("span", {
366
+ className: "text-sm text-text-secondary",
367
+ children: "7 days"
368
+ })]
369
+ }), /* @__PURE__ */ v("p", {
358
370
  className: "text-sm text-text-secondary",
359
- children: "7 days"
371
+ children: "Incoming webhook events are automatically deleted after 7 days"
360
372
  })]
361
- }), /* @__PURE__ */ v("p", {
362
- className: "text-sm text-text-secondary",
363
- children: "Incoming webhook events are automatically deleted after 7 days"
364
- })]
365
- }),
366
- /* @__PURE__ */ y("div", {
367
- className: "p-4 rounded-lg border",
368
- children: [/* @__PURE__ */ y("div", {
369
- className: "flex items-center justify-between mb-2",
370
- children: [/* @__PURE__ */ v("p", {
371
- className: "font-medium",
372
- children: C("channel.deliveryAttemptRetention", "Delivery Attempt Retention")
373
- }), /* @__PURE__ */ v("span", {
373
+ }),
374
+ /* @__PURE__ */ y("div", {
375
+ className: "p-4 rounded-lg border",
376
+ children: [/* @__PURE__ */ y("div", {
377
+ className: "flex items-center justify-between mb-2",
378
+ children: [/* @__PURE__ */ v("p", {
379
+ className: "font-medium",
380
+ children: T("channel.deliveryAttemptRetention", "Delivery Attempt Retention")
381
+ }), /* @__PURE__ */ v("span", {
382
+ className: "text-sm text-text-secondary",
383
+ children: "7 days"
384
+ })]
385
+ }), /* @__PURE__ */ v("p", {
374
386
  className: "text-sm text-text-secondary",
375
- children: "7 days"
387
+ children: "Successful delivery attempts are automatically deleted after 7 days"
376
388
  })]
377
- }), /* @__PURE__ */ v("p", {
378
- className: "text-sm text-text-secondary",
379
- children: "Successful delivery attempts are automatically deleted after 7 days"
380
- })]
381
- }),
382
- /* @__PURE__ */ y("div", {
383
- className: "p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle",
384
- children: [/* @__PURE__ */ y("div", {
385
- className: "flex items-center justify-between mb-2",
386
- children: [/* @__PURE__ */ v("p", {
387
- className: "font-medium",
388
- children: C("channel.failedDeliveryRetention", "Failed Delivery Retention")
389
- }), /* @__PURE__ */ v("span", {
390
- className: "text-sm text-status-warning-text",
391
- children: "30 days"
389
+ }),
390
+ /* @__PURE__ */ y("div", {
391
+ className: "p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle",
392
+ children: [/* @__PURE__ */ y("div", {
393
+ className: "flex items-center justify-between mb-2",
394
+ children: [/* @__PURE__ */ v("p", {
395
+ className: "font-medium",
396
+ children: T("channel.failedDeliveryRetention", "Failed Delivery Retention")
397
+ }), /* @__PURE__ */ v("span", {
398
+ className: "text-sm text-status-warning-text",
399
+ children: "30 days"
400
+ })]
401
+ }), /* @__PURE__ */ v("p", {
402
+ className: "text-sm text-text-secondary",
403
+ children: "Failed delivery attempts are kept for 30 days for debugging"
392
404
  })]
393
- }), /* @__PURE__ */ v("p", {
394
- className: "text-sm text-text-secondary",
395
- children: "Failed delivery attempts are kept for 30 days for debugging"
396
- })]
397
- })
398
- ]
405
+ })
406
+ ]
407
+ })
399
408
  })]
400
409
  }),
401
410
  /* @__PURE__ */ v("div", {
402
411
  className: "flex justify-end pt-4",
403
412
  children: /* @__PURE__ */ y("button", {
404
413
  type: "button",
405
- onClick: R,
406
- disabled: A || T || !!E,
414
+ onClick: B,
415
+ disabled: M || D || !!O,
407
416
  className: "px-6 py-3 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50 flex items-center gap-2",
408
- children: [A ? /* @__PURE__ */ v(p, { className: "size-5 animate-spin" }) : /* @__PURE__ */ v(m, { className: "size-5 " }), C("channel.saveSettings", "Save Settings")]
417
+ children: [M ? /* @__PURE__ */ v(p, { className: "size-5 animate-spin" }) : /* @__PURE__ */ v(m, { className: "size-5 " }), T("channel.saveSettings", "Save Settings")]
409
418
  })
410
419
  })
411
420
  ]
@@ -414,6 +423,6 @@ function x() {
414
423
  });
415
424
  }
416
425
  //#endregion
417
- export { x as ChannelSettingsPage };
426
+ export { C as ChannelSettingsPage };
418
427
 
419
428
  //# sourceMappingURL=ChannelSettingsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelSettingsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelSettingsPage.tsx"],"sourcesContent":["/**\n * Channel Settings Page\n *\n * Global settings for the channels module.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Settings,\n Bell,\n Shield,\n Clock,\n Database,\n Save,\n Loader2,\n AlertCircle,\n CheckCircle2,\n} from 'lucide-react';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useChannelOrgSettings } from '../../../hooks/useChannelOrgSettings';\nimport type { ChannelOrgSettingsInput } from '../../../hooks/useChannelOrgSettings';\n\ninterface ChannelSettings {\n defaultPriority: number;\n rateLimitPerMinute: number;\n rateLimitPerHour: number;\n healthCheckIntervalMinutes: number;\n enableAuditLogging: boolean;\n enableWebhookVerification: boolean;\n sessionTimeoutMinutes: number;\n}\n\nconst DEFAULT_SETTINGS: ChannelSettings = {\n defaultPriority: 2,\n rateLimitPerMinute: 100,\n rateLimitPerHour: 5000,\n healthCheckIntervalMinutes: 5,\n enableAuditLogging: true,\n enableWebhookVerification: true,\n sessionTimeoutMinutes: 30,\n};\n\nexport function ChannelSettingsPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const {\n settings: remoteSettings,\n isLoading: isLoadingSettings,\n error: settingsLoadError,\n saveSettings,\n } = useChannelOrgSettings();\n\n const [settings, setSettings] = useState<ChannelSettings>(DEFAULT_SETTINGS);\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const saveSuccessTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Populate / re-sync form from remote settings whenever they change.\n // This handles both the initial load and server-side normalisations returned\n // by saveSettings (e.g. clamped values), so the form always reflects the\n // true persisted state rather than potentially stale local values.\n // Defer setState to a microtask to avoid react-hooks/exhaustive-deps warnings.\n useEffect(() => {\n const timerRef = saveSuccessTimerRef;\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, []);\n\n const loadedOrgIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (!remoteSettings) return;\n // Re-hydrate whenever the org changes *or* the updatedAt timestamp changes,\n // so server-side coercion after a save is reflected immediately.\n const changeKey = `${remoteSettings.organizationId}:${remoteSettings.updatedAt}`;\n if (changeKey === loadedOrgIdRef.current) return;\n loadedOrgIdRef.current = changeKey;\n void Promise.resolve().then(() => {\n setSettings({\n defaultPriority: remoteSettings.defaultPriority,\n rateLimitPerMinute: remoteSettings.rateLimitPerMinute,\n rateLimitPerHour: remoteSettings.rateLimitPerHour,\n healthCheckIntervalMinutes: remoteSettings.healthCheckIntervalMinutes,\n enableAuditLogging: remoteSettings.enableAuditLogging,\n enableWebhookVerification: remoteSettings.enableWebhookVerification,\n sessionTimeoutMinutes: remoteSettings.sessionTimeoutMinutes,\n });\n });\n }, [remoteSettings]);\n\n const handleSave = async () => {\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const input: ChannelOrgSettingsInput = {\n defaultPriority: settings.defaultPriority,\n rateLimitPerMinute: settings.rateLimitPerMinute,\n rateLimitPerHour: settings.rateLimitPerHour,\n healthCheckIntervalMinutes: settings.healthCheckIntervalMinutes,\n enableAuditLogging: settings.enableAuditLogging,\n enableWebhookVerification: settings.enableWebhookVerification,\n sessionTimeoutMinutes: settings.sessionTimeoutMinutes,\n };\n // saveSettings returns null when organizationId is absent (context not\n // ready); only show success when something was actually persisted (P2).\n const saved = await saveSettings(input);\n if (saved) {\n setSaveSuccess(true);\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 3000);\n } else {\n setSaveError(\n tr(\n 'channel.settingsSaveNotPersisted',\n 'Settings could not be saved — organization context is unavailable.'\n )\n );\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'Failed to save settings';\n setSaveError(message);\n } finally {\n setIsSaving(false);\n }\n };\n\n const updateSetting = <K extends keyof ChannelSettings>(key: K, value: ChannelSettings[K]) => {\n setSettings((prev) => ({ ...prev, [key]: value }));\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-lg\">\n <Settings className=\"size-5 text-primary\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold\">\n {tr('channel.channelSettings', 'Channel Settings')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.channelSettingsSubtitle',\n 'Configure global channel preferences'\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-6 py-8 max-w-2xl space-y-8\">\n {/* Loading state */}\n {isLoadingSettings && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"text-sm\">Loading settings…</span>\n </div>\n )}\n\n {/* Settings load error */}\n {settingsLoadError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr(\n 'channel.settingsLoadError',\n 'Failed to load settings — saving is disabled to prevent overwriting existing configuration.'\n )}\n </p>\n </div>\n )}\n\n {/* Alerts */}\n {saveSuccess && (\n <div className=\"p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3\">\n <CheckCircle2 className=\"size-5 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">\n {tr('channel.settingsSaved', 'Settings saved successfully')}\n </p>\n </div>\n )}\n\n {saveError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg/10 border border-status-error-border/20 flex items-center gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{saveError}</p>\n </div>\n )}\n\n {/* Delivery Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Bell className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.deliverySettings', 'Delivery Settings')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.defaultPriority', 'Default Priority')}\n </label>\n <select\n value={settings.defaultPriority}\n onChange={(e) => updateSetting('defaultPriority', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={0}>0 - Lowest</option>\n <option value={1}>1 - Low</option>\n <option value={2}>2 - Normal</option>\n <option value={3}>3 - High</option>\n <option value={4}>4 - Highest</option>\n </select>\n <p className=\"text-xs text-text-secondary mt-1\">\n Priority used when creating new channels without explicit priority\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"channel-settings-rate-limit-min\"\n className=\"text-sm font-medium block mb-1\"\n >\n {tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n </label>\n <input\n id=\"channel-settings-rate-limit-min\"\n aria-label={tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n type=\"number\"\n value={settings.rateLimitPerMinute}\n onChange={(e) => updateSetting('rateLimitPerMinute', Number(e.target.value))}\n min={1}\n max={1000}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"channel-settings-rate-limit-hour\"\n className=\"text-sm font-medium block mb-1\"\n >\n {tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n </label>\n <input\n id=\"channel-settings-rate-limit-hour\"\n aria-label={tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n type=\"number\"\n value={settings.rateLimitPerHour}\n onChange={(e) => updateSetting('rateLimitPerHour', Number(e.target.value))}\n min={1}\n max={50000}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n </div>\n </div>\n\n {/* Health Check Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.healthMonitoring', 'Health Monitoring')}\n </h2>\n </div>\n\n <div>\n <label\n htmlFor=\"channel-settings-health-interval\"\n className=\"text-sm font-medium block mb-1\"\n >\n Health Check Interval (minutes)\n </label>\n <select\n id=\"channel-settings-health-interval\"\n value={settings.healthCheckIntervalMinutes}\n onChange={(e) =>\n updateSetting('healthCheckIntervalMinutes', Number(e.target.value))\n }\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={1}>1 minute</option>\n <option value={5}>5 minutes</option>\n <option value={10}>10 minutes</option>\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n </select>\n <p className=\"text-xs text-text-secondary mt-1\">\n How often to check connection health status\n </p>\n </div>\n </div>\n\n {/* Security Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Shield className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">{tr('channel.security', 'Security')}</h2>\n </div>\n\n <div className=\"space-y-4\">\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">\n {tr('channel.webhookSignatureVerification', 'Webhook Signature Verification')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n Verify HMAC signatures on incoming webhooks\n </p>\n </div>\n <input\n type=\"checkbox\"\n aria-label={tr(\n 'channel.webhookSignatureVerification',\n 'Webhook Signature Verification'\n )}\n checked={settings.enableWebhookVerification}\n onChange={(e) => updateSetting('enableWebhookVerification', e.target.checked)}\n className=\"size-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">{tr('channel.auditLogging', 'Audit Logging')}</p>\n <p className=\"text-sm text-text-secondary\">\n Log all channel operations for audit trail\n </p>\n </div>\n <input\n type=\"checkbox\"\n aria-label={tr('channel.auditLogging', 'Audit Logging')}\n checked={settings.enableAuditLogging}\n onChange={(e) => updateSetting('enableAuditLogging', e.target.checked)}\n className=\"size-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.sessionTimeout', 'Session Timeout (minutes)')}\n </label>\n <select\n value={settings.sessionTimeoutMinutes}\n onChange={(e) => updateSetting('sessionTimeoutMinutes', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n <option value={120}>2 hours</option>\n <option value={240}>4 hours</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Data Management */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.dataManagement', 'Data Management')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.webhookEventRetention', 'Webhook Event Retention')}\n </p>\n <span className=\"text-sm text-text-secondary\">7 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Incoming webhook events are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.deliveryAttemptRetention', 'Delivery Attempt Retention')}\n </p>\n <span className=\"text-sm text-text-secondary\">7 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Successful delivery attempts are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.failedDeliveryRetention', 'Failed Delivery Retention')}\n </p>\n <span className=\"text-sm text-status-warning-text\">30 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Failed delivery attempts are kept for 30 days for debugging\n </p>\n </div>\n </div>\n </div>\n\n {/* Save Button */}\n <div className=\"flex justify-end pt-4\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={isSaving || isLoadingSettings || !!settingsLoadError}\n className=\"px-6 py-3 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50 flex items-center gap-2\"\n >\n {isSaving ? (\n <Loader2 className=\"size-5 animate-spin\" />\n ) : (\n <Save className=\"size-5 \" />\n )}\n {tr('channel.saveSettings', 'Save Settings')}\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChannelSettingsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelSettingsPage.tsx"],"sourcesContent":["/**\n * Channel Settings Page\n *\n * Global settings for the channels module.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Settings,\n Bell,\n Shield,\n Clock,\n Database,\n Save,\n Loader2,\n AlertCircle,\n CheckCircle2,\n} from 'lucide-react';\nimport { GlassCard, ShowMore } from '@burdenoff/fe-libs/ui';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useChannelOrgSettings } from '../../../hooks/useChannelOrgSettings';\nimport type { ChannelOrgSettingsInput } from '../../../hooks/useChannelOrgSettings';\n\ninterface ChannelSettings {\n defaultPriority: number;\n rateLimitPerMinute: number;\n rateLimitPerHour: number;\n healthCheckIntervalMinutes: number;\n enableAuditLogging: boolean;\n enableWebhookVerification: boolean;\n sessionTimeoutMinutes: number;\n}\n\nconst DEFAULT_SETTINGS: ChannelSettings = {\n defaultPriority: 2,\n rateLimitPerMinute: 100,\n rateLimitPerHour: 5000,\n healthCheckIntervalMinutes: 5,\n enableAuditLogging: true,\n enableWebhookVerification: true,\n sessionTimeoutMinutes: 30,\n};\n\nexport function ChannelSettingsPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const {\n settings: remoteSettings,\n isLoading: isLoadingSettings,\n error: settingsLoadError,\n saveSettings,\n } = useChannelOrgSettings();\n\n const [settings, setSettings] = useState<ChannelSettings>(DEFAULT_SETTINGS);\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const saveSuccessTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Populate / re-sync form from remote settings whenever they change.\n // This handles both the initial load and server-side normalisations returned\n // by saveSettings (e.g. clamped values), so the form always reflects the\n // true persisted state rather than potentially stale local values.\n // Defer setState to a microtask to avoid react-hooks/exhaustive-deps warnings.\n useEffect(() => {\n const timerRef = saveSuccessTimerRef;\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, []);\n\n const loadedOrgIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (!remoteSettings) return;\n // Re-hydrate whenever the org changes *or* the updatedAt timestamp changes,\n // so server-side coercion after a save is reflected immediately.\n const changeKey = `${remoteSettings.organizationId}:${remoteSettings.updatedAt}`;\n if (changeKey === loadedOrgIdRef.current) return;\n loadedOrgIdRef.current = changeKey;\n void Promise.resolve().then(() => {\n setSettings({\n defaultPriority: remoteSettings.defaultPriority,\n rateLimitPerMinute: remoteSettings.rateLimitPerMinute,\n rateLimitPerHour: remoteSettings.rateLimitPerHour,\n healthCheckIntervalMinutes: remoteSettings.healthCheckIntervalMinutes,\n enableAuditLogging: remoteSettings.enableAuditLogging,\n enableWebhookVerification: remoteSettings.enableWebhookVerification,\n sessionTimeoutMinutes: remoteSettings.sessionTimeoutMinutes,\n });\n });\n }, [remoteSettings]);\n\n const handleSave = async () => {\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const input: ChannelOrgSettingsInput = {\n defaultPriority: settings.defaultPriority,\n rateLimitPerMinute: settings.rateLimitPerMinute,\n rateLimitPerHour: settings.rateLimitPerHour,\n healthCheckIntervalMinutes: settings.healthCheckIntervalMinutes,\n enableAuditLogging: settings.enableAuditLogging,\n enableWebhookVerification: settings.enableWebhookVerification,\n sessionTimeoutMinutes: settings.sessionTimeoutMinutes,\n };\n // saveSettings returns null when organizationId is absent (context not\n // ready); only show success when something was actually persisted (P2).\n const saved = await saveSettings(input);\n if (saved) {\n setSaveSuccess(true);\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 3000);\n } else {\n setSaveError(\n tr(\n 'channel.settingsSaveNotPersisted',\n 'Settings could not be saved — organization context is unavailable.'\n )\n );\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'Failed to save settings';\n setSaveError(message);\n } finally {\n setIsSaving(false);\n }\n };\n\n const updateSetting = <K extends keyof ChannelSettings>(key: K, value: ChannelSettings[K]) => {\n setSettings((prev) => ({ ...prev, [key]: value }));\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-lg\">\n <Settings className=\"size-5 text-primary\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold\">\n {tr('channel.channelSettings', 'Channel Settings')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.channelSettingsSubtitle',\n 'Configure global channel preferences'\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-6 py-8 max-w-2xl space-y-8\">\n {/* Loading state */}\n {isLoadingSettings && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"text-sm\">Loading settings…</span>\n </div>\n )}\n\n {/* Settings load error */}\n {settingsLoadError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr(\n 'channel.settingsLoadError',\n 'Failed to load settings — saving is disabled to prevent overwriting existing configuration.'\n )}\n </p>\n </div>\n )}\n\n {/* Alerts */}\n {saveSuccess && (\n <div className=\"p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3\">\n <CheckCircle2 className=\"size-5 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">\n {tr('channel.settingsSaved', 'Settings saved successfully')}\n </p>\n </div>\n )}\n\n {saveError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg/10 border border-status-error-border/20 flex items-center gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{saveError}</p>\n </div>\n )}\n\n {/* Delivery Settings */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Bell className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.deliverySettings', 'Delivery Settings')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.defaultPriority', 'Default Priority')}\n </label>\n <select\n value={settings.defaultPriority}\n onChange={(e) => updateSetting('defaultPriority', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={0}>0 - Lowest</option>\n <option value={1}>1 - Low</option>\n <option value={2}>2 - Normal</option>\n <option value={3}>3 - High</option>\n <option value={4}>4 - Highest</option>\n </select>\n <p className=\"text-xs text-text-secondary mt-1\">\n Priority used when creating new channels without explicit priority\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"channel-settings-rate-limit-min\"\n className=\"text-sm font-medium block mb-1\"\n >\n {tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n </label>\n <input\n id=\"channel-settings-rate-limit-min\"\n aria-label={tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n type=\"number\"\n value={settings.rateLimitPerMinute}\n onChange={(e) => updateSetting('rateLimitPerMinute', Number(e.target.value))}\n min={1}\n max={1000}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n <div>\n <label\n htmlFor=\"channel-settings-rate-limit-hour\"\n className=\"text-sm font-medium block mb-1\"\n >\n {tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n </label>\n <input\n id=\"channel-settings-rate-limit-hour\"\n aria-label={tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n type=\"number\"\n value={settings.rateLimitPerHour}\n onChange={(e) => updateSetting('rateLimitPerHour', Number(e.target.value))}\n min={1}\n max={50000}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Health Check Settings */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.healthMonitoring', 'Health Monitoring')}\n </h2>\n </div>\n\n <div>\n <label\n htmlFor=\"channel-settings-health-interval\"\n className=\"text-sm font-medium block mb-1\"\n >\n Health Check Interval (minutes)\n </label>\n <select\n id=\"channel-settings-health-interval\"\n value={settings.healthCheckIntervalMinutes}\n onChange={(e) =>\n updateSetting('healthCheckIntervalMinutes', Number(e.target.value))\n }\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={1}>1 minute</option>\n <option value={5}>5 minutes</option>\n <option value={10}>10 minutes</option>\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n </select>\n <p className=\"text-xs text-text-secondary mt-1\">\n How often to check connection health status\n </p>\n </div>\n </GlassCard>\n\n {/* Security Settings */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Shield className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">{tr('channel.security', 'Security')}</h2>\n </div>\n\n <div className=\"space-y-4\">\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">\n {tr('channel.webhookSignatureVerification', 'Webhook Signature Verification')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n Verify HMAC signatures on incoming webhooks\n </p>\n </div>\n <input\n type=\"checkbox\"\n aria-label={tr(\n 'channel.webhookSignatureVerification',\n 'Webhook Signature Verification'\n )}\n checked={settings.enableWebhookVerification}\n onChange={(e) => updateSetting('enableWebhookVerification', e.target.checked)}\n className=\"size-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-bg-sunken/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">{tr('channel.auditLogging', 'Audit Logging')}</p>\n <p className=\"text-sm text-text-secondary\">\n Log all channel operations for audit trail\n </p>\n </div>\n <input\n type=\"checkbox\"\n aria-label={tr('channel.auditLogging', 'Audit Logging')}\n checked={settings.enableAuditLogging}\n onChange={(e) => updateSetting('enableAuditLogging', e.target.checked)}\n className=\"size-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.sessionTimeout', 'Session Timeout (minutes)')}\n </label>\n <select\n value={settings.sessionTimeoutMinutes}\n onChange={(e) => updateSetting('sessionTimeoutMinutes', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n <option value={120}>2 hours</option>\n <option value={240}>4 hours</option>\n </select>\n </div>\n </div>\n </GlassCard>\n\n {/* Data Management */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"size-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.dataManagement', 'Data Management')}\n </h2>\n </div>\n\n <ShowMore\n collapsedHeight={96}\n label={tr('channel.showRetentionPolicies', 'Show retention policies')}\n >\n <div className=\"space-y-4\">\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.webhookEventRetention', 'Webhook Event Retention')}\n </p>\n <span className=\"text-sm text-text-secondary\">7 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Incoming webhook events are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.deliveryAttemptRetention', 'Delivery Attempt Retention')}\n </p>\n <span className=\"text-sm text-text-secondary\">7 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Successful delivery attempts are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.failedDeliveryRetention', 'Failed Delivery Retention')}\n </p>\n <span className=\"text-sm text-status-warning-text\">30 days</span>\n </div>\n <p className=\"text-sm text-text-secondary\">\n Failed delivery attempts are kept for 30 days for debugging\n </p>\n </div>\n </div>\n </ShowMore>\n </GlassCard>\n\n {/* Save Button */}\n <div className=\"flex justify-end pt-4\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={isSaving || isLoadingSettings || !!settingsLoadError}\n className=\"px-6 py-3 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50 flex items-center gap-2\"\n >\n {isSaving ? (\n <Loader2 className=\"size-5 animate-spin\" />\n ) : (\n <Save className=\"size-5 \" />\n )}\n {tr('channel.saveSettings', 'Save Settings')}\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}