@payez/next-mvp 4.8.0 → 4.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/POST-INSTALL.md +408 -408
  2. package/README.md +859 -859
  3. package/dist/components/account/MobileNavDrawer.js +18 -18
  4. package/dist/components/account/UserAvatarMenu.js +1 -1
  5. package/dist/components/admin/AlertSettingsTab.js +40 -40
  6. package/dist/components/admin/LoggingSettingsTab.js +28 -28
  7. package/dist/lib/session-store.js +21 -21
  8. package/dist/lib/startup-init.js +19 -19
  9. package/dist/middleware/create-middleware.js +6 -5
  10. package/dist/pages/login/page.js +28 -28
  11. package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
  12. package/dist/vibe/sessions.js +28 -3
  13. package/package.json +2 -2
  14. package/scripts/check-publish-eol.mjs +33 -15
  15. package/src/api/auth-handler.ts +550 -550
  16. package/src/api/index.ts +17 -17
  17. package/src/api-handlers/account/change-password.ts +170 -170
  18. package/src/api-handlers/account/masked-info.ts +46 -46
  19. package/src/api-handlers/account/profile.ts +99 -99
  20. package/src/api-handlers/account/recovery/forward.ts +117 -117
  21. package/src/api-handlers/account/recovery/initiate.ts +59 -59
  22. package/src/api-handlers/account/recovery/send-code.ts +21 -21
  23. package/src/api-handlers/account/recovery/verify-code.ts +20 -20
  24. package/src/api-handlers/account/reset-password.ts +87 -87
  25. package/src/api-handlers/account/send-code.ts +70 -70
  26. package/src/api-handlers/account/update-phone.ts +84 -84
  27. package/src/api-handlers/account/validate-password.ts +139 -139
  28. package/src/api-handlers/account/verify-email.ts +134 -134
  29. package/src/api-handlers/account/verify-sms.ts +134 -134
  30. package/src/api-handlers/admin/analytics.ts +452 -452
  31. package/src/api-handlers/admin/audit.ts +232 -232
  32. package/src/api-handlers/admin/index.ts +63 -63
  33. package/src/api-handlers/admin/redis-sessions.ts +259 -259
  34. package/src/api-handlers/admin/sessions.ts +327 -327
  35. package/src/api-handlers/admin/site-logs.ts +388 -388
  36. package/src/api-handlers/admin/stats.ts +260 -260
  37. package/src/api-handlers/admin/users.ts +251 -251
  38. package/src/api-handlers/admin/vibe-data.ts +333 -333
  39. package/src/api-handlers/anon/preferences.ts +123 -123
  40. package/src/api-handlers/auth/credential-login-email-code.ts +92 -92
  41. package/src/api-handlers/auth/credential-login.ts +948 -948
  42. package/src/api-handlers/auth/jwks.ts +31 -31
  43. package/src/api-handlers/auth/login.ts +252 -252
  44. package/src/api-handlers/auth/passkey-signin.ts +116 -116
  45. package/src/api-handlers/auth/refresh.ts +829 -829
  46. package/src/api-handlers/auth/signout.ts +201 -201
  47. package/src/api-handlers/auth/status.ts +23 -23
  48. package/src/api-handlers/auth/update-session.ts +144 -144
  49. package/src/api-handlers/auth/validate.ts +45 -45
  50. package/src/api-handlers/auth/verify-code.ts +116 -116
  51. package/src/api-handlers/mfa/backup-code-verify.ts +170 -170
  52. package/src/api-handlers/mfa/backup-codes.ts +42 -42
  53. package/src/api-handlers/mfa/methods.ts +47 -47
  54. package/src/api-handlers/mfa/totp-confirm.ts +43 -43
  55. package/src/api-handlers/mfa/totp-enroll.ts +43 -43
  56. package/src/api-handlers/mfa/totp-verify.ts +172 -172
  57. package/src/api-handlers/oauth/resume.ts +182 -182
  58. package/src/api-handlers/onboarding/send-email-code.ts +18 -18
  59. package/src/api-handlers/onboarding/set-password.ts +21 -21
  60. package/src/api-handlers/onboarding/verify-code.ts +20 -20
  61. package/src/api-handlers/onboarding/verify-welcome-email.ts +17 -17
  62. package/src/api-handlers/passkeys/list.ts +32 -32
  63. package/src/api-handlers/passkeys/manage.ts +55 -55
  64. package/src/api-handlers/passkeys/register-options.ts +43 -43
  65. package/src/api-handlers/passkeys/register-verify.ts +42 -42
  66. package/src/api-handlers/session/refresh-viability.ts +56 -56
  67. package/src/api-handlers/session/viability.ts +238 -238
  68. package/src/api-handlers/test/force-expire.ts +167 -167
  69. package/src/auth/auth-decision.ts +292 -292
  70. package/src/auth/better-auth.ts +1688 -1688
  71. package/src/auth/idp-oauth.ts +310 -310
  72. package/src/auth/password-reset-bridge.ts +98 -98
  73. package/src/auth/route-config.ts +236 -236
  74. package/src/auth/safe-callback.ts +199 -199
  75. package/src/auth/session-cookie-clear.ts +30 -30
  76. package/src/auth/session-reset-link.ts +159 -159
  77. package/src/auth/session-response-sanitizer.ts +151 -151
  78. package/src/auth/types/auth-types.ts +555 -555
  79. package/src/auth/types/index.ts +7 -7
  80. package/src/auth/unauthenticated-routes.ts +2 -2
  81. package/src/auth/utils/idp-client.ts +364 -364
  82. package/src/auth/utils/index.ts +6 -6
  83. package/src/auth/utils/token-utils.ts +243 -243
  84. package/src/client/AuthContext.tsx +192 -192
  85. package/src/client/better-auth-client.ts +70 -70
  86. package/src/client/fetch-with-auth.ts +47 -47
  87. package/src/client/fetchWithSession.ts +21 -21
  88. package/src/client/index.ts +13 -13
  89. package/src/client/useAnonSession.ts +131 -131
  90. package/src/components/SessionSync.tsx +138 -138
  91. package/src/components/SignalRHealthCheck.tsx +131 -131
  92. package/src/components/account/MobileNavDrawer.tsx +305 -305
  93. package/src/components/account/UserAvatarMenu.tsx +277 -277
  94. package/src/components/account/index.ts +13 -13
  95. package/src/components/admin/AlertSettingsTab.tsx +728 -728
  96. package/src/components/admin/AnalyticsTab.tsx +703 -703
  97. package/src/components/admin/DataBrowserTab.tsx +508 -508
  98. package/src/components/admin/LoggingSettingsTab.tsx +665 -665
  99. package/src/components/admin/SessionsTab.tsx +421 -421
  100. package/src/components/admin/StatsTab.tsx +379 -379
  101. package/src/components/admin/VibeAdminContext.tsx +87 -87
  102. package/src/components/admin/VibeAdminLayout.tsx +187 -187
  103. package/src/components/admin/index.ts +59 -59
  104. package/src/components/auth/CredentialSignInForm.tsx +240 -240
  105. package/src/components/auth/FederatedAuthSection.tsx +129 -129
  106. package/src/components/auth/ModeAwareLoginPage.tsx +146 -146
  107. package/src/components/auth/ModeAwareSignupPage.tsx +200 -200
  108. package/src/components/auth/PasswordResetBridge.tsx +30 -30
  109. package/src/components/auth/TraditionalAuthSection.tsx +103 -103
  110. package/src/components/recovery/CompleteStep.tsx +36 -36
  111. package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
  112. package/src/components/recovery/RequestedStep.tsx +30 -30
  113. package/src/components/recovery/SelectMethodStep.tsx +73 -73
  114. package/src/components/recovery/SetPasswordStep.tsx +118 -118
  115. package/src/components/recovery/VerifyCodeStep.tsx +90 -90
  116. package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
  117. package/src/components/reserved/ReservedStatusBox.tsx +118 -118
  118. package/src/components/ui/BetaBadge.tsx +58 -58
  119. package/src/components/ui/Footer.tsx +93 -93
  120. package/src/config/env.ts +55 -55
  121. package/src/config/logger.ts +62 -62
  122. package/src/config/logging-config.ts +82 -82
  123. package/src/config/unauthenticated-routes.ts +19 -19
  124. package/src/config/vibe-log-transport.ts +264 -264
  125. package/src/edge/internal-api-url.ts +65 -65
  126. package/src/edge/middleware.ts +42 -42
  127. package/src/hooks/useAuth.ts +117 -117
  128. package/src/hooks/useAuthSettings.ts +109 -109
  129. package/src/hooks/useAvailableProviders.ts +143 -143
  130. package/src/hooks/usePasswordValidation.ts +127 -127
  131. package/src/hooks/useProfile.ts +82 -82
  132. package/src/hooks/usePublicAuthSettings.ts +171 -171
  133. package/src/hooks/useSessionExpiration.ts +101 -101
  134. package/src/hooks/useViabilitySession.ts +337 -337
  135. package/src/index.ts +64 -64
  136. package/src/lib/anon-session.ts +213 -213
  137. package/src/lib/api-handler.ts +643 -643
  138. package/src/lib/app-slug.ts +228 -228
  139. package/src/lib/authorize-resume.ts +172 -172
  140. package/src/lib/bearer-fetch.ts +31 -31
  141. package/src/lib/client-key.ts +88 -88
  142. package/src/lib/code-step-copy.ts +83 -83
  143. package/src/lib/credential-sign-in-client.ts +101 -101
  144. package/src/lib/demo-mode.ts +18 -18
  145. package/src/lib/device-id.ts +67 -67
  146. package/src/lib/ensure-fresh-access-token.ts +509 -509
  147. package/src/lib/geolocation.ts +265 -265
  148. package/src/lib/idp-client-config.ts +1005 -1005
  149. package/src/lib/idp-fetch.ts +169 -169
  150. package/src/lib/idp-url-guard.ts +37 -37
  151. package/src/lib/internal-api.ts +171 -171
  152. package/src/lib/jwt-decode-client.ts +45 -45
  153. package/src/lib/jwt-decode.ts +82 -82
  154. package/src/lib/lock-window.ts +49 -49
  155. package/src/lib/login-error-message.ts +41 -41
  156. package/src/lib/never-in-response.ts +64 -64
  157. package/src/lib/nextauth-secret.ts +168 -168
  158. package/src/lib/onboarding-proxy.ts +57 -57
  159. package/src/lib/password-rules.ts +50 -50
  160. package/src/lib/rate-limit-service.ts +9 -9
  161. package/src/lib/redis.ts +105 -105
  162. package/src/lib/refresh-token-validator.ts +64 -64
  163. package/src/lib/roles.ts +177 -177
  164. package/src/lib/safe-error-response.ts +149 -149
  165. package/src/lib/secret-validation.ts +8 -8
  166. package/src/lib/session-cookie-shape.ts +185 -185
  167. package/src/lib/session-store.ts +1092 -1092
  168. package/src/lib/session.ts +34 -34
  169. package/src/lib/single-flight.ts +64 -64
  170. package/src/lib/site-logger.ts +245 -245
  171. package/src/lib/standardized-client-api.ts +901 -901
  172. package/src/lib/startup-init.ts +276 -276
  173. package/src/lib/test-aware-get-token.ts +41 -41
  174. package/src/lib/token-expiry.ts +40 -40
  175. package/src/lib/token-lifecycle.ts +546 -546
  176. package/src/lib/token-log-id.ts +69 -69
  177. package/src/lib/twofa-rejection.ts +91 -91
  178. package/src/lib/types/api-responses.ts +336 -336
  179. package/src/lib/user-agent-parser.ts +252 -252
  180. package/src/logging/api/admin-analytics.ts +51 -51
  181. package/src/logging/api/audit-log.ts +53 -53
  182. package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
  183. package/src/logging/components/AuditLogViewer.tsx +125 -125
  184. package/src/logging/components/ErrorMetricsCard.tsx +98 -98
  185. package/src/logging/components/HealthMetricsCard.tsx +70 -70
  186. package/src/logging/hooks/useAdminAnalytics.ts +22 -22
  187. package/src/logging/hooks/useAuditLog.ts +24 -24
  188. package/src/logging/hooks/useErrorMetrics.ts +40 -40
  189. package/src/logging/hooks/useHealthMetrics.ts +44 -44
  190. package/src/logging/index.ts +18 -18
  191. package/src/logging/types/analytics.ts +81 -81
  192. package/src/logging/types/audit.ts +31 -31
  193. package/src/logging/types/index.ts +3 -3
  194. package/src/middleware/create-middleware.ts +1139 -1138
  195. package/src/middleware/rbac-check.ts +279 -279
  196. package/src/middleware/twofa-presets.ts +224 -224
  197. package/src/models/DecodedAccessToken.ts +16 -16
  198. package/src/models/SessionModel.ts +331 -331
  199. package/src/pages/admin-login/page.tsx +111 -111
  200. package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
  201. package/src/pages/admin-page-permissions/index.ts +7 -7
  202. package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
  203. package/src/pages/admin-roles/index.ts +9 -9
  204. package/src/pages/admin-roles/modals.tsx +469 -469
  205. package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
  206. package/src/pages/client-admin/index.ts +33 -33
  207. package/src/pages/coming-soon/page.tsx +83 -83
  208. package/src/pages/login/page.tsx +338 -338
  209. package/src/pages/onboarding/index.ts +11 -11
  210. package/src/pages/onboarding/page.tsx +212 -212
  211. package/src/pages/profile/EnhancedProfilePage.tsx +514 -514
  212. package/src/pages/profile/index.ts +9 -9
  213. package/src/pages/profile/page.tsx +166 -166
  214. package/src/pages/recovery/page.tsx +267 -267
  215. package/src/pages/roles/MyRolesPage.tsx +211 -211
  216. package/src/pages/roles/components.tsx +294 -294
  217. package/src/pages/roles/index.ts +17 -17
  218. package/src/pages/security/EnhancedSecurityPage.tsx +660 -660
  219. package/src/pages/security/index.ts +12 -12
  220. package/src/pages/security/page.tsx +507 -507
  221. package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
  222. package/src/pages/settings/index.ts +9 -9
  223. package/src/pages/settings/page.tsx +47 -47
  224. package/src/pages/showcase/ShowcasePage.tsx +532 -532
  225. package/src/pages/showcase/index.ts +13 -13
  226. package/src/pages/test-env/EmergencyLogoutPage.tsx +192 -192
  227. package/src/pages/test-env/JwtInspectPage.tsx +449 -449
  228. package/src/pages/test-env/RefreshTokenPage.tsx +178 -178
  229. package/src/pages/test-env/TestEnvPage.tsx +130 -130
  230. package/src/pages/test-env/guarded.tsx +81 -81
  231. package/src/pages/test-env/index.ts +77 -77
  232. package/src/pages/test-env/redact-credentials.ts +96 -96
  233. package/src/pages/verify-code/page.tsx +746 -746
  234. package/src/routes/account/index.ts +31 -31
  235. package/src/routes/account/masked-info.ts +36 -36
  236. package/src/routes/account/send-code.ts +39 -39
  237. package/src/routes/account/update-phone.ts +13 -13
  238. package/src/routes/account/verify-email.ts +40 -40
  239. package/src/routes/account/verify-sms.ts +40 -40
  240. package/src/routes/auth/credential-login-email-code.ts +6 -6
  241. package/src/routes/auth/credential-login.ts +6 -6
  242. package/src/routes/auth/index.ts +22 -22
  243. package/src/routes/auth/logout.ts +111 -111
  244. package/src/routes/auth/nextauth.ts +45 -45
  245. package/src/routes/auth/passkey-signin.ts +13 -13
  246. package/src/routes/auth/refresh.ts +77 -77
  247. package/src/routes/auth/session-reset.ts +37 -37
  248. package/src/routes/auth/session.ts +261 -261
  249. package/src/routes/auth/settings.ts +181 -181
  250. package/src/routes/auth/viability.ts +519 -519
  251. package/src/routes/index.ts +17 -17
  252. package/src/routes/mfa/backup-code-verify.ts +1 -1
  253. package/src/routes/mfa/backup-codes.ts +10 -10
  254. package/src/routes/mfa/methods.ts +22 -22
  255. package/src/routes/mfa/totp-confirm.ts +10 -10
  256. package/src/routes/mfa/totp-enroll.ts +10 -10
  257. package/src/routes/mfa/totp-verify.ts +14 -14
  258. package/src/routes/onboarding/index.ts +15 -15
  259. package/src/routes/onboarding/send-email-code.ts +2 -2
  260. package/src/routes/onboarding/set-password.ts +2 -2
  261. package/src/routes/onboarding/verify-code.ts +2 -2
  262. package/src/routes/onboarding/verify-welcome-email.ts +2 -2
  263. package/src/routes/passkeys/list.ts +10 -10
  264. package/src/routes/passkeys/manage.ts +23 -23
  265. package/src/routes/passkeys/register-options.ts +10 -10
  266. package/src/routes/passkeys/register-verify.ts +10 -10
  267. package/src/routes/session/index.ts +7 -7
  268. package/src/routes/session/refresh-viability.ts +17 -17
  269. package/src/server/auth-guard.ts +203 -203
  270. package/src/server/auth.ts +486 -486
  271. package/src/server/decode-session.ts +309 -309
  272. package/src/server/slim-middleware.ts +117 -117
  273. package/src/server/verified-session-token.ts +103 -103
  274. package/src/server/with-auth.ts +93 -93
  275. package/src/services/signalrActivityService.ts +258 -258
  276. package/src/stores/authStore.ts +1900 -1900
  277. package/src/templates/instrumentation.ts +60 -60
  278. package/src/theme/ThemeProvider.tsx +39 -39
  279. package/src/theme/default.ts +33 -33
  280. package/src/theme/index.ts +31 -31
  281. package/src/theme/types.ts +69 -69
  282. package/src/theme/useTheme.ts +57 -57
  283. package/src/theme/utils.ts +40 -40
  284. package/src/types/api.ts +12 -12
  285. package/src/types/auth.d.ts +23 -23
  286. package/src/types/auth.ts +48 -48
  287. package/src/types/logging.ts +11 -11
  288. package/src/types/recovery.ts +54 -54
  289. package/src/utils/api.ts +401 -401
  290. package/src/utils/circuitBreaker.ts +40 -40
  291. package/src/utils/error-message.ts +108 -108
  292. package/src/utils/layout/reservedSpace.ts +124 -124
  293. package/src/utils/logout.ts +30 -30
  294. package/src/vibe/client.ts +877 -877
  295. package/src/vibe/enterprise-auth.ts +208 -208
  296. package/src/vibe/errors.ts +209 -209
  297. package/src/vibe/generic.ts +339 -339
  298. package/src/vibe/hooks/index.ts +367 -367
  299. package/src/vibe/index.ts +133 -133
  300. package/src/vibe/sessions.ts +925 -899
  301. package/src/vibe/types.ts +577 -577
@@ -58,24 +58,24 @@ function MobileNavDrawer({ isOpen, onClose, navItems, customSections, basePath =
58
58
  const userEmail = session?.user?.email;
59
59
  const displaySource = userName || userEmail;
60
60
  const userInitial = displaySource?.charAt(0).toUpperCase() || '?';
61
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `
62
- fixed inset-0 bg-black/50 backdrop-blur-sm z-40 lg:hidden
63
- transition-opacity duration-300
64
- ${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}
65
- `, onClick: onClose, "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", "aria-expanded": isOpen, className: `
66
- fixed top-0 right-0 bottom-0 w-80 max-w-[85vw]
67
- bg-white dark:bg-slate-900
68
- shadow-[-8px_0_32px_rgba(0,0,0,0.15)]
69
- dark:shadow-[-8px_0_32px_rgba(0,0,0,0.4)]
70
- z-50 lg:hidden
71
- overflow-y-auto
72
- transition-transform duration-300 ease-out
73
- ${isOpen ? 'translate-x-0' : 'translate-x-full'}
74
- `, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-white/10", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Menu" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\r\n p-2 rounded-xl\r\n text-gray-400 hover:text-gray-900\r\n dark:hover:text-white\r\n hover:bg-gray-100 dark:hover:bg-white/10\r\n transition-colors\r\n ", "aria-label": "Close menu", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-5 w-5" }) })] }), isAuthenticated && session?.user && ((0, jsx_runtime_1.jsx)("div", { className: "p-4 border-b border-gray-200 dark:border-white/10", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 48, height: 48, className: "w-12 h-12 rounded-full", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-lg", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-500 dark:text-slate-400 truncate", children: userEmail }))] })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "p-2", children: navItems.map((item) => ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: `
75
- flex items-center gap-3 px-4 py-3.5 rounded-xl
76
- transition-colors duration-200
61
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `
62
+ fixed inset-0 bg-black/50 backdrop-blur-sm z-40 lg:hidden
63
+ transition-opacity duration-300
64
+ ${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}
65
+ `, onClick: onClose, "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", "aria-expanded": isOpen, className: `
66
+ fixed top-0 right-0 bottom-0 w-80 max-w-[85vw]
67
+ bg-white dark:bg-slate-900
68
+ shadow-[-8px_0_32px_rgba(0,0,0,0.15)]
69
+ dark:shadow-[-8px_0_32px_rgba(0,0,0,0.4)]
70
+ z-50 lg:hidden
71
+ overflow-y-auto
72
+ transition-transform duration-300 ease-out
73
+ ${isOpen ? 'translate-x-0' : 'translate-x-full'}
74
+ `, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-white/10", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Menu" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\n p-2 rounded-xl\n text-gray-400 hover:text-gray-900\n dark:hover:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", "aria-label": "Close menu", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-5 w-5" }) })] }), isAuthenticated && session?.user && ((0, jsx_runtime_1.jsx)("div", { className: "p-4 border-b border-gray-200 dark:border-white/10", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 48, height: 48, className: "w-12 h-12 rounded-full", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-lg", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-500 dark:text-slate-400 truncate", children: userEmail }))] })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "p-2", children: navItems.map((item) => ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: `
75
+ flex items-center gap-3 px-4 py-3.5 rounded-xl
76
+ transition-colors duration-200
77
77
  ${isActiveRoute(item.href)
78
78
  ? 'bg-blue-500/10 text-blue-500'
79
- : 'text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-white/10'}
80
- `, children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label }), isActiveRoute(item.href) && ((0, jsx_runtime_1.jsx)("span", { className: "ml-auto w-2 h-2 rounded-full bg-blue-500" }))] }, item.href))) }), customSections?.map((section, sectionIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: "p-2 border-t border-gray-200 dark:border-white/10", children: [section.title && ((0, jsx_runtime_1.jsx)("p", { className: "px-4 py-2 text-xs font-semibold text-gray-500 dark:text-slate-400 uppercase tracking-wider", children: section.title })), section.items.map((item, itemIndex) => item.href ? ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: "\r\n flex items-center gap-3 px-4 py-3 rounded-xl\r\n text-gray-900 dark:text-white\r\n hover:bg-gray-100 dark:hover:bg-white/10\r\n transition-colors\r\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)) : ((0, jsx_runtime_1.jsxs)("button", { onClick: () => handleSectionItemClick(item), className: "\r\n flex items-center gap-3 px-4 py-3 rounded-xl w-full text-left\r\n text-gray-900 dark:text-white\r\n hover:bg-gray-100 dark:hover:bg-white/10\r\n transition-colors\r\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)))] }, sectionIndex))), (0, jsx_runtime_1.jsx)("div", { className: "p-4 mt-auto border-t border-gray-200 dark:border-white/10", children: !isAuthenticated ? (unauthActions ?? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: (0, jsx_runtime_1.jsx)("button", { onClick: handleSignIn, className: "\r\n w-full px-4 py-3 rounded-xl\r\n text-blue-500 font-semibold\r\n border border-blue-500/30\r\n hover:bg-blue-500/10\r\n transition-colors\r\n ", children: "Login" }) }))) : (authFooter ?? ((0, jsx_runtime_1.jsx)(link_1.default, { href: basePath, onClick: onClose, className: "\r\n flex items-center justify-center gap-2\r\n w-full px-4 py-3 rounded-xl\r\n text-gray-500 dark:text-slate-400 font-medium\r\n hover:bg-gray-100 dark:hover:bg-white/10\r\n transition-colors\r\n ", children: "Account Settings" }))) })] })] }));
79
+ : 'text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-white/10'}
80
+ `, children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label }), isActiveRoute(item.href) && ((0, jsx_runtime_1.jsx)("span", { className: "ml-auto w-2 h-2 rounded-full bg-blue-500" }))] }, item.href))) }), customSections?.map((section, sectionIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: "p-2 border-t border-gray-200 dark:border-white/10", children: [section.title && ((0, jsx_runtime_1.jsx)("p", { className: "px-4 py-2 text-xs font-semibold text-gray-500 dark:text-slate-400 uppercase tracking-wider", children: section.title })), section.items.map((item, itemIndex) => item.href ? ((0, jsx_runtime_1.jsxs)(link_1.default, { href: item.href, onClick: onClose, className: "\n flex items-center gap-3 px-4 py-3 rounded-xl\n text-gray-900 dark:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)) : ((0, jsx_runtime_1.jsxs)("button", { onClick: () => handleSectionItemClick(item), className: "\n flex items-center gap-3 px-4 py-3 rounded-xl w-full text-left\n text-gray-900 dark:text-white\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: [item.icon && (0, jsx_runtime_1.jsx)("span", { className: "text-xl", children: item.icon }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: item.label })] }, itemIndex)))] }, sectionIndex))), (0, jsx_runtime_1.jsx)("div", { className: "p-4 mt-auto border-t border-gray-200 dark:border-white/10", children: !isAuthenticated ? (unauthActions ?? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: (0, jsx_runtime_1.jsx)("button", { onClick: handleSignIn, className: "\n w-full px-4 py-3 rounded-xl\n text-blue-500 font-semibold\n border border-blue-500/30\n hover:bg-blue-500/10\n transition-colors\n ", children: "Login" }) }))) : (authFooter ?? ((0, jsx_runtime_1.jsx)(link_1.default, { href: basePath, onClick: onClose, className: "\n flex items-center justify-center gap-2\n w-full px-4 py-3 rounded-xl\n text-gray-500 dark:text-slate-400 font-medium\n hover:bg-gray-100 dark:hover:bg-white/10\n transition-colors\n ", children: "Account Settings" }))) })] })] }));
81
81
  }
@@ -92,7 +92,7 @@ function UserAvatarMenu({ basePath = '', showProfile = true, showSettings = true
92
92
  router.push(item.href);
93
93
  }
94
94
  };
95
- return ((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "relative", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(!isOpen), className: "flex items-center justify-center h-10 w-10 rounded-full overflow-hidden bg-blue-500 text-white font-semibold text-lg hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900", "aria-label": "User menu", "aria-expanded": isOpen, "aria-haspopup": "true", children: session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 40, height: 40, className: "w-10 h-10 rounded-full object-cover", unoptimized: true })) : (userInitial) }), isOpen && ((0, jsx_runtime_1.jsxs)("div", { className: "absolute right-0 mt-2 w-56 rounded-md shadow-lg z-50\r\n bg-white dark:bg-slate-900\r\n border border-gray-200 dark:border-slate-700", role: "menu", "aria-orientation": "vertical", children: [(0, jsx_runtime_1.jsx)("div", { className: "px-4 py-3 border-b border-gray-200 dark:border-slate-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 32, height: 32, className: "w-8 h-8 rounded-full flex-shrink-0", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-sm flex-shrink-0", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-700 dark:text-slate-200 truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400 truncate", children: userEmail })), !userName && !userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400", children: "Signed in" }))] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "py-1", children: [showProfile && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.User, { className: "h-4 w-4" }), label: "Profile", onClick: () => handleNavigation(`${basePath}/profile`) })), showSettings && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { className: "h-4 w-4" }), label: "Settings", onClick: () => handleNavigation(`${basePath}/settings`) })), showSecurity && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Shield, { className: "h-4 w-4" }), label: "Security", onClick: () => handleNavigation(`${basePath}/security`) })), customItems?.map((item, index) => ((0, jsx_runtime_1.jsx)(MenuItem, { icon: item.icon, label: item.label, onClick: () => handleItemClick(item) }, index)))] }), (0, jsx_runtime_1.jsx)("div", { className: "border-t border-gray-200 dark:border-slate-700 py-1", children: (0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "h-4 w-4" }), label: "Sign Out", onClick: handleSignOut, variant: "danger" }) })] }))] }));
95
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "relative", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(!isOpen), className: "flex items-center justify-center h-10 w-10 rounded-full overflow-hidden bg-blue-500 text-white font-semibold text-lg hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900", "aria-label": "User menu", "aria-expanded": isOpen, "aria-haspopup": "true", children: session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 40, height: 40, className: "w-10 h-10 rounded-full object-cover", unoptimized: true })) : (userInitial) }), isOpen && ((0, jsx_runtime_1.jsxs)("div", { className: "absolute right-0 mt-2 w-56 rounded-md shadow-lg z-50\n bg-white dark:bg-slate-900\n border border-gray-200 dark:border-slate-700", role: "menu", "aria-orientation": "vertical", children: [(0, jsx_runtime_1.jsx)("div", { className: "px-4 py-3 border-b border-gray-200 dark:border-slate-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [session.user.image ? ((0, jsx_runtime_1.jsx)(image_1.default, { src: session.user.image, alt: "", width: 32, height: 32, className: "w-8 h-8 rounded-full flex-shrink-0", unoptimized: true })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold text-sm flex-shrink-0", children: userInitial })), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [userName && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-700 dark:text-slate-200 truncate", children: userName })), userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400 truncate", children: userEmail })), !userName && !userEmail && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-slate-400", children: "Signed in" }))] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "py-1", children: [showProfile && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.User, { className: "h-4 w-4" }), label: "Profile", onClick: () => handleNavigation(`${basePath}/profile`) })), showSettings && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { className: "h-4 w-4" }), label: "Settings", onClick: () => handleNavigation(`${basePath}/settings`) })), showSecurity && ((0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Shield, { className: "h-4 w-4" }), label: "Security", onClick: () => handleNavigation(`${basePath}/security`) })), customItems?.map((item, index) => ((0, jsx_runtime_1.jsx)(MenuItem, { icon: item.icon, label: item.label, onClick: () => handleItemClick(item) }, index)))] }), (0, jsx_runtime_1.jsx)("div", { className: "border-t border-gray-200 dark:border-slate-700 py-1", children: (0, jsx_runtime_1.jsx)(MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "h-4 w-4" }), label: "Sign Out", onClick: handleSignOut, variant: "danger" }) })] }))] }));
96
96
  }
97
97
  function MenuItem({ icon, label, onClick, variant = 'default' }) {
98
98
  const baseClasses = "flex items-center w-full px-4 py-2 text-sm cursor-pointer transition-colors";
@@ -277,22 +277,22 @@ function AlertSettingsTab({ isDark = true, apiBasePath = '/api/admin/alerts', on
277
277
  e.preventDefault();
278
278
  addRecipient();
279
279
  }
280
- }, placeholder: "Enter email address", className: `
281
- w-full px-3 py-2 rounded-lg text-sm
282
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
283
- focus:outline-none focus:ring-2 focus:ring-indigo-500
284
- ${recipientError ? (isDark ? 'border-red-500' : 'border-red-400') : ''}
285
- ` }), recipientError && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-1 ${isDark ? 'text-red-400' : 'text-red-500'}`, children: recipientError }))] }), (0, jsx_runtime_1.jsxs)("button", { onClick: addRecipient, className: `
286
- px-4 py-2 rounded-lg font-medium text-sm transition-colors
287
- ${isDark ? 'bg-indigo-600 hover:bg-indigo-700' : 'bg-indigo-500 hover:bg-indigo-600'}
288
- text-white flex items-center gap-2
280
+ }, placeholder: "Enter email address", className: `
281
+ w-full px-3 py-2 rounded-lg text-sm
282
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
283
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
284
+ ${recipientError ? (isDark ? 'border-red-500' : 'border-red-400') : ''}
285
+ ` }), recipientError && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-1 ${isDark ? 'text-red-400' : 'text-red-500'}`, children: recipientError }))] }), (0, jsx_runtime_1.jsxs)("button", { onClick: addRecipient, className: `
286
+ px-4 py-2 rounded-lg font-medium text-sm transition-colors
287
+ ${isDark ? 'bg-indigo-600 hover:bg-indigo-700' : 'bg-indigo-500 hover:bg-indigo-600'}
288
+ text-white flex items-center gap-2
289
289
  `, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16 }), "Add"] })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: isDark ? 'text-cyan-400' : 'text-cyan-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Delivery Mode" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "How alerts are delivered to recipients" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3", children: DELIVERY_MODES.map((mode) => {
290
290
  const isSelected = editedMode === mode.value;
291
- return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setEditedMode(mode.value), className: `
292
- p-4 rounded-lg border-2 text-left transition-all
291
+ return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setEditedMode(mode.value), className: `
292
+ p-4 rounded-lg border-2 text-left transition-all
293
293
  ${isSelected
294
294
  ? 'border-indigo-500 ' + (isDark ? 'bg-indigo-500/10' : 'bg-indigo-50')
295
- : (isDark ? 'border-slate-700 hover:border-slate-600' : 'border-gray-200 hover:border-gray-300')}
295
+ : (isDark ? 'border-slate-700 hover:border-slate-600' : 'border-gray-200 hover:border-gray-300')}
296
296
  `, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-1", children: [(0, jsx_runtime_1.jsx)("div", { className: `w-3 h-3 rounded-full border-2 flex items-center justify-center ${isSelected ? 'border-indigo-500' : (isDark ? 'border-slate-500' : 'border-gray-400')}`, children: isSelected && (0, jsx_runtime_1.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500" }) }), (0, jsx_runtime_1.jsx)("span", { className: `font-medium ${themeClasses.textPrimary}`, children: mode.label })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${themeClasses.textMuted} ml-5`, children: mode.description })] }, mode.value));
297
297
  }) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Bell, { className: isDark ? 'text-amber-400' : 'text-amber-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Alert Types" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Configure which alerts to receive and their thresholds" })] }), (0, jsx_runtime_1.jsx)("div", { className: "divide-y divide-slate-700/50", children: ALERT_TYPES.map((alertType) => {
298
298
  const config = editedAlerts[alertType.id];
@@ -305,47 +305,47 @@ function AlertSettingsTab({ isDark = true, apiBasePath = '/api/admin/alerts', on
305
305
  else {
306
306
  updateAlertConfig(alertType.id, { threshold: val });
307
307
  }
308
- }, disabled: !config.enabled && !alertType.alwaysSent, className: `
309
- w-16 px-2 py-1.5 rounded text-sm text-center
310
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
311
- focus:outline-none focus:ring-2 focus:ring-indigo-500
312
- disabled:opacity-50
313
- ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs ${themeClasses.textMuted} whitespace-nowrap`, children: alertType.thresholdUnit })] })), !alertType.alwaysSent && ((0, jsx_runtime_1.jsx)("button", { onClick: () => updateAlertConfig(alertType.id, { enabled: !config.enabled }), className: `
314
- relative w-11 h-6 rounded-full transition-colors
308
+ }, disabled: !config.enabled && !alertType.alwaysSent, className: `
309
+ w-16 px-2 py-1.5 rounded text-sm text-center
310
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
311
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
312
+ disabled:opacity-50
313
+ ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs ${themeClasses.textMuted} whitespace-nowrap`, children: alertType.thresholdUnit })] })), !alertType.alwaysSent && ((0, jsx_runtime_1.jsx)("button", { onClick: () => updateAlertConfig(alertType.id, { enabled: !config.enabled }), className: `
314
+ relative w-11 h-6 rounded-full transition-colors
315
315
  ${config.enabled
316
316
  ? (isDark ? 'bg-indigo-600' : 'bg-indigo-500')
317
- : (isDark ? 'bg-slate-600' : 'bg-gray-300')}
318
- `, children: (0, jsx_runtime_1.jsx)("span", { className: `
319
- absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform
320
- ${config.enabled ? 'translate-x-5' : ''}
317
+ : (isDark ? 'bg-slate-600' : 'bg-gray-300')}
318
+ `, children: (0, jsx_runtime_1.jsx)("span", { className: `
319
+ absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform
320
+ ${config.enabled ? 'translate-x-5' : ''}
321
321
  ` }) }))] })] }) }, alertType.id));
322
- }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Rate Limiting" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Maximum alerts:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "100", value: editedRateLimit, onChange: (e) => setEditedRateLimit(parseInt(e.target.value) || 10), className: `
323
- w-20 px-3 py-2 rounded-lg text-sm
324
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
325
- focus:outline-none focus:ring-2 focus:ring-indigo-500
322
+ }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Rate Limiting" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Maximum alerts:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "100", value: editedRateLimit, onChange: (e) => setEditedRateLimit(parseInt(e.target.value) || 10), className: `
323
+ w-20 px-3 py-2 rounded-lg text-sm
324
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
325
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
326
326
  ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "per hour" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-start gap-2 mt-3 text-xs ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { size: 14, className: "flex-shrink-0 mt-0.5" }), (0, jsx_runtime_1.jsx)("span", { children: "Critical alerts (Storage Critical, Agent Expired) are always sent regardless of this limit." })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Send, { className: isDark ? 'text-blue-400' : 'text-blue-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Test & History" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsxs)("select", { disabled: sendingTest || editedRecipients.length === 0, onChange: (e) => {
327
327
  if (e.target.value) {
328
328
  sendTestAlert(e.target.value);
329
329
  e.target.value = '';
330
330
  }
331
- }, className: `
332
- px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
333
- ${isDark ? 'bg-blue-900/30 text-blue-300 border-blue-700 hover:bg-blue-900/50' : 'bg-blue-50 text-blue-700 border-blue-300 hover:bg-blue-100'}
334
- border focus:outline-none focus:ring-2 focus:ring-blue-500
335
- disabled:opacity-50 disabled:cursor-not-allowed
331
+ }, className: `
332
+ px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
333
+ ${isDark ? 'bg-blue-900/30 text-blue-300 border-blue-700 hover:bg-blue-900/50' : 'bg-blue-50 text-blue-700 border-blue-300 hover:bg-blue-100'}
334
+ border focus:outline-none focus:ring-2 focus:ring-blue-500
335
+ disabled:opacity-50 disabled:cursor-not-allowed
336
336
  `, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Send Test Alert..." }), TEST_ALERT_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-blue-400' : 'text-blue-500'}` })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => {
337
337
  // TODO: Open history modal or navigate to history page
338
338
  window.open(`${apiBasePath}/history`, '_blank');
339
- }, className: `
340
- px-4 py-2.5 rounded-lg text-sm font-medium flex items-center gap-2
341
- ${isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
342
- border ${isDark ? 'border-slate-600' : 'border-gray-300'}
343
- `, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.History, { size: 16 }), "View Alert History"] })] }), editedRecipients.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-3 ${themeClasses.textMuted}`, children: "Add at least one recipient to send test alerts." }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
344
- px-6 py-2.5 rounded-lg font-medium text-sm transition-all
339
+ }, className: `
340
+ px-4 py-2.5 rounded-lg text-sm font-medium flex items-center gap-2
341
+ ${isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}
342
+ border ${isDark ? 'border-slate-600' : 'border-gray-300'}
343
+ `, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.History, { size: 16 }), "View Alert History"] })] }), editedRecipients.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-3 ${themeClasses.textMuted}`, children: "Add at least one recipient to send test alerts." }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
344
+ px-6 py-2.5 rounded-lg font-medium text-sm transition-all
345
345
  ${hasChanges
346
346
  ? 'bg-indigo-600 text-white hover:bg-indigo-700'
347
- : (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
348
- disabled:cursor-not-allowed
347
+ : (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
348
+ disabled:cursor-not-allowed
349
349
  `, children: saving ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Saving..."] })) : ('Save Settings') })] })] }));
350
350
  }
351
351
  exports.default = AlertSettingsTab;
@@ -280,18 +280,18 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
280
280
  }
281
281
  return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [error && ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center gap-3 p-4 rounded-lg ${isDark ? 'bg-red-900/30 border-red-700' : 'bg-red-50 border-red-300'} border`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: isDark ? 'text-red-400' : 'text-red-500', size: 20 }), (0, jsx_runtime_1.jsx)("span", { className: isDark ? 'text-red-300' : 'text-red-700', children: error })] })), successMessage && ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center gap-3 p-4 rounded-lg ${isDark ? 'bg-green-900/30 border-green-700' : 'bg-green-50 border-green-300'} border`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: isDark ? 'text-green-400' : 'text-green-500', size: 20 }), (0, jsx_runtime_1.jsx)("span", { className: isDark ? 'text-green-300' : 'text-green-700', children: successMessage })] })), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { className: isDark ? 'text-amber-400' : 'text-amber-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Quick Setup" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Choose a preset to quickly configure log levels" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-3", children: PRESETS.map((preset) => {
282
282
  const isActive = getActivePreset() === preset.id;
283
- return ((0, jsx_runtime_1.jsx)("button", { onClick: () => applyPreset(preset.id), className: `
284
- px-4 py-2.5 rounded-lg font-medium text-sm transition-all
283
+ return ((0, jsx_runtime_1.jsx)("button", { onClick: () => applyPreset(preset.id), className: `
284
+ px-4 py-2.5 rounded-lg font-medium text-sm transition-all
285
285
  ${isActive
286
286
  ? 'bg-indigo-600 text-white ring-2 ring-indigo-500 ring-offset-2 ' + (isDark ? 'ring-offset-slate-800' : 'ring-offset-white')
287
- : (isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200')}
287
+ : (isDark ? 'bg-slate-700 text-white hover:bg-slate-600' : 'bg-gray-100 text-gray-700 hover:bg-gray-200')}
288
288
  `, children: preset.label }, preset.id));
289
289
  }) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Bug, { className: isDark ? 'text-violet-400' : 'text-violet-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Log Levels" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "Set verbosity per category" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "space-y-4", children: CATEGORIES.map((category) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${themeClasses.textPrimary}`, children: category.label }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${themeClasses.textMuted}`, children: category.description })] }), (0, jsx_runtime_1.jsx)("select", { value: editedLevels?.[category.id] || 'info', onChange: (e) => {
290
290
  setEditedLevels(prev => prev ? { ...prev, [category.id]: e.target.value } : null);
291
- }, className: `
292
- px-3 py-2 rounded-lg text-sm font-medium cursor-pointer
293
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
294
- focus:outline-none focus:ring-2 focus:ring-indigo-500
291
+ }, className: `
292
+ px-3 py-2 rounded-lg text-sm font-medium cursor-pointer
293
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
294
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
295
295
  `, children: LOG_LEVELS.map((level) => ((0, jsx_runtime_1.jsx)("option", { value: level.value, children: level.label }, level.value))) })] }, category.id))) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: isDark ? 'text-cyan-400' : 'text-cyan-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Retention Settings" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${themeClasses.textMuted} mt-1`, children: "How long to keep logs by severity level" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4", children: [
296
296
  { key: 'debug_days', label: 'Debug' },
297
297
  { key: 'info_days', label: 'Info' },
@@ -300,22 +300,22 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
300
300
  { key: 'critical_days', label: 'Critical' },
301
301
  ].map((item) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm ${themeClasses.textSecondary} mb-1`, children: item.label }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "365", value: editedRetention?.[item.key] || 7, onChange: (e) => {
302
302
  setEditedRetention(prev => prev ? { ...prev, [item.key]: parseInt(e.target.value) || 7 } : null);
303
- }, className: `
304
- w-20 px-3 py-2 rounded-lg text-sm
305
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
306
- focus:outline-none focus:ring-2 focus:ring-indigo-500
303
+ }, className: `
304
+ w-20 px-3 py-2 rounded-lg text-sm
305
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
306
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
307
307
  ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "days" })] })] }, item.key))) }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${themeClasses.cardBg} border rounded-xl overflow-hidden`, children: [(0, jsx_runtime_1.jsx)("div", { className: `px-5 py-4 border-b ${isDark ? 'border-slate-700' : 'border-gray-200'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.HardDrive, { className: isDark ? 'text-emerald-400' : 'text-emerald-500', size: 20 }), (0, jsx_runtime_1.jsx)("h3", { className: `font-semibold ${themeClasses.textPrimary}`, children: "Storage Limits" })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "p-5 space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("label", { className: `text-sm ${themeClasses.textSecondary}`, children: "Max size:" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: "1", max: "1000", value: editedLimits?.max_size_mb || 10, onChange: (e) => {
308
308
  setEditedLimits(prev => prev ? { ...prev, max_size_mb: parseInt(e.target.value) || 10 } : null);
309
- }, className: `
310
- w-20 px-3 py-2 rounded-lg text-sm
311
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
312
- focus:outline-none focus:ring-2 focus:ring-indigo-500
309
+ }, className: `
310
+ w-20 px-3 py-2 rounded-lg text-sm
311
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
312
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
313
313
  ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "MB" })] }), (0, jsx_runtime_1.jsx)("span", { className: themeClasses.textMuted, children: "or" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("input", { type: "number", min: "100", max: "1000000", step: "1000", value: editedLimits?.max_rows || 20000, onChange: (e) => {
314
314
  setEditedLimits(prev => prev ? { ...prev, max_rows: parseInt(e.target.value) || 20000 } : null);
315
- }, className: `
316
- w-28 px-3 py-2 rounded-lg text-sm
317
- ${themeClasses.inputBg} ${themeClasses.textPrimary} border
318
- focus:outline-none focus:ring-2 focus:ring-indigo-500
315
+ }, className: `
316
+ w-28 px-3 py-2 rounded-lg text-sm
317
+ ${themeClasses.inputBg} ${themeClasses.textPrimary} border
318
+ focus:outline-none focus:ring-2 focus:ring-indigo-500
319
319
  ` }), (0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "rows" })] })] }), usage && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textSecondary}`, children: "Current usage" }), (0, jsx_runtime_1.jsxs)("span", { className: `text-sm font-medium ${themeClasses.textPrimary}`, children: [usage.total_size_mb?.toFixed(1) || 0, " MB of ", usage.limits.max_size_mb, " MB (", usage.percent_used || 0, "%)"] })] }), (0, jsx_runtime_1.jsx)("div", { className: `h-3 rounded-full overflow-hidden ${isDark ? 'bg-slate-700' : 'bg-gray-200'}`, children: (0, jsx_runtime_1.jsx)("div", { className: `h-full rounded-full transition-all ${(usage.percent_used || 0) > 90 ? 'bg-red-500' :
320
320
  (usage.percent_used || 0) > 70 ? 'bg-amber-500' :
321
321
  'bg-emerald-500'}`, style: { width: `${Math.min(usage.percent_used || 0, 100)}%` } }) }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between mt-2 text-xs ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsxs)("span", { children: [(usage.total_rows || 0).toLocaleString(), " rows"] }), usage.oldest_entry && ((0, jsx_runtime_1.jsxs)("span", { children: ["Oldest: ", new Date(usage.oldest_entry).toLocaleDateString()] }))] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsxs)("select", { disabled: pruning, onChange: (e) => {
@@ -323,17 +323,17 @@ function LoggingSettingsTab({ isDark = true, apiBasePath = '/api/admin/logging',
323
323
  handlePrune(e.target.value);
324
324
  e.target.value = '';
325
325
  }
326
- }, className: `
327
- px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
328
- ${isDark ? 'bg-red-900/30 text-red-300 border-red-700 hover:bg-red-900/50' : 'bg-red-50 text-red-700 border-red-300 hover:bg-red-100'}
329
- border focus:outline-none focus:ring-2 focus:ring-red-500
330
- disabled:opacity-50 disabled:cursor-not-allowed
331
- `, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Prune Now..." }), PRUNE_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-red-400' : 'text-red-500'}` })] }), pruning && ((0, jsx_runtime_1.jsxs)("span", { className: `flex items-center gap-2 text-sm ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Pruning..."] }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
332
- px-6 py-2.5 rounded-lg font-medium text-sm transition-all
326
+ }, className: `
327
+ px-4 py-2.5 pr-10 rounded-lg text-sm font-medium cursor-pointer appearance-none
328
+ ${isDark ? 'bg-red-900/30 text-red-300 border-red-700 hover:bg-red-900/50' : 'bg-red-50 text-red-700 border-red-300 hover:bg-red-100'}
329
+ border focus:outline-none focus:ring-2 focus:ring-red-500
330
+ disabled:opacity-50 disabled:cursor-not-allowed
331
+ `, children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Prune Now..." }), PRUNE_OPTIONS.map((opt) => ((0, jsx_runtime_1.jsx)("option", { value: opt.value, children: opt.label }, opt.value)))] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: `absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none ${isDark ? 'text-red-400' : 'text-red-500'}` })] }), pruning && ((0, jsx_runtime_1.jsxs)("span", { className: `flex items-center gap-2 text-sm ${themeClasses.textMuted}`, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Pruning..."] }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-end gap-4", children: [hasChanges && ((0, jsx_runtime_1.jsx)("span", { className: `text-sm ${themeClasses.textMuted}`, children: "You have unsaved changes" })), (0, jsx_runtime_1.jsx)("button", { onClick: saveSettings, disabled: !hasChanges || saving, className: `
332
+ px-6 py-2.5 rounded-lg font-medium text-sm transition-all
333
333
  ${hasChanges
334
334
  ? 'bg-indigo-600 text-white hover:bg-indigo-700'
335
- : (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
336
- disabled:cursor-not-allowed
335
+ : (isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-400')}
336
+ disabled:cursor-not-allowed
337
337
  `, children: saving ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { className: "w-4 h-4 animate-spin" }), "Saving..."] })) : ('Save Settings') })] })] }));
338
338
  }
339
339
  exports.default = LoggingSettingsTab;
@@ -823,27 +823,27 @@ async function releaseRefreshLock(sessionToken, requestId, lockVersion) {
823
823
  const lockKey = getRefreshLockKey(sessionToken);
824
824
  try {
825
825
  // Lua script for atomic lock validation and release
826
- const luaScript = `
827
- local lockKey = KEYS[1]
828
- local expectedRequestId = ARGV[1]
829
- local expectedVersion = ARGV[2]
830
-
831
- local lockData = redis.call('GET', lockKey)
832
- if not lockData then
833
- return 0 -- Lock doesn't exist
834
- end
835
-
836
- local lockInfo = cjson.decode(lockData)
837
- if lockInfo.acquiredBy == expectedRequestId then
838
- if not expectedVersion or expectedVersion == '' or tostring(lockInfo.lockVersion) == expectedVersion then
839
- redis.call('DEL', lockKey)
840
- return 1 -- Successfully released
841
- else
842
- return -2 -- Version mismatch
843
- end
844
- else
845
- return -1 -- Wrong owner
846
- end
826
+ const luaScript = `
827
+ local lockKey = KEYS[1]
828
+ local expectedRequestId = ARGV[1]
829
+ local expectedVersion = ARGV[2]
830
+
831
+ local lockData = redis.call('GET', lockKey)
832
+ if not lockData then
833
+ return 0 -- Lock doesn't exist
834
+ end
835
+
836
+ local lockInfo = cjson.decode(lockData)
837
+ if lockInfo.acquiredBy == expectedRequestId then
838
+ if not expectedVersion or expectedVersion == '' or tostring(lockInfo.lockVersion) == expectedVersion then
839
+ redis.call('DEL', lockKey)
840
+ return 1 -- Successfully released
841
+ else
842
+ return -2 -- Version mismatch
843
+ end
844
+ else
845
+ return -1 -- Wrong owner
846
+ end
847
847
  `;
848
848
  const result = await redis_1.default.eval(luaScript, 1, lockKey, requestId, lockVersion ? lockVersion.toString() : '');
849
849
  if (result === 1) {
@@ -231,25 +231,25 @@ async function performInitialization() {
231
231
  const connectionLine = isConnectionError
232
232
  ? '║ 🔌 CONNECTION REFUSED - IDP appears to be down ║\n║ ║\n'
233
233
  : '';
234
- console.error(`
235
- ╔══════════════════════════════════════════════════════════════╗
236
- ║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║
237
- ║ ║
238
- ║ The app cannot start without a valid auth signing secret. ║
239
- ║ This should be fetched from IDP at startup. ║
240
- ║ ║
241
- ${connectionLine}║ Possible causes: ║
242
- ║ • IDP is not running or unreachable ║
243
- ║ • CLIENT_ID is not registered in IDP ║
244
- ║ • IDP_URL is incorrect ║
245
- ║ • Network connectivity issue ║
246
- ║ ║
247
- ║ Current config: ║
248
- ║ • IDP_URL: ${idpUrl}║
249
- ║ • CLIENT_ID: ${clientId}║
250
- ╚══════════════════════════════════════════════════════════════╝
251
-
252
- [STARTUP] Error: ${errorMsg}
234
+ console.error(`
235
+ ╔══════════════════════════════════════════════════════════════╗
236
+ ║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║
237
+ ║ ║
238
+ ║ The app cannot start without a valid auth signing secret. ║
239
+ ║ This should be fetched from IDP at startup. ║
240
+ ║ ║
241
+ ${connectionLine}║ Possible causes: ║
242
+ ║ • IDP is not running or unreachable ║
243
+ ║ • CLIENT_ID is not registered in IDP ║
244
+ ║ • IDP_URL is incorrect ║
245
+ ║ • Network connectivity issue ║
246
+ ║ ║
247
+ ║ Current config: ║
248
+ ║ • IDP_URL: ${idpUrl}║
249
+ ║ • CLIENT_ID: ${clientId}║
250
+ ╚══════════════════════════════════════════════════════════════╝
251
+
252
+ [STARTUP] Error: ${errorMsg}
253
253
  `);
254
254
  // Re-throw so callers know initialization failed
255
255
  throw lastInitError;
@@ -198,16 +198,17 @@ function serviceUnavailableResponse(request, check = 'viability', unavailablePag
198
198
  // callback, and no Set-Cookie (nothing here touches the session). No redirect, so no 404 is possible.
199
199
  // `unavailablePage` is accepted only as a PLAIN path ([A-Za-z0-9_-/]; no query, fragment, %-encoding, dot segment or
200
200
  // scheme), so the self-check below compares like with like and a mis-set value falls back to the inline page (Scout 74668).
201
- // The named page applies to the revocation hold ONLY (BAPert 74648: the viability-failure arm is untouched). If the
201
+ // PAY-2345: the viability-FAILURE arm (500 / timeout / breaker) takes the same path, so an app with no page of its own
202
+ // gets the inline 503 instead of a 404 during an outage. The two arms differ only in `check` (JSON body for /api/*). If the
202
203
  // request is already FOR the named page (an app that did not register it as public), answer inline: redirecting to
203
204
  // it again would loop with a longer callbackUrl each time (QAPert 74651).
204
- const named = check === 'revocation' && unavailablePage && /^\/[A-Za-z0-9_\-\/]*$/.test(unavailablePage) && !unavailablePage.includes('//') ? unavailablePage : null;
205
- if (check === 'revocation' && (!named || pathname.replace(/\/+$/, '') === named.replace(/\/+$/, ''))) {
205
+ const named = unavailablePage && /^\/[A-Za-z0-9_\-\/]*$/.test(unavailablePage) && !unavailablePage.includes('//') ? unavailablePage : null;
206
+ if (!named || pathname.replace(/\/+$/, '') === named.replace(/\/+$/, '')) {
206
207
  const href = callback.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
207
208
  return new server_1.NextResponse('<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Temporarily unavailable</title>' +
208
209
  '<main role="status" style="font-family:system-ui,sans-serif;max-width:28rem;margin:20vh auto;padding:0 1rem;text-align:center">' +
209
210
  '<h1 style="font-size:1.25rem">Temporarily unavailable</h1>' +
210
- '<p>We could not check your session just now. You are still signed in.</p>' +
211
+ '<p>' + (check === 'revocation' ? 'We could not check your session just now. You are still signed in.' : 'We could not reach the sign-in service just now. Please try again.') + '</p>' +
211
212
  '<p><a href="' + href + '">Try again</a></p></main>', { status: 503, headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store', 'retry-after': '30' } });
212
213
  }
213
214
  const target = new URL(named || '/service-unavailable', request.url);
@@ -766,7 +767,7 @@ async function handleRefresh(request, safeCallback, opts) {
766
767
  log.warn('Circuit breaker preventing refresh attempt');
767
768
  // PAY-2147: a breaker that merely declines to TRY a refresh has learned nothing about the session; wiping the
768
769
  // cookies here logged out every user on the first request after a blip (HALF_OPEN refuses all but one probe).
769
- return serviceUnavailableResponse(request);
770
+ return serviceUnavailableResponse(request, 'viability', opts.unavailablePage);
770
771
  }
771
772
  try {
772
773
  const baseUrl = (0, internal_api_url_1.getInternalApiUrl)(request);
@@ -152,34 +152,34 @@ function LoginForm({ altAuthSlot }) {
152
152
  if (!forceShowForm && !shownError && (loading || (status === 'loading' && !session))) {
153
153
  return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-col items-center justify-center min-h-screen ${colors.background}`, children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin h-6 w-6", style: { color: buttonColor }, viewBox: "0 0 24 24", fill: "none", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })] }), (0, jsx_runtime_1.jsx)("p", { className: `mt-4 text-sm ${colors.muted}`, children: status === 'loading' ? 'Loading...' : 'Authenticating...' })] }));
154
154
  }
155
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: `
156
- .themed-input:focus {
157
- outline: none;
158
- border-color: ${buttonColor} !important;
159
- box-shadow: 0 0 0 2px ${buttonColor}40 !important;
160
- }
161
- .themed-button {
162
- background-color: ${buttonColor} !important;
163
- }
164
- .themed-button:hover:not(:disabled) {
165
- background-color: ${buttonHoverColor} !important;
166
- }
167
- .themed-link {
168
- color: ${buttonColor} !important;
169
- }
170
- .themed-link:hover {
171
- color: ${buttonHoverColor} !important;
172
- }
173
- .themed-status-submitting {
174
- background-color: ${buttonColor}1A !important;
175
- border-color: ${buttonColor}33 !important;
176
- }
177
- .themed-status-submitting-text {
178
- color: ${buttonColor} !important;
179
- }
180
- .themed-status-submitting svg {
181
- color: ${buttonColor} !important;
182
- }
155
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: `
156
+ .themed-input:focus {
157
+ outline: none;
158
+ border-color: ${buttonColor} !important;
159
+ box-shadow: 0 0 0 2px ${buttonColor}40 !important;
160
+ }
161
+ .themed-button {
162
+ background-color: ${buttonColor} !important;
163
+ }
164
+ .themed-button:hover:not(:disabled) {
165
+ background-color: ${buttonHoverColor} !important;
166
+ }
167
+ .themed-link {
168
+ color: ${buttonColor} !important;
169
+ }
170
+ .themed-link:hover {
171
+ color: ${buttonHoverColor} !important;
172
+ }
173
+ .themed-status-submitting {
174
+ background-color: ${buttonColor}1A !important;
175
+ border-color: ${buttonColor}33 !important;
176
+ }
177
+ .themed-status-submitting-text {
178
+ color: ${buttonColor} !important;
179
+ }
180
+ .themed-status-submitting svg {
181
+ color: ${buttonColor} !important;
182
+ }
183
183
  ` } }), (0, jsx_runtime_1.jsx)("div", { className: `min-h-screen flex items-center justify-center px-4 ${colors.background}`, children: (0, jsx_runtime_1.jsx)("div", { className: "max-w-md w-full", children: (0, jsx_runtime_1.jsxs)("div", { className: `rounded-2xl shadow-xl p-8 ${colors.card} ${colors.border} text-gray-800`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-3xl font-bold mb-2", style: { color: 'inherit' }, children: branding.tagline || 'Welcome Back' }), (0, jsx_runtime_1.jsx)("p", { className: colors.muted, children: "Sign in to your account" })] }), shownError && ((0, jsx_runtime_1.jsx)("div", { role: "alert", "data-testid": "login-error", className: "mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700", children: shownError })), googleEnabled && ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => void handleGoogleSignIn(), disabled: googleLoading || isSubmitting, "data-testid": "google-sign-in", className: "w-full px-4 py-3 mb-6 rounded-lg border border-gray-300 font-semibold transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50", style: { backgroundColor: 'white', color: '#1f2937' }, children: [googleLoading ? ((0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin w-5 h-5", fill: "none", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })] })) : ((0, jsx_runtime_1.jsxs)("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "currentColor", children: [(0, jsx_runtime_1.jsx)("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }), (0, jsx_runtime_1.jsx)("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })] })), googleLoading ? 'Redirecting to Google…' : 'Continue with Google'] }) })), altAuthSlot && (0, jsx_runtime_1.jsx)("div", { className: "mt-4", children: altAuthSlot }), (0, jsx_runtime_1.jsx)("div", { className: `mt-6 text-center text-sm ${colors.muted}`, children: (0, jsx_runtime_1.jsx)("a", { href: "/account-auth/recovery", className: "themed-link hover:underline", children: "Start account recovery" }) })] }) }) })] }));
184
184
  }
185
185
  function LoginPageFallback() {