@payez/next-mvp 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +782 -0
- package/dist/api/auth-handler.d.ts +67 -0
- package/dist/api/auth-handler.js +397 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +19 -0
- package/dist/api-handlers/account/change-password.d.ts +9 -0
- package/dist/api-handlers/account/change-password.js +112 -0
- package/dist/api-handlers/account/masked-info.d.ts +2 -0
- package/dist/api-handlers/account/masked-info.js +41 -0
- package/dist/api-handlers/account/profile.d.ts +3 -0
- package/dist/api-handlers/account/profile.js +63 -0
- package/dist/api-handlers/account/recovery/initiate.d.ts +2 -0
- package/dist/api-handlers/account/recovery/initiate.js +26 -0
- package/dist/api-handlers/account/recovery/send-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/send-code.js +28 -0
- package/dist/api-handlers/account/recovery/verify-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/verify-code.js +28 -0
- package/dist/api-handlers/account/reset-password.d.ts +2 -0
- package/dist/api-handlers/account/reset-password.js +26 -0
- package/dist/api-handlers/account/send-code.d.ts +24 -0
- package/dist/api-handlers/account/send-code.js +60 -0
- package/dist/api-handlers/account/update-phone.d.ts +27 -0
- package/dist/api-handlers/account/update-phone.js +64 -0
- package/dist/api-handlers/account/validate-password.d.ts +17 -0
- package/dist/api-handlers/account/validate-password.js +81 -0
- package/dist/api-handlers/account/verify-email.d.ts +26 -0
- package/dist/api-handlers/account/verify-email.js +106 -0
- package/dist/api-handlers/account/verify-sms.d.ts +26 -0
- package/dist/api-handlers/account/verify-sms.js +106 -0
- package/dist/api-handlers/admin/analytics.d.ts +20 -0
- package/dist/api-handlers/admin/analytics.js +379 -0
- package/dist/api-handlers/admin/audit.d.ts +20 -0
- package/dist/api-handlers/admin/audit.js +214 -0
- package/dist/api-handlers/admin/index.d.ts +21 -0
- package/dist/api-handlers/admin/index.js +41 -0
- package/dist/api-handlers/admin/redis-sessions.d.ts +36 -0
- package/dist/api-handlers/admin/redis-sessions.js +204 -0
- package/dist/api-handlers/admin/sessions.d.ts +21 -0
- package/dist/api-handlers/admin/sessions.js +284 -0
- package/dist/api-handlers/admin/site-logs.d.ts +46 -0
- package/dist/api-handlers/admin/site-logs.js +318 -0
- package/dist/api-handlers/admin/users.d.ts +20 -0
- package/dist/api-handlers/admin/users.js +222 -0
- package/dist/api-handlers/admin/vibe-data.d.ts +80 -0
- package/dist/api-handlers/admin/vibe-data.js +268 -0
- package/dist/api-handlers/anon/preferences.d.ts +37 -0
- package/dist/api-handlers/anon/preferences.js +96 -0
- package/dist/api-handlers/auth/jwks.d.ts +2 -0
- package/dist/api-handlers/auth/jwks.js +24 -0
- package/dist/api-handlers/auth/login.d.ts +42 -0
- package/dist/api-handlers/auth/login.js +178 -0
- package/dist/api-handlers/auth/refresh.d.ts +74 -0
- package/dist/api-handlers/auth/refresh.js +635 -0
- package/dist/api-handlers/auth/signout.d.ts +37 -0
- package/dist/api-handlers/auth/signout.js +187 -0
- package/dist/api-handlers/auth/status.d.ts +8 -0
- package/dist/api-handlers/auth/status.js +26 -0
- package/dist/api-handlers/auth/update-session.d.ts +37 -0
- package/dist/api-handlers/auth/update-session.js +95 -0
- package/dist/api-handlers/auth/validate.d.ts +6 -0
- package/dist/api-handlers/auth/validate.js +43 -0
- package/dist/api-handlers/auth/verify-code.d.ts +43 -0
- package/dist/api-handlers/auth/verify-code.js +94 -0
- package/dist/api-handlers/session/refresh-viability.d.ts +14 -0
- package/dist/api-handlers/session/refresh-viability.js +39 -0
- package/dist/api-handlers/session/viability.d.ts +13 -0
- package/dist/api-handlers/session/viability.js +146 -0
- package/dist/api-handlers/test/force-expire.d.ts +23 -0
- package/dist/api-handlers/test/force-expire.js +65 -0
- package/dist/auth/auth-decision.d.ts +39 -0
- package/dist/auth/auth-decision.js +182 -0
- package/dist/auth/auth-options.d.ts +57 -0
- package/dist/auth/auth-options.js +213 -0
- package/dist/auth/callbacks/index.d.ts +6 -0
- package/dist/auth/callbacks/index.js +12 -0
- package/dist/auth/callbacks/jwt.d.ts +45 -0
- package/dist/auth/callbacks/jwt.js +305 -0
- package/dist/auth/callbacks/session.d.ts +60 -0
- package/dist/auth/callbacks/session.js +170 -0
- package/dist/auth/callbacks/signin.d.ts +23 -0
- package/dist/auth/callbacks/signin.js +44 -0
- package/dist/auth/events/index.d.ts +4 -0
- package/dist/auth/events/index.js +8 -0
- package/dist/auth/events/signout.d.ts +17 -0
- package/dist/auth/events/signout.js +32 -0
- package/dist/auth/providers/credentials.d.ts +32 -0
- package/dist/auth/providers/credentials.js +223 -0
- package/dist/auth/providers/index.d.ts +5 -0
- package/dist/auth/providers/index.js +21 -0
- package/dist/auth/providers/oauth.d.ts +26 -0
- package/dist/auth/providers/oauth.js +105 -0
- package/dist/auth/route-config.d.ts +66 -0
- package/dist/auth/route-config.js +190 -0
- package/dist/auth/types/auth-types.d.ts +417 -0
- package/dist/auth/types/auth-types.js +53 -0
- package/dist/auth/types/index.d.ts +6 -0
- package/dist/auth/types/index.js +22 -0
- package/dist/auth/unauthenticated-routes.d.ts +1 -0
- package/dist/auth/unauthenticated-routes.js +19 -0
- package/dist/auth/utils/idp-client.d.ts +94 -0
- package/dist/auth/utils/idp-client.js +383 -0
- package/dist/auth/utils/index.d.ts +5 -0
- package/dist/auth/utils/index.js +21 -0
- package/dist/auth/utils/token-utils.d.ts +84 -0
- package/dist/auth/utils/token-utils.js +219 -0
- package/dist/client/AuthContext.d.ts +19 -0
- package/dist/client/AuthContext.js +112 -0
- package/dist/client/fetch-with-auth.d.ts +11 -0
- package/dist/client/fetch-with-auth.js +44 -0
- package/dist/client/fetchWithSession.d.ts +3 -0
- package/dist/client/fetchWithSession.js +24 -0
- package/dist/client/index.d.ts +9 -0
- package/dist/client/index.js +20 -0
- package/dist/client/useAnonSession.d.ts +36 -0
- package/dist/client/useAnonSession.js +99 -0
- package/dist/components/SessionSync.d.ts +13 -0
- package/dist/components/SessionSync.js +119 -0
- package/dist/components/SignalRHealthCheck.d.ts +10 -0
- package/dist/components/SignalRHealthCheck.js +97 -0
- package/dist/components/account/UserAvatarMenu.d.ts +20 -0
- package/dist/components/account/UserAvatarMenu.js +80 -0
- package/dist/components/account/index.d.ts +7 -0
- package/dist/components/account/index.js +10 -0
- package/dist/components/admin/AlertSettingsTab.d.ts +48 -0
- package/dist/components/admin/AlertSettingsTab.js +351 -0
- package/dist/components/admin/AnalyticsTab.d.ts +22 -0
- package/dist/components/admin/AnalyticsTab.js +167 -0
- package/dist/components/admin/DataBrowserTab.d.ts +19 -0
- package/dist/components/admin/DataBrowserTab.js +252 -0
- package/dist/components/admin/LoggingSettingsTab.d.ts +73 -0
- package/dist/components/admin/LoggingSettingsTab.js +339 -0
- package/dist/components/admin/SessionsTab.d.ts +37 -0
- package/dist/components/admin/SessionsTab.js +165 -0
- package/dist/components/admin/StatsTab.d.ts +53 -0
- package/dist/components/admin/StatsTab.js +161 -0
- package/dist/components/admin/VibeAdminContext.d.ts +32 -0
- package/dist/components/admin/VibeAdminContext.js +38 -0
- package/dist/components/admin/VibeAdminLayout.d.ts +11 -0
- package/dist/components/admin/VibeAdminLayout.js +69 -0
- package/dist/components/admin/index.d.ts +29 -0
- package/dist/components/admin/index.js +44 -0
- package/dist/components/auth/FederatedAuthSection.d.ts +8 -0
- package/dist/components/auth/FederatedAuthSection.js +45 -0
- package/dist/components/auth/ModeAwareLoginPage.d.ts +10 -0
- package/dist/components/auth/ModeAwareLoginPage.js +42 -0
- package/dist/components/auth/ModeAwareSignupPage.d.ts +9 -0
- package/dist/components/auth/ModeAwareSignupPage.js +78 -0
- package/dist/components/auth/TraditionalAuthSection.d.ts +14 -0
- package/dist/components/auth/TraditionalAuthSection.js +20 -0
- package/dist/components/recovery/CompleteStep.d.ts +5 -0
- package/dist/components/recovery/CompleteStep.js +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.d.ts +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.js +20 -0
- package/dist/components/recovery/SelectMethodStep.d.ts +8 -0
- package/dist/components/recovery/SelectMethodStep.js +8 -0
- package/dist/components/recovery/SetPasswordStep.d.ts +6 -0
- package/dist/components/recovery/SetPasswordStep.js +20 -0
- package/dist/components/recovery/VerifyCodeStep.d.ts +10 -0
- package/dist/components/recovery/VerifyCodeStep.js +24 -0
- package/dist/components/reserved/ReservedRecoveryWarning.d.ts +38 -0
- package/dist/components/reserved/ReservedRecoveryWarning.js +92 -0
- package/dist/components/reserved/ReservedStatusBox.d.ts +30 -0
- package/dist/components/reserved/ReservedStatusBox.js +71 -0
- package/dist/components/ui/BetaBadge.d.ts +29 -0
- package/dist/components/ui/BetaBadge.js +38 -0
- package/dist/components/ui/Footer.d.ts +37 -0
- package/dist/components/ui/Footer.js +41 -0
- package/dist/config/env.d.ts +66 -0
- package/dist/config/env.js +57 -0
- package/dist/config/logger.d.ts +57 -0
- package/dist/config/logger.js +73 -0
- package/dist/config/logging-config.d.ts +30 -0
- package/dist/config/logging-config.js +122 -0
- package/dist/config/unauthenticated-routes.d.ts +17 -0
- package/dist/config/unauthenticated-routes.js +24 -0
- package/dist/config/vibe-log-transport.d.ts +79 -0
- package/dist/config/vibe-log-transport.js +203 -0
- package/dist/edge/internal-api-url.d.ts +53 -0
- package/dist/edge/internal-api-url.js +63 -0
- package/dist/edge/middleware.d.ts +14 -0
- package/dist/edge/middleware.js +32 -0
- package/dist/hooks/useAuth.d.ts +23 -0
- package/dist/hooks/useAuth.js +81 -0
- package/dist/hooks/useAuthSettings.d.ts +59 -0
- package/dist/hooks/useAuthSettings.js +93 -0
- package/dist/hooks/useAvailableProviders.d.ts +45 -0
- package/dist/hooks/useAvailableProviders.js +108 -0
- package/dist/hooks/usePasswordValidation.d.ts +27 -0
- package/dist/hooks/usePasswordValidation.js +102 -0
- package/dist/hooks/useProfile.d.ts +15 -0
- package/dist/hooks/useProfile.js +59 -0
- package/dist/hooks/usePublicAuthSettings.d.ts +56 -0
- package/dist/hooks/usePublicAuthSettings.js +131 -0
- package/dist/hooks/useSessionExpiration.d.ts +57 -0
- package/dist/hooks/useSessionExpiration.js +72 -0
- package/dist/hooks/useViabilitySession.d.ts +75 -0
- package/dist/hooks/useViabilitySession.js +268 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +54 -0
- package/dist/lib/anon-session.d.ts +74 -0
- package/dist/lib/anon-session.js +169 -0
- package/dist/lib/api-handler.d.ts +123 -0
- package/dist/lib/api-handler.js +478 -0
- package/dist/lib/app-slug.d.ts +95 -0
- package/dist/lib/app-slug.js +172 -0
- package/dist/lib/demo-mode.d.ts +6 -0
- package/dist/lib/demo-mode.js +16 -0
- package/dist/lib/geolocation.d.ts +64 -0
- package/dist/lib/geolocation.js +235 -0
- package/dist/lib/idp-client-config.d.ts +75 -0
- package/dist/lib/idp-client-config.js +351 -0
- package/dist/lib/idp-fetch.d.ts +14 -0
- package/dist/lib/idp-fetch.js +91 -0
- package/dist/lib/internal-api.d.ts +87 -0
- package/dist/lib/internal-api.js +122 -0
- package/dist/lib/jwt-decode-client.d.ts +10 -0
- package/dist/lib/jwt-decode-client.js +46 -0
- package/dist/lib/jwt-decode.d.ts +48 -0
- package/dist/lib/jwt-decode.js +57 -0
- package/dist/lib/nextauth-secret.d.ts +10 -0
- package/dist/lib/nextauth-secret.js +104 -0
- package/dist/lib/rate-limit-service.d.ts +23 -0
- package/dist/lib/rate-limit-service.js +6 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.js +28 -0
- package/dist/lib/refresh-token-validator.d.ts +13 -0
- package/dist/lib/refresh-token-validator.js +117 -0
- package/dist/lib/roles.d.ts +145 -0
- package/dist/lib/roles.js +168 -0
- package/dist/lib/secret-validation.d.ts +4 -0
- package/dist/lib/secret-validation.js +14 -0
- package/dist/lib/session-store.d.ts +166 -0
- package/dist/lib/session-store.js +537 -0
- package/dist/lib/session.d.ts +21 -0
- package/dist/lib/session.js +26 -0
- package/dist/lib/site-logger.d.ts +214 -0
- package/dist/lib/site-logger.js +210 -0
- package/dist/lib/standardized-client-api.d.ts +161 -0
- package/dist/lib/standardized-client-api.js +786 -0
- package/dist/lib/startup-init.d.ts +40 -0
- package/dist/lib/startup-init.js +261 -0
- package/dist/lib/test-aware-get-token.d.ts +2 -0
- package/dist/lib/test-aware-get-token.js +81 -0
- package/dist/lib/token-expiry.d.ts +14 -0
- package/dist/lib/token-expiry.js +39 -0
- package/dist/lib/token-lifecycle.d.ts +52 -0
- package/dist/lib/token-lifecycle.js +398 -0
- package/dist/lib/types/api-responses.d.ts +128 -0
- package/dist/lib/types/api-responses.js +171 -0
- package/dist/lib/user-agent-parser.d.ts +50 -0
- package/dist/lib/user-agent-parser.js +220 -0
- package/dist/logging/api/admin-analytics.d.ts +3 -0
- package/dist/logging/api/admin-analytics.js +45 -0
- package/dist/logging/api/audit-log.d.ts +3 -0
- package/dist/logging/api/audit-log.js +52 -0
- package/dist/logging/components/AdminAnalyticsLayout.d.ts +10 -0
- package/dist/logging/components/AdminAnalyticsLayout.js +11 -0
- package/dist/logging/components/AuditLogViewer.d.ts +7 -0
- package/dist/logging/components/AuditLogViewer.js +51 -0
- package/dist/logging/components/ErrorMetricsCard.d.ts +7 -0
- package/dist/logging/components/ErrorMetricsCard.js +16 -0
- package/dist/logging/components/HealthMetricsCard.d.ts +7 -0
- package/dist/logging/components/HealthMetricsCard.js +19 -0
- package/dist/logging/hooks/useAdminAnalytics.d.ts +24 -0
- package/dist/logging/hooks/useAdminAnalytics.js +22 -0
- package/dist/logging/hooks/useAuditLog.d.ts +6 -0
- package/dist/logging/hooks/useAuditLog.js +25 -0
- package/dist/logging/hooks/useErrorMetrics.d.ts +6 -0
- package/dist/logging/hooks/useErrorMetrics.js +38 -0
- package/dist/logging/hooks/useHealthMetrics.d.ts +6 -0
- package/dist/logging/hooks/useHealthMetrics.js +41 -0
- package/dist/logging/index.d.ts +11 -0
- package/dist/logging/index.js +40 -0
- package/dist/logging/types/analytics.d.ts +68 -0
- package/dist/logging/types/analytics.js +3 -0
- package/dist/logging/types/audit.d.ts +29 -0
- package/dist/logging/types/audit.js +2 -0
- package/dist/logging/types/index.d.ts +2 -0
- package/dist/logging/types/index.js +19 -0
- package/dist/middleware/auth-decision.d.ts +33 -0
- package/dist/middleware/auth-decision.js +65 -0
- package/dist/middleware/create-middleware.d.ts +100 -0
- package/dist/middleware/create-middleware.js +445 -0
- package/dist/middleware/rbac-check.d.ts +44 -0
- package/dist/middleware/rbac-check.js +191 -0
- package/dist/middleware/twofa-presets.d.ts +134 -0
- package/dist/middleware/twofa-presets.js +175 -0
- package/dist/models/DecodedAccessToken.d.ts +17 -0
- package/dist/models/DecodedAccessToken.js +2 -0
- package/dist/models/SessionModel.d.ts +122 -0
- package/dist/models/SessionModel.js +136 -0
- package/dist/pages/admin-login/page.d.ts +31 -0
- package/dist/pages/admin-login/page.js +83 -0
- package/dist/pages/admin-roles/RolesAdminPage.d.ts +15 -0
- package/dist/pages/admin-roles/RolesAdminPage.js +78 -0
- package/dist/pages/admin-roles/index.d.ts +8 -0
- package/dist/pages/admin-roles/index.js +15 -0
- package/dist/pages/admin-roles/modals.d.ts +72 -0
- package/dist/pages/admin-roles/modals.js +154 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.d.ts +79 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.js +177 -0
- package/dist/pages/client-admin/index.d.ts +32 -0
- package/dist/pages/client-admin/index.js +37 -0
- package/dist/pages/login/page.d.ts +22 -0
- package/dist/pages/login/page.js +239 -0
- package/dist/pages/profile/EnhancedProfilePage.d.ts +13 -0
- package/dist/pages/profile/EnhancedProfilePage.js +150 -0
- package/dist/pages/profile/index.d.ts +8 -0
- package/dist/pages/profile/index.js +16 -0
- package/dist/pages/profile/page.d.ts +19 -0
- package/dist/pages/profile/page.js +47 -0
- package/dist/pages/profile/profile-patch.d.ts +1 -0
- package/dist/pages/profile/profile-patch.js +281 -0
- package/dist/pages/recovery/page.d.ts +1 -0
- package/dist/pages/recovery/page.js +142 -0
- package/dist/pages/roles/MyRolesPage.d.ts +24 -0
- package/dist/pages/roles/MyRolesPage.js +71 -0
- package/dist/pages/roles/components.d.ts +63 -0
- package/dist/pages/roles/components.js +108 -0
- package/dist/pages/roles/index.d.ts +8 -0
- package/dist/pages/roles/index.js +19 -0
- package/dist/pages/security/EnhancedSecurityPage.d.ts +14 -0
- package/dist/pages/security/EnhancedSecurityPage.js +248 -0
- package/dist/pages/security/index.d.ts +8 -0
- package/dist/pages/security/index.js +16 -0
- package/dist/pages/security/page.d.ts +21 -0
- package/dist/pages/security/page.js +212 -0
- package/dist/pages/security/security-patch.d.ts +1 -0
- package/dist/pages/security/security-patch.js +302 -0
- package/dist/pages/settings/EnhancedSettingsPage.d.ts +46 -0
- package/dist/pages/settings/EnhancedSettingsPage.js +231 -0
- package/dist/pages/settings/index.d.ts +8 -0
- package/dist/pages/settings/index.js +16 -0
- package/dist/pages/settings/page.d.ts +7 -0
- package/dist/pages/settings/page.js +26 -0
- package/dist/pages/showcase/ShowcasePage.d.ts +13 -0
- package/dist/pages/showcase/ShowcasePage.js +140 -0
- package/dist/pages/showcase/index.d.ts +12 -0
- package/dist/pages/showcase/index.js +17 -0
- package/dist/pages/test-env/EmergencyLogoutPage.d.ts +14 -0
- package/dist/pages/test-env/EmergencyLogoutPage.js +98 -0
- package/dist/pages/test-env/JwtInspectPage.d.ts +14 -0
- package/dist/pages/test-env/JwtInspectPage.js +114 -0
- package/dist/pages/test-env/RefreshTokenPage.d.ts +15 -0
- package/dist/pages/test-env/RefreshTokenPage.js +91 -0
- package/dist/pages/test-env/TestEnvPage.d.ts +13 -0
- package/dist/pages/test-env/TestEnvPage.js +49 -0
- package/dist/pages/test-env/index.d.ts +24 -0
- package/dist/pages/test-env/index.js +32 -0
- package/dist/pages/verify-code/page.d.ts +30 -0
- package/dist/pages/verify-code/page.js +408 -0
- package/dist/routes/account/index.d.ts +28 -0
- package/dist/routes/account/index.js +71 -0
- package/dist/routes/account/masked-info.d.ts +33 -0
- package/dist/routes/account/masked-info.js +39 -0
- package/dist/routes/account/send-code.d.ts +37 -0
- package/dist/routes/account/send-code.js +42 -0
- package/dist/routes/account/update-phone.d.ts +13 -0
- package/dist/routes/account/update-phone.js +17 -0
- package/dist/routes/account/verify-email.d.ts +38 -0
- package/dist/routes/account/verify-email.js +43 -0
- package/dist/routes/account/verify-sms.d.ts +38 -0
- package/dist/routes/account/verify-sms.js +43 -0
- package/dist/routes/auth/index.d.ts +19 -0
- package/dist/routes/auth/index.js +64 -0
- package/dist/routes/auth/logout.d.ts +31 -0
- package/dist/routes/auth/logout.js +113 -0
- package/dist/routes/auth/nextauth.d.ts +19 -0
- package/dist/routes/auth/nextauth.js +72 -0
- package/dist/routes/auth/refresh.d.ts +30 -0
- package/dist/routes/auth/refresh.js +51 -0
- package/dist/routes/auth/session.d.ts +72 -0
- package/dist/routes/auth/session.js +180 -0
- package/dist/routes/auth/settings.d.ts +25 -0
- package/dist/routes/auth/settings.js +55 -0
- package/dist/routes/auth/viability.d.ts +52 -0
- package/dist/routes/auth/viability.js +201 -0
- package/dist/routes/index.d.ts +12 -0
- package/dist/routes/index.js +54 -0
- package/dist/routes/session/index.d.ts +6 -0
- package/dist/routes/session/index.js +10 -0
- package/dist/routes/session/refresh-viability.d.ts +16 -0
- package/dist/routes/session/refresh-viability.js +20 -0
- package/dist/services/signalrActivityService.d.ts +44 -0
- package/dist/services/signalrActivityService.js +257 -0
- package/dist/stores/authStore.d.ts +154 -0
- package/dist/stores/authStore.js +1531 -0
- package/dist/theme/ThemeProvider.d.ts +14 -0
- package/dist/theme/ThemeProvider.js +28 -0
- package/dist/theme/default.d.ts +8 -0
- package/dist/theme/default.js +33 -0
- package/dist/theme/index.d.ts +15 -0
- package/dist/theme/index.js +25 -0
- package/dist/theme/types.d.ts +56 -0
- package/dist/theme/types.js +8 -0
- package/dist/theme/useTheme.d.ts +60 -0
- package/dist/theme/useTheme.js +63 -0
- package/dist/theme/utils.d.ts +13 -0
- package/dist/theme/utils.js +39 -0
- package/dist/types/api.d.ts +134 -0
- package/dist/types/api.js +44 -0
- package/dist/types/auth.d.ts +19 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/logging.d.ts +42 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/recovery.d.ts +48 -0
- package/dist/types/recovery.js +2 -0
- package/dist/types/security.d.ts +1 -0
- package/dist/types/security.js +2 -0
- package/dist/utils/api.d.ts +85 -0
- package/dist/utils/api.js +287 -0
- package/dist/utils/circuitBreaker.d.ts +43 -0
- package/dist/utils/circuitBreaker.js +91 -0
- package/dist/utils/error-message.d.ts +1 -0
- package/dist/utils/error-message.js +103 -0
- package/dist/utils/layout/reservedSpace.d.ts +59 -0
- package/dist/utils/layout/reservedSpace.js +102 -0
- package/dist/utils/logout.d.ts +14 -0
- package/dist/utils/logout.js +32 -0
- package/dist/vibe/client.d.ts +261 -0
- package/dist/vibe/client.js +445 -0
- package/dist/vibe/errors.d.ts +83 -0
- package/dist/vibe/errors.js +146 -0
- package/dist/vibe/generic.d.ts +234 -0
- package/dist/vibe/generic.js +369 -0
- package/dist/vibe/hooks/index.d.ts +169 -0
- package/dist/vibe/hooks/index.js +252 -0
- package/dist/vibe/index.d.ts +23 -0
- package/dist/vibe/index.js +67 -0
- package/dist/vibe/sessions.d.ts +161 -0
- package/dist/vibe/sessions.js +391 -0
- package/dist/vibe/types.d.ts +353 -0
- package/dist/vibe/types.js +315 -0
- package/package.json +855 -0
- package/scripts/check-internal-url-usage.sh +73 -0
- package/scripts/dev-broker.ps1 +35 -0
- package/scripts/dev-local.ps1 +45 -0
- package/src/api/auth-handler.ts +550 -0
- package/src/api/index.ts +18 -0
- package/src/api-handlers/account/change-password.ts +145 -0
- package/src/api-handlers/account/masked-info.ts +45 -0
- package/src/api-handlers/account/profile.ts +80 -0
- package/src/api-handlers/account/recovery/initiate.ts +23 -0
- package/src/api-handlers/account/recovery/send-code.ts +25 -0
- package/src/api-handlers/account/recovery/verify-code.ts +25 -0
- package/src/api-handlers/account/reset-password.ts +23 -0
- package/src/api-handlers/account/send-code.ts +76 -0
- package/src/api-handlers/account/update-phone.ts +79 -0
- package/src/api-handlers/account/validate-password.ts +118 -0
- package/src/api-handlers/account/verify-email.ts +125 -0
- package/src/api-handlers/account/verify-sms.ts +125 -0
- package/src/api-handlers/admin/analytics.ts +445 -0
- package/src/api-handlers/admin/audit.ts +225 -0
- package/src/api-handlers/admin/index.ts +59 -0
- package/src/api-handlers/admin/redis-sessions.ts +253 -0
- package/src/api-handlers/admin/sessions.ts +320 -0
- package/src/api-handlers/admin/site-logs.ts +367 -0
- package/src/api-handlers/admin/users.ts +244 -0
- package/src/api-handlers/admin/vibe-data.ts +326 -0
- package/src/api-handlers/anon/preferences.ts +123 -0
- package/src/api-handlers/auth/jwks.ts +20 -0
- package/src/api-handlers/auth/login.ts +240 -0
- package/src/api-handlers/auth/refresh.ts +687 -0
- package/src/api-handlers/auth/signout.ts +212 -0
- package/src/api-handlers/auth/status.ts +23 -0
- package/src/api-handlers/auth/update-session.ts +125 -0
- package/src/api-handlers/auth/validate.ts +44 -0
- package/src/api-handlers/auth/verify-code.ts +129 -0
- package/src/api-handlers/session/refresh-viability.ts +36 -0
- package/src/api-handlers/session/viability.ts +166 -0
- package/src/api-handlers/test/force-expire.ts +67 -0
- package/src/auth/auth-decision.ts +230 -0
- package/src/auth/auth-options.ts +237 -0
- package/src/auth/callbacks/index.ts +7 -0
- package/src/auth/callbacks/jwt.ts +382 -0
- package/src/auth/callbacks/session.ts +243 -0
- package/src/auth/callbacks/signin.ts +56 -0
- package/src/auth/events/index.ts +5 -0
- package/src/auth/events/signout.ts +33 -0
- package/src/auth/providers/credentials.ts +256 -0
- package/src/auth/providers/index.ts +6 -0
- package/src/auth/providers/oauth.ts +114 -0
- package/src/auth/route-config.ts +220 -0
- package/src/auth/types/auth-types.ts +555 -0
- package/src/auth/types/index.ts +7 -0
- package/src/auth/unauthenticated-routes.ts +3 -0
- package/src/auth/utils/idp-client.ts +444 -0
- package/src/auth/utils/index.ts +6 -0
- package/src/auth/utils/token-utils.ts +244 -0
- package/src/client/AuthContext.tsx +140 -0
- package/src/client/fetch-with-auth.ts +48 -0
- package/src/client/fetchWithSession.ts +21 -0
- package/src/client/index.ts +13 -0
- package/src/client/useAnonSession.ts +131 -0
- package/src/components/SessionSync.tsx +137 -0
- package/src/components/SignalRHealthCheck.tsx +131 -0
- package/src/components/account/UserAvatarMenu.tsx +217 -0
- package/src/components/account/index.ts +8 -0
- package/src/components/admin/AlertSettingsTab.tsx +728 -0
- package/src/components/admin/AnalyticsTab.tsx +703 -0
- package/src/components/admin/DataBrowserTab.tsx +505 -0
- package/src/components/admin/LoggingSettingsTab.tsx +665 -0
- package/src/components/admin/SessionsTab.tsx +414 -0
- package/src/components/admin/StatsTab.tsx +379 -0
- package/src/components/admin/VibeAdminContext.tsx +87 -0
- package/src/components/admin/VibeAdminLayout.tsx +185 -0
- package/src/components/admin/index.ts +59 -0
- package/src/components/auth/FederatedAuthSection.tsx +95 -0
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -0
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -0
- package/src/components/auth/TraditionalAuthSection.tsx +99 -0
- package/src/components/recovery/CompleteStep.tsx +36 -0
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -0
- package/src/components/recovery/SelectMethodStep.tsx +73 -0
- package/src/components/recovery/SetPasswordStep.tsx +97 -0
- package/src/components/recovery/VerifyCodeStep.tsx +90 -0
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -0
- package/src/components/reserved/ReservedStatusBox.tsx +118 -0
- package/src/components/ui/BetaBadge.tsx +58 -0
- package/src/components/ui/Footer.tsx +93 -0
- package/src/config/env.ts +57 -0
- package/src/config/logger.ts +62 -0
- package/src/config/logging-config.ts +82 -0
- package/src/config/unauthenticated-routes.ts +19 -0
- package/src/config/vibe-log-transport.ts +250 -0
- package/src/edge/internal-api-url.ts +65 -0
- package/src/edge/middleware.ts +42 -0
- package/src/hooks/useAuth.ts +115 -0
- package/src/hooks/useAuthSettings.ts +97 -0
- package/src/hooks/useAvailableProviders.ts +118 -0
- package/src/hooks/usePasswordValidation.ts +127 -0
- package/src/hooks/useProfile.ts +75 -0
- package/src/hooks/usePublicAuthSettings.ts +149 -0
- package/src/hooks/useSessionExpiration.ts +102 -0
- package/src/hooks/useViabilitySession.ts +335 -0
- package/src/index.ts +63 -0
- package/src/lib/anon-session.ts +213 -0
- package/src/lib/api-handler.ts +625 -0
- package/src/lib/app-slug.ts +178 -0
- package/src/lib/demo-mode.ts +13 -0
- package/src/lib/geolocation.ts +265 -0
- package/src/lib/idp-client-config.ts +442 -0
- package/src/lib/idp-fetch.ts +101 -0
- package/src/lib/internal-api.ts +171 -0
- package/src/lib/jwt-decode-client.ts +45 -0
- package/src/lib/jwt-decode.ts +83 -0
- package/src/lib/nextauth-secret.ts +126 -0
- package/src/lib/rate-limit-service.ts +9 -0
- package/src/lib/redis.ts +27 -0
- package/src/lib/refresh-token-validator.ts +64 -0
- package/src/lib/roles.ts +177 -0
- package/src/lib/secret-validation.ts +8 -0
- package/src/lib/session-store.ts +637 -0
- package/src/lib/session.ts +34 -0
- package/src/lib/site-logger.ts +245 -0
- package/src/lib/standardized-client-api.ts +896 -0
- package/src/lib/startup-init.ts +247 -0
- package/src/lib/test-aware-get-token.ts +30 -0
- package/src/lib/token-expiry.ts +40 -0
- package/src/lib/token-lifecycle.ts +477 -0
- package/src/lib/types/api-responses.ts +336 -0
- package/src/lib/user-agent-parser.ts +252 -0
- package/src/logging/api/admin-analytics.ts +51 -0
- package/src/logging/api/audit-log.ts +53 -0
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -0
- package/src/logging/components/AuditLogViewer.tsx +125 -0
- package/src/logging/components/ErrorMetricsCard.tsx +98 -0
- package/src/logging/components/HealthMetricsCard.tsx +70 -0
- package/src/logging/hooks/useAdminAnalytics.ts +22 -0
- package/src/logging/hooks/useAuditLog.ts +24 -0
- package/src/logging/hooks/useErrorMetrics.ts +40 -0
- package/src/logging/hooks/useHealthMetrics.ts +44 -0
- package/src/logging/index.ts +18 -0
- package/src/logging/types/analytics.ts +81 -0
- package/src/logging/types/audit.ts +31 -0
- package/src/logging/types/index.ts +3 -0
- package/src/middleware/auth-decision.ts +43 -0
- package/src/middleware/create-middleware.ts +626 -0
- package/src/middleware/rbac-check.ts +244 -0
- package/src/middleware/twofa-presets.ts +224 -0
- package/src/models/DecodedAccessToken.ts +17 -0
- package/src/models/SessionModel.ts +258 -0
- package/src/pages/admin-login/page.tsx +229 -0
- package/src/pages/admin-roles/RolesAdminPage.tsx +357 -0
- package/src/pages/admin-roles/index.ts +9 -0
- package/src/pages/admin-roles/modals.tsx +469 -0
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +380 -0
- package/src/pages/client-admin/index.ts +33 -0
- package/src/pages/login/page.tsx +463 -0
- package/src/pages/profile/EnhancedProfilePage.tsx +479 -0
- package/src/pages/profile/index.ts +9 -0
- package/src/pages/profile/page.tsx +166 -0
- package/src/pages/recovery/page.tsx +234 -0
- package/src/pages/roles/MyRolesPage.tsx +211 -0
- package/src/pages/roles/components.tsx +294 -0
- package/src/pages/roles/index.ts +17 -0
- package/src/pages/security/EnhancedSecurityPage.tsx +574 -0
- package/src/pages/security/index.ts +9 -0
- package/src/pages/security/page.tsx +507 -0
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -0
- package/src/pages/settings/index.ts +9 -0
- package/src/pages/settings/page.tsx +47 -0
- package/src/pages/showcase/ShowcasePage.tsx +530 -0
- package/src/pages/showcase/index.ts +13 -0
- package/src/pages/test-env/EmergencyLogoutPage.tsx +179 -0
- package/src/pages/test-env/JwtInspectPage.tsx +418 -0
- package/src/pages/test-env/RefreshTokenPage.tsx +155 -0
- package/src/pages/test-env/TestEnvPage.tsx +116 -0
- package/src/pages/test-env/index.ts +25 -0
- package/src/pages/verify-code/page.tsx +648 -0
- package/src/routes/account/index.ts +32 -0
- package/src/routes/account/masked-info.ts +37 -0
- package/src/routes/account/send-code.ts +40 -0
- package/src/routes/account/update-phone.ts +13 -0
- package/src/routes/account/verify-email.ts +41 -0
- package/src/routes/account/verify-sms.ts +41 -0
- package/src/routes/auth/index.ts +23 -0
- package/src/routes/auth/logout.ts +127 -0
- package/src/routes/auth/nextauth.ts +71 -0
- package/src/routes/auth/refresh.ts +54 -0
- package/src/routes/auth/session.ts +193 -0
- package/src/routes/auth/settings.ts +75 -0
- package/src/routes/auth/viability.ts +220 -0
- package/src/routes/index.ts +18 -0
- package/src/routes/session/index.ts +7 -0
- package/src/routes/session/refresh-viability.ts +17 -0
- package/src/services/signalrActivityService.ts +258 -0
- package/src/stores/authStore.ts +1904 -0
- package/src/templates/instrumentation.ts +41 -0
- package/src/theme/ThemeProvider.tsx +39 -0
- package/src/theme/default.ts +33 -0
- package/src/theme/index.ts +31 -0
- package/src/theme/types.ts +69 -0
- package/src/theme/useTheme.ts +57 -0
- package/src/theme/utils.ts +40 -0
- package/src/types/api.ts +13 -0
- package/src/types/auth.d.ts +15 -0
- package/src/types/auth.ts +22 -0
- package/src/types/logging.ts +11 -0
- package/src/types/next-auth.d.ts +15 -0
- package/src/types/recovery.ts +54 -0
- package/src/types/security.ts +1 -0
- package/src/utils/api.ts +353 -0
- package/src/utils/circuitBreaker.ts +40 -0
- package/src/utils/error-message.ts +108 -0
- package/src/utils/layout/reservedSpace.ts +124 -0
- package/src/utils/logout.ts +30 -0
- package/src/vibe/client.ts +590 -0
- package/src/vibe/errors.ts +185 -0
- package/src/vibe/generic.ts +429 -0
- package/src/vibe/hooks/index.ts +367 -0
- package/src/vibe/index.ts +121 -0
- package/src/vibe/sessions.ts +551 -0
- package/src/vibe/types.ts +577 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced Security Page for @payez/next-mvp
|
|
4
|
+
*
|
|
5
|
+
* Implements BAPert's Member Self-Service spec with:
|
|
6
|
+
* - Password section with change form
|
|
7
|
+
* - Two-factor authentication management
|
|
8
|
+
* - Connected OAuth accounts
|
|
9
|
+
* - Active sessions list
|
|
10
|
+
* - Recent activity log
|
|
11
|
+
* - Danger zone (data export, account deletion)
|
|
12
|
+
*
|
|
13
|
+
* @see docs/specs/MEMBER_SELF_SERVICE_SPEC.md
|
|
14
|
+
*/
|
|
15
|
+
'use client';
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.default = EnhancedSecurityPage;
|
|
51
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
52
|
+
const react_1 = __importStar(require("react"));
|
|
53
|
+
const useProfile_1 = require("../../hooks/useProfile");
|
|
54
|
+
const usePasswordValidation_1 = require("../../hooks/usePasswordValidation");
|
|
55
|
+
const useTheme_1 = require("../../theme/useTheme");
|
|
56
|
+
// Password Strength Meter Component
|
|
57
|
+
function PasswordStrengthMeter({ score, failedRequirements, tip, isDark }) {
|
|
58
|
+
const getColor = (s) => {
|
|
59
|
+
if (s >= 4)
|
|
60
|
+
return 'bg-green-500';
|
|
61
|
+
if (s >= 3)
|
|
62
|
+
return 'bg-yellow-500';
|
|
63
|
+
if (s >= 2)
|
|
64
|
+
return 'bg-orange-500';
|
|
65
|
+
return 'bg-red-500';
|
|
66
|
+
};
|
|
67
|
+
const getLabel = (s) => {
|
|
68
|
+
if (s >= 4)
|
|
69
|
+
return 'Strong';
|
|
70
|
+
if (s >= 3)
|
|
71
|
+
return 'Good';
|
|
72
|
+
if (s >= 2)
|
|
73
|
+
return 'Fair';
|
|
74
|
+
if (s >= 1)
|
|
75
|
+
return 'Weak';
|
|
76
|
+
return 'Very Weak';
|
|
77
|
+
};
|
|
78
|
+
const mutedText = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
79
|
+
const barBg = isDark ? 'bg-slate-600' : 'bg-gray-200';
|
|
80
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: `flex-1 h-2 rounded-full overflow-hidden ${barBg}`, children: (0, jsx_runtime_1.jsx)("div", { className: `h-full transition-all duration-300 ${getColor(score)}`, style: { width: `${Math.min(score * 20, 100)}%` } }) }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs w-16 ${mutedText}`, children: getLabel(score) })] }), failedRequirements.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: `text-xs space-y-1 ${mutedText}`, children: failedRequirements.map((req, i) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-red-400", children: "x" }), " ", req] }, i))) })), tip && (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-blue-400", children: tip })] }));
|
|
81
|
+
}
|
|
82
|
+
// Policy Checklist Component
|
|
83
|
+
function PolicyChecklist({ policy, password, isDark }) {
|
|
84
|
+
const checks = react_1.default.useMemo(() => {
|
|
85
|
+
const list = [];
|
|
86
|
+
const minLen = policy?.min_length || 8;
|
|
87
|
+
list.push({ label: `At least ${minLen} characters`, ok: (password?.length || 0) >= minLen });
|
|
88
|
+
if (policy?.require_uppercase)
|
|
89
|
+
list.push({ label: 'One uppercase letter', ok: /[A-Z]/.test(password || '') });
|
|
90
|
+
if (policy?.require_lowercase)
|
|
91
|
+
list.push({ label: 'One lowercase letter', ok: /[a-z]/.test(password || '') });
|
|
92
|
+
if (policy?.require_digit)
|
|
93
|
+
list.push({ label: 'One digit', ok: /\d/.test(password || '') });
|
|
94
|
+
if (policy?.require_special)
|
|
95
|
+
list.push({ label: 'One special character', ok: /[^A-Za-z0-9]/.test(password || '') });
|
|
96
|
+
return list;
|
|
97
|
+
}, [policy, password]);
|
|
98
|
+
const mutedText = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
99
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "space-y-1 text-xs", children: checks.map((c, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [c.ok ? (0, jsx_runtime_1.jsx)("span", { className: "text-green-400", children: "v" }) : (0, jsx_runtime_1.jsx)("span", { className: mutedText, children: "o" }), (0, jsx_runtime_1.jsx)("span", { className: c.ok ? 'text-green-400' : mutedText, children: c.label })] }, i))) }));
|
|
100
|
+
}
|
|
101
|
+
// Section Card Component
|
|
102
|
+
function SecuritySection({ title, description, children, isDark }) {
|
|
103
|
+
const cardBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
104
|
+
const borderColor = isDark ? 'border-slate-700' : 'border-gray-200';
|
|
105
|
+
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
106
|
+
const textMuted = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
107
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `rounded-lg border ${cardBg} ${borderColor}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: `px-6 py-4 border-b ${borderColor}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `text-lg font-semibold ${textPrimary}`, children: title }), description && (0, jsx_runtime_1.jsx)("p", { className: `text-sm mt-1 ${textMuted}`, children: description })] }), (0, jsx_runtime_1.jsx)("div", { className: "px-6 py-4", children: children })] }));
|
|
108
|
+
}
|
|
109
|
+
// Status Badge Component
|
|
110
|
+
function StatusBadge({ enabled, label, isDark }) {
|
|
111
|
+
const bgClass = enabled
|
|
112
|
+
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
|
|
113
|
+
: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400';
|
|
114
|
+
return ((0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${bgClass}`, children: label || (enabled ? 'Enabled' : 'Not Active') }));
|
|
115
|
+
}
|
|
116
|
+
function formatDate(dateString) {
|
|
117
|
+
if (!dateString)
|
|
118
|
+
return '';
|
|
119
|
+
try {
|
|
120
|
+
return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return dateString;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function formatRelativeTime(dateString) {
|
|
127
|
+
if (!dateString)
|
|
128
|
+
return '';
|
|
129
|
+
try {
|
|
130
|
+
const date = new Date(dateString);
|
|
131
|
+
const now = new Date();
|
|
132
|
+
const diffMs = now.getTime() - date.getTime();
|
|
133
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
134
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
135
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
136
|
+
if (diffMins < 1)
|
|
137
|
+
return 'Just now';
|
|
138
|
+
if (diffMins < 60)
|
|
139
|
+
return `${diffMins}m ago`;
|
|
140
|
+
if (diffHours < 24)
|
|
141
|
+
return `${diffHours}h ago`;
|
|
142
|
+
if (diffDays < 7)
|
|
143
|
+
return `${diffDays}d ago`;
|
|
144
|
+
return formatDate(dateString);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return dateString;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function EnhancedSecurityPage() {
|
|
151
|
+
const { data: profileData, isLoading: isProfileLoading } = (0, useProfile_1.useProfile)();
|
|
152
|
+
const layout = (0, useTheme_1.useLayout)();
|
|
153
|
+
const colors = (0, useTheme_1.useColors)();
|
|
154
|
+
// Security data state (would come from API in production)
|
|
155
|
+
const [securityData, setSecurityData] = (0, react_1.useState)(null);
|
|
156
|
+
const [loadingSecurityData, setLoadingSecurityData] = (0, react_1.useState)(true);
|
|
157
|
+
// Determine dark mode
|
|
158
|
+
const isDark = colors?.background?.includes('slate-9') ||
|
|
159
|
+
colors?.background?.includes('gray-9') ||
|
|
160
|
+
colors?.card?.includes('slate-8');
|
|
161
|
+
// Password form state
|
|
162
|
+
const [formData, setFormData] = (0, react_1.useState)({
|
|
163
|
+
current_password: '',
|
|
164
|
+
new_password: '',
|
|
165
|
+
confirm_password: '',
|
|
166
|
+
});
|
|
167
|
+
const [showPasswords, setShowPasswords] = (0, react_1.useState)({ current: false, new: false, confirm: false });
|
|
168
|
+
// Re-authentication modal state (Security Addendum)
|
|
169
|
+
const [reAuthModal, setReAuthModal] = (0, react_1.useState)({ isOpen: false, action: null });
|
|
170
|
+
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
171
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
172
|
+
const [success, setSuccess] = (0, react_1.useState)('');
|
|
173
|
+
// Password validation
|
|
174
|
+
const { setPassword: validateNewPassword, isValid: newPasswordIsValid, score: newPasswordScore, failedRequirements, tip, policy } = (0, usePasswordValidation_1.usePasswordValidation)({ debounceMs: 250 });
|
|
175
|
+
(0, react_1.useEffect)(() => { validateNewPassword(formData.new_password); }, [formData.new_password, validateNewPassword]);
|
|
176
|
+
// Fetch security data
|
|
177
|
+
const fetchSecurityData = (0, react_1.useCallback)(async () => {
|
|
178
|
+
try {
|
|
179
|
+
setLoadingSecurityData(true);
|
|
180
|
+
const res = await fetch('/api/account/security');
|
|
181
|
+
if (res.ok) {
|
|
182
|
+
const data = await res.json();
|
|
183
|
+
setSecurityData(data);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
// Security data fetch is optional - page still works without it
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
setLoadingSecurityData(false);
|
|
191
|
+
}
|
|
192
|
+
}, []);
|
|
193
|
+
(0, react_1.useEffect)(() => {
|
|
194
|
+
if (profileData)
|
|
195
|
+
fetchSecurityData();
|
|
196
|
+
}, [profileData, fetchSecurityData]);
|
|
197
|
+
const passwordsMatch = formData.confirm_password.length > 0 && formData.new_password === formData.confirm_password;
|
|
198
|
+
const canSubmit = !submitting && newPasswordIsValid && passwordsMatch && formData.current_password.length > 0;
|
|
199
|
+
const handlePasswordSubmit = async (e) => {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
setError('');
|
|
202
|
+
setSuccess('');
|
|
203
|
+
if (!canSubmit)
|
|
204
|
+
return;
|
|
205
|
+
try {
|
|
206
|
+
setSubmitting(true);
|
|
207
|
+
const response = await fetch('/api/account/change-password', {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
headers: { 'Content-Type': 'application/json' },
|
|
210
|
+
body: JSON.stringify(formData),
|
|
211
|
+
});
|
|
212
|
+
const result = await response.json();
|
|
213
|
+
if (!response.ok || !result.success) {
|
|
214
|
+
let errorMsg = result.message || 'Failed to change password';
|
|
215
|
+
if (result.details?.value?.[0]?.message)
|
|
216
|
+
errorMsg = result.details.value[0].message;
|
|
217
|
+
setError(errorMsg);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
setSuccess(result.message || 'Password changed successfully');
|
|
221
|
+
setFormData({ current_password: '', new_password: '', confirm_password: '' });
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
setError(err.message || 'Failed to change password');
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
setSubmitting(false);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
// Theme classes
|
|
231
|
+
const bgClass = isDark ? 'bg-slate-900' : 'bg-gray-50';
|
|
232
|
+
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
233
|
+
const textSecondary = isDark ? 'text-slate-300' : 'text-gray-600';
|
|
234
|
+
const textMuted = isDark ? 'text-slate-400' : 'text-gray-500';
|
|
235
|
+
const cardBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
236
|
+
const borderColor = isDark ? 'border-slate-700' : 'border-gray-200';
|
|
237
|
+
const elevatedBg = isDark ? 'bg-slate-700' : 'bg-gray-100';
|
|
238
|
+
const inputBg = isDark ? 'bg-slate-800' : 'bg-white';
|
|
239
|
+
const inputBorder = isDark ? 'border-slate-600' : 'border-gray-300';
|
|
240
|
+
const inputText = isDark ? 'text-white placeholder-slate-400' : 'text-gray-900 placeholder-gray-400';
|
|
241
|
+
// Loading state
|
|
242
|
+
if (isProfileLoading) {
|
|
243
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgClass} flex items-center justify-center`, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center space-y-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500" }), (0, jsx_runtime_1.jsx)("p", { className: textMuted, children: "Loading security settings..." })] }) }));
|
|
244
|
+
}
|
|
245
|
+
const twoFactorEnabled = profileData?.two_factor_enabled || securityData?.two_factor?.enabled || false;
|
|
246
|
+
const twoFactorMethod = securityData?.two_factor?.method_display || (twoFactorEnabled ? 'SMS' : undefined);
|
|
247
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen ${bgClass}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `${layout?.maxWidth || 'max-w-3xl'} mx-auto ${layout?.padding || 'p-6'} space-y-6`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: `text-2xl font-bold ${textPrimary}`, children: "Security" }), (0, jsx_runtime_1.jsx)("p", { className: `mt-1 ${textMuted}`, children: "Manage your account security settings" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `rounded-lg border ${cardBg} ${borderColor} p-6`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `text-lg font-semibold mb-4 ${textPrimary}`, children: "Security Summary" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondary}`, children: "2FA Status" }), (0, jsx_runtime_1.jsx)(StatusBadge, { enabled: twoFactorEnabled, isDark: isDark }), twoFactorMethod && (0, jsx_runtime_1.jsxs)("p", { className: `text-xs mt-2 ${textMuted}`, children: ["Method: ", twoFactorMethod] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondary}`, children: "Email Status" }), (0, jsx_runtime_1.jsx)(StatusBadge, { enabled: profileData?.email_confirmed || false, label: profileData?.email_confirmed ? 'Verified' : 'Not Verified', isDark: isDark }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-2 ${textMuted} truncate`, children: profileData?.email })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondary}`, children: "Phone Status" }), (0, jsx_runtime_1.jsx)(StatusBadge, { enabled: profileData?.phone_confirmed || false, label: profileData?.phone_confirmed ? 'Verified' : 'Not Verified', isDark: isDark }), profileData?.phone_number && (0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-2 ${textMuted}`, children: profileData.phone_number })] })] })] }), (0, jsx_runtime_1.jsx)(SecuritySection, { title: "Password", description: securityData?.password?.last_changed ? `Last changed ${formatDate(securityData.password.last_changed)}` : undefined, isDark: isDark, children: (0, jsx_runtime_1.jsxs)("form", { onSubmit: handlePasswordSubmit, className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondary}`, children: "Current Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.current ? 'text' : 'password', value: formData.current_password, onChange: e => setFormData({ ...formData, current_password: e.target.value }), className: `w-full px-3 py-2 rounded-md border focus:outline-none focus:ring-2 focus:ring-blue-500 ${inputBg} ${inputBorder} ${inputText}`, placeholder: "Enter current password", disabled: submitting }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, current: !p.current })), className: `absolute inset-y-0 right-2 flex items-center ${textMuted}`, children: showPasswords.current ? 'Hide' : 'Show' })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondary}`, children: "New Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.new ? 'text' : 'password', value: formData.new_password, onChange: e => setFormData({ ...formData, new_password: e.target.value }), className: `w-full px-3 py-2 rounded-md border focus:outline-none focus:ring-2 focus:ring-blue-500 ${inputBg} ${inputBorder} ${inputText}`, placeholder: "Enter new password", disabled: submitting }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, new: !p.new })), className: `absolute inset-y-0 right-2 flex items-center ${textMuted}`, children: showPasswords.new ? 'Hide' : 'Show' })] }), formData.new_password && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2", children: (0, jsx_runtime_1.jsx)(PasswordStrengthMeter, { score: newPasswordScore, failedRequirements: failedRequirements, tip: tip, isDark: isDark }) })), policy && formData.new_password && ((0, jsx_runtime_1.jsx)("div", { className: "mt-3", children: (0, jsx_runtime_1.jsx)(PolicyChecklist, { policy: policy, password: formData.new_password, isDark: isDark }) }))] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondary}`, children: "Confirm New Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.confirm ? 'text' : 'password', value: formData.confirm_password, onChange: e => setFormData({ ...formData, confirm_password: e.target.value }), className: `w-full px-3 py-2 rounded-md border focus:outline-none focus:ring-2 focus:ring-blue-500 ${inputBg} ${inputBorder} ${inputText}`, placeholder: "Confirm new password", disabled: submitting }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, confirm: !p.confirm })), className: `absolute inset-y-0 right-2 flex items-center ${textMuted}`, children: showPasswords.confirm ? 'Hide' : 'Show' })] }), formData.confirm_password && !passwordsMatch && (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-red-400", children: "Passwords do not match" }), formData.confirm_password && passwordsMatch && (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-green-400", children: "Passwords match" })] }), error && (0, jsx_runtime_1.jsx)("div", { className: "bg-red-900/30 border border-red-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-400 text-sm", children: error }) }), success && (0, jsx_runtime_1.jsx)("div", { className: "bg-green-900/30 border border-green-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-green-400 text-sm", children: success }) }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: !canSubmit, className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed", children: submitting ? 'Changing...' : 'Change Password' })] }) }), (0, jsx_runtime_1.jsx)(SecuritySection, { title: "Two-Factor Authentication", description: "Add an extra layer of security to your account", isDark: isDark, children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(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 ${textPrimary}`, children: "Status" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: twoFactorEnabled ? `Enabled via ${twoFactorMethod || 'SMS'}` : 'Not enabled' })] }), (0, jsx_runtime_1.jsx)(StatusBadge, { enabled: twoFactorEnabled, isDark: isDark })] }), twoFactorEnabled ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { className: `px-3 py-1.5 text-sm rounded border ${isDark ? 'border-slate-600 hover:bg-slate-700' : 'border-gray-300 hover:bg-gray-50'} ${textSecondary}`, children: "Change Method" }), (0, jsx_runtime_1.jsx)("button", { className: "px-3 py-1.5 text-sm rounded border border-red-500 text-red-500 hover:bg-red-500/10", children: "Disable 2FA" })] })) : ((0, jsx_runtime_1.jsx)("button", { className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700", children: "Enable 2FA" })), securityData?.two_factor?.backup_codes_remaining !== undefined && ((0, jsx_runtime_1.jsxs)("div", { className: `mt-4 p-3 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textSecondary}`, children: "Backup Codes" }), (0, jsx_runtime_1.jsxs)("p", { className: textMuted, children: ["You have ", securityData.two_factor.backup_codes_remaining, " of 10 backup codes remaining"] }), (0, jsx_runtime_1.jsx)("button", { className: `mt-2 text-sm ${isDark ? 'text-blue-400' : 'text-blue-600'}`, children: "View Codes" })] }))] }) }), (0, jsx_runtime_1.jsxs)(SecuritySection, { title: "Connected Accounts", description: "OAuth providers linked to your account", isDark: isDark, children: [securityData?.connected_accounts && securityData.connected_accounts.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: securityData.connected_accounts.map((account, idx) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-3 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${textPrimary}`, children: account.provider }), account.email && (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: account.email }), account.connected_at && (0, jsx_runtime_1.jsxs)("p", { className: `text-xs ${textMuted}`, children: ["Connected ", formatDate(account.connected_at)] })] }), account.can_disconnect && ((0, jsx_runtime_1.jsx)("button", { className: "text-sm text-red-500 hover:text-red-400", children: "Disconnect" }))] }, idx))) })) : ((0, jsx_runtime_1.jsx)("p", { className: textMuted, children: "No connected accounts" })), (0, jsx_runtime_1.jsx)("button", { className: `mt-4 text-sm ${isDark ? 'text-blue-400' : 'text-blue-600'}`, children: "Connect Another Account" })] }), (0, jsx_runtime_1.jsxs)(SecuritySection, { title: "Active Sessions", description: "Devices currently logged into your account", isDark: isDark, children: [securityData?.active_sessions && securityData.active_sessions.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: securityData.active_sessions.map((session, idx) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-3 rounded-lg ${elevatedBg}`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${textPrimary}`, children: session.device_name || 'Unknown Device' }), session.is_current && (0, jsx_runtime_1.jsx)("span", { className: "px-2 py-0.5 rounded text-xs bg-blue-500 text-white", children: "This Device" })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: session.location || session.ip_address }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${textMuted}`, children: session.is_current ? 'Active now' : `Last active ${formatRelativeTime(session.last_active_at)}` })] }), !session.is_current && ((0, jsx_runtime_1.jsx)("button", { className: "text-sm text-red-500 hover:text-red-400", children: "Revoke" }))] }, idx))) })) : ((0, jsx_runtime_1.jsx)("p", { className: textMuted, children: "Session information not available" })), (0, jsx_runtime_1.jsx)("button", { className: "mt-4 text-sm text-red-500 hover:text-red-400", children: "Sign Out All Other Devices" })] }), (0, jsx_runtime_1.jsxs)(SecuritySection, { title: "Recent Activity", description: "Security events on your account", isDark: isDark, children: [securityData?.recent_activity && securityData.recent_activity.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-2", children: securityData.recent_activity.slice(0, 5).map((activity, idx) => ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between py-2 border-b last:border-b-0 ${borderColor}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: activity.success ? 'text-green-400' : 'text-red-400', children: activity.success ? 'v' : 'x' }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textPrimary}`, children: activity.description }), activity.device && (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${textMuted}`, children: activity.device })] })] }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs ${textMuted}`, children: formatRelativeTime(activity.timestamp) })] }, idx))) })) : ((0, jsx_runtime_1.jsx)("p", { className: textMuted, children: "No recent activity" })), (0, jsx_runtime_1.jsx)("button", { className: `mt-4 text-sm ${isDark ? 'text-blue-400' : 'text-blue-600'}`, children: "View Full History" })] }), (0, jsx_runtime_1.jsx)(SecuritySection, { title: "Danger Zone", description: "Irreversible actions", isDark: isDark, children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-4 rounded-lg border border-yellow-500/50`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${textPrimary}`, children: "Download My Data" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: "Get a copy of all your personal data" })] }), (0, jsx_runtime_1.jsx)("button", { className: "px-4 py-2 text-sm rounded border border-yellow-500 text-yellow-500 hover:bg-yellow-500/10", children: "Request Export" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-4 rounded-lg border border-red-500/50`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `font-medium ${textPrimary}`, children: "Delete Account" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMuted}`, children: "Permanently delete your account and all data" })] }), (0, jsx_runtime_1.jsx)("button", { className: "px-4 py-2 text-sm rounded border border-red-500 text-red-500 hover:bg-red-500/10", children: "Delete Account" })] })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-center", children: (0, jsx_runtime_1.jsx)("a", { href: "/account/profile", className: `text-sm hover:underline ${textMuted}`, children: "Back to Profile" }) })] }) }));
|
|
248
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Page exports
|
|
3
|
+
*
|
|
4
|
+
* - SecurityPage: Basic security display (legacy)
|
|
5
|
+
* - EnhancedSecurityPage: Full-featured security with password, 2FA, sessions, activity, danger zone
|
|
6
|
+
*/
|
|
7
|
+
export { default as SecurityPage } from './page';
|
|
8
|
+
export { default as EnhancedSecurityPage } from './EnhancedSecurityPage';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security Page exports
|
|
4
|
+
*
|
|
5
|
+
* - SecurityPage: Basic security display (legacy)
|
|
6
|
+
* - EnhancedSecurityPage: Full-featured security with password, 2FA, sessions, activity, danger zone
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EnhancedSecurityPage = exports.SecurityPage = void 0;
|
|
13
|
+
var page_1 = require("./page");
|
|
14
|
+
Object.defineProperty(exports, "SecurityPage", { enumerable: true, get: function () { return __importDefault(page_1).default; } });
|
|
15
|
+
var EnhancedSecurityPage_1 = require("./EnhancedSecurityPage");
|
|
16
|
+
Object.defineProperty(exports, "EnhancedSecurityPage", { enumerable: true, get: function () { return __importDefault(EnhancedSecurityPage_1).default; } });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Themed Security Page for @payez/next-mvp
|
|
3
|
+
*
|
|
4
|
+
* DEPENDENCIES: Only React, Next.js, next-auth, React Query, and Tailwind CSS
|
|
5
|
+
* NO shadcn/ui or other UI library required!
|
|
6
|
+
*
|
|
7
|
+
* FEATURES:
|
|
8
|
+
* - Security summary (2FA status, email status, phone status)
|
|
9
|
+
* - Change password form with policy validation
|
|
10
|
+
* - Themeable styling via ThemeProvider
|
|
11
|
+
* - Uses React Query for data fetching (matches website-membership pattern)
|
|
12
|
+
*
|
|
13
|
+
* USAGE:
|
|
14
|
+
* 1. Import from @payez/next-mvp/pages/security
|
|
15
|
+
* 2. Wrap your app with ThemeProvider to customize branding
|
|
16
|
+
* 3. Create API routes at:
|
|
17
|
+
* - src/app/api/account/profile/route.ts
|
|
18
|
+
* - src/app/api/account/change-password/route.ts
|
|
19
|
+
* - src/app/api/account/validate-password/route.ts
|
|
20
|
+
*/
|
|
21
|
+
export default function SecurityPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Themed Security Page for @payez/next-mvp
|
|
4
|
+
*
|
|
5
|
+
* DEPENDENCIES: Only React, Next.js, next-auth, React Query, and Tailwind CSS
|
|
6
|
+
* NO shadcn/ui or other UI library required!
|
|
7
|
+
*
|
|
8
|
+
* FEATURES:
|
|
9
|
+
* - Security summary (2FA status, email status, phone status)
|
|
10
|
+
* - Change password form with policy validation
|
|
11
|
+
* - Themeable styling via ThemeProvider
|
|
12
|
+
* - Uses React Query for data fetching (matches website-membership pattern)
|
|
13
|
+
*
|
|
14
|
+
* USAGE:
|
|
15
|
+
* 1. Import from @payez/next-mvp/pages/security
|
|
16
|
+
* 2. Wrap your app with ThemeProvider to customize branding
|
|
17
|
+
* 3. Create API routes at:
|
|
18
|
+
* - src/app/api/account/profile/route.ts
|
|
19
|
+
* - src/app/api/account/change-password/route.ts
|
|
20
|
+
* - src/app/api/account/validate-password/route.ts
|
|
21
|
+
*/
|
|
22
|
+
'use client';
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.default = SecurityPage;
|
|
58
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
59
|
+
const react_1 = __importStar(require("react"));
|
|
60
|
+
const useProfile_1 = require("../../hooks/useProfile");
|
|
61
|
+
const usePasswordValidation_1 = require("../../hooks/usePasswordValidation");
|
|
62
|
+
const useTheme_1 = require("../../theme/useTheme");
|
|
63
|
+
// Inline PasswordStrengthMeter component
|
|
64
|
+
function PasswordStrengthMeter({ score, failedRequirements, tip, isDarkMode = false, }) {
|
|
65
|
+
const getStrengthColor = (s) => {
|
|
66
|
+
if (s >= 4)
|
|
67
|
+
return 'bg-green-500';
|
|
68
|
+
if (s >= 3)
|
|
69
|
+
return 'bg-yellow-500';
|
|
70
|
+
if (s >= 2)
|
|
71
|
+
return 'bg-orange-500';
|
|
72
|
+
return 'bg-red-500';
|
|
73
|
+
};
|
|
74
|
+
const getStrengthLabel = (s) => {
|
|
75
|
+
if (s >= 4)
|
|
76
|
+
return 'Strong';
|
|
77
|
+
if (s >= 3)
|
|
78
|
+
return 'Good';
|
|
79
|
+
if (s >= 2)
|
|
80
|
+
return 'Fair';
|
|
81
|
+
if (s >= 1)
|
|
82
|
+
return 'Weak';
|
|
83
|
+
return 'Very Weak';
|
|
84
|
+
};
|
|
85
|
+
const mutedTextClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
|
|
86
|
+
const barBgClass = isDarkMode ? 'bg-slate-600' : 'bg-gray-200';
|
|
87
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: `flex-1 h-2 rounded-full overflow-hidden ${barBgClass}`, children: (0, jsx_runtime_1.jsx)("div", { className: `h-full transition-all duration-300 ${getStrengthColor(score)}`, style: { width: `${Math.min(score * 20, 100)}%` } }) }), (0, jsx_runtime_1.jsx)("span", { className: `text-xs w-16 ${mutedTextClass}`, children: getStrengthLabel(score) })] }), failedRequirements.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: `text-xs space-y-1 ${mutedTextClass}`, children: failedRequirements.map((req, i) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-red-400", children: "\u00D7" }), " ", req] }, i))) })), tip && ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-blue-400", children: tip }))] }));
|
|
88
|
+
}
|
|
89
|
+
// Inline PasswordPolicyChecklist component
|
|
90
|
+
function PasswordPolicyChecklist({ policy, password, isDarkMode = false, }) {
|
|
91
|
+
const checks = react_1.default.useMemo(() => {
|
|
92
|
+
const list = [];
|
|
93
|
+
const minLen = typeof policy?.min_length === 'number' && policy.min_length > 0 ? policy.min_length : 8;
|
|
94
|
+
list.push({ label: `At least ${minLen} characters`, ok: (password?.length || 0) >= minLen });
|
|
95
|
+
if (policy?.require_uppercase)
|
|
96
|
+
list.push({ label: 'One uppercase letter (A-Z)', ok: /[A-Z]/.test(password || '') });
|
|
97
|
+
if (policy?.require_lowercase)
|
|
98
|
+
list.push({ label: 'One lowercase letter (a-z)', ok: /[a-z]/.test(password || '') });
|
|
99
|
+
if (policy?.require_digit)
|
|
100
|
+
list.push({ label: 'One digit (0-9)', ok: /\d/.test(password || '') });
|
|
101
|
+
if (policy?.require_special)
|
|
102
|
+
list.push({ label: 'One special character (!@#$% etc.)', ok: /[^A-Za-z0-9]/.test(password || '') });
|
|
103
|
+
return list;
|
|
104
|
+
}, [policy, password]);
|
|
105
|
+
const mutedTextClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
|
|
106
|
+
const successTextClass = 'text-green-400';
|
|
107
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "space-y-1 text-xs", children: checks.map((c, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [c.ok ? ((0, jsx_runtime_1.jsx)("span", { className: successTextClass, children: "\u2713" })) : ((0, jsx_runtime_1.jsx)("span", { className: mutedTextClass, children: "\u25CB" })), (0, jsx_runtime_1.jsx)("span", { className: c.ok ? successTextClass : mutedTextClass, children: c.label })] }, i))) }));
|
|
108
|
+
}
|
|
109
|
+
function SecurityPage() {
|
|
110
|
+
const { data: profileData, isLoading: isProfileLoading } = (0, useProfile_1.useProfile)();
|
|
111
|
+
const layout = (0, useTheme_1.useLayout)();
|
|
112
|
+
const colors = (0, useTheme_1.useColors)();
|
|
113
|
+
// Determine if dark mode based on background color
|
|
114
|
+
const isDarkMode = colors?.background?.includes('slate-9') ||
|
|
115
|
+
colors?.background?.includes('gray-9') ||
|
|
116
|
+
colors?.background?.includes('dark') ||
|
|
117
|
+
colors?.card?.includes('slate-8') ||
|
|
118
|
+
colors?.card?.includes('gray-8');
|
|
119
|
+
// Password form state
|
|
120
|
+
const [formData, setFormData] = (0, react_1.useState)({
|
|
121
|
+
current_password: '',
|
|
122
|
+
new_password: '',
|
|
123
|
+
confirm_password: '',
|
|
124
|
+
});
|
|
125
|
+
const [showPasswords, setShowPasswords] = (0, react_1.useState)({
|
|
126
|
+
current: false,
|
|
127
|
+
new: false,
|
|
128
|
+
confirm: false,
|
|
129
|
+
});
|
|
130
|
+
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
131
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
132
|
+
const [success, setSuccess] = (0, react_1.useState)('');
|
|
133
|
+
// Password validation
|
|
134
|
+
const { setPassword: validateNewPassword, isValid: newPasswordIsValid, score: newPasswordScore, failedRequirements, tip, policy, } = (0, usePasswordValidation_1.usePasswordValidation)({ debounceMs: 250 });
|
|
135
|
+
// Validate new password on change
|
|
136
|
+
(0, react_1.useEffect)(() => {
|
|
137
|
+
validateNewPassword(formData.new_password);
|
|
138
|
+
}, [formData.new_password, validateNewPassword]);
|
|
139
|
+
const passwordsMatch = formData.confirm_password.length > 0 && formData.new_password === formData.confirm_password;
|
|
140
|
+
const canSubmit = !submitting && newPasswordIsValid && passwordsMatch && formData.current_password.length > 0;
|
|
141
|
+
const handleSubmit = async (e) => {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
setError('');
|
|
144
|
+
setSuccess('');
|
|
145
|
+
if (!formData.current_password || !formData.new_password || !formData.confirm_password) {
|
|
146
|
+
setError('All fields are required');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (formData.new_password !== formData.confirm_password) {
|
|
150
|
+
setError('New password and confirmation do not match');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!newPasswordIsValid) {
|
|
154
|
+
setError('Please meet the password requirements before submitting');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
setSubmitting(true);
|
|
159
|
+
const response = await fetch('/api/account/change-password', {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: { 'Content-Type': 'application/json' },
|
|
162
|
+
body: JSON.stringify({
|
|
163
|
+
current_password: formData.current_password,
|
|
164
|
+
new_password: formData.new_password,
|
|
165
|
+
confirm_password: formData.confirm_password,
|
|
166
|
+
}),
|
|
167
|
+
});
|
|
168
|
+
const result = await response.json();
|
|
169
|
+
if (!response.ok || !result.success) {
|
|
170
|
+
// Extract error message
|
|
171
|
+
let errorMsg = result.message || 'Failed to change password';
|
|
172
|
+
if (result.details?.value && Array.isArray(result.details.value) && result.details.value.length > 0) {
|
|
173
|
+
errorMsg = result.details.value[0].message || errorMsg;
|
|
174
|
+
}
|
|
175
|
+
else if (result.details?.message) {
|
|
176
|
+
errorMsg = result.details.message;
|
|
177
|
+
}
|
|
178
|
+
setError(errorMsg);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Success
|
|
182
|
+
setSuccess(result.message || 'Password changed successfully');
|
|
183
|
+
setFormData({ current_password: '', new_password: '', confirm_password: '' });
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
setError(err.message || 'Failed to change password');
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
setSubmitting(false);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
// Determine loading state colors before any early returns
|
|
193
|
+
const loadingTextClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
|
|
194
|
+
// Loading state
|
|
195
|
+
if (isProfileLoading) {
|
|
196
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen", children: (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center min-h-[400px]", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center space-y-4", children: [(0, jsx_runtime_1.jsxs)("svg", { className: `animate-spin h-8 w-8 ${loadingTextClass}`, 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 12 a 8 8 0 0 1 8 -8 v 4 a 4 4 0 0 0 -4 4 H4 z" })] }), (0, jsx_runtime_1.jsx)("p", { className: loadingTextClass, children: "Loading security settings..." })] }) }) }));
|
|
197
|
+
}
|
|
198
|
+
// Theme-aware styling using colors from ThemeProvider
|
|
199
|
+
const cardBgClass = colors?.card || 'bg-white';
|
|
200
|
+
const borderClass = colors?.border || 'border-gray-200';
|
|
201
|
+
// Text colors based on dark/light mode
|
|
202
|
+
const textPrimaryClass = isDarkMode ? 'text-white' : 'text-gray-900';
|
|
203
|
+
const textSecondaryClass = isDarkMode ? 'text-slate-300' : 'text-gray-600';
|
|
204
|
+
const textMutedClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
|
|
205
|
+
// Input styling
|
|
206
|
+
const inputBgClass = isDarkMode ? 'bg-slate-800' : 'bg-white';
|
|
207
|
+
const inputBorderClass = isDarkMode ? 'border-slate-600' : 'border-gray-300';
|
|
208
|
+
const inputTextClass = isDarkMode ? 'text-white placeholder-slate-400' : 'text-gray-900 placeholder-gray-400';
|
|
209
|
+
// Elevated surfaces (status boxes)
|
|
210
|
+
const elevatedBgClass = isDarkMode ? 'bg-slate-700' : 'bg-gray-100';
|
|
211
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen", children: (0, jsx_runtime_1.jsxs)("div", { className: `${layout?.spacing || 'space-y-6'} ${layout?.maxWidth || 'max-w-4xl'} mx-auto ${layout?.padding || 'p-6'}`, children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between", children: (0, jsx_runtime_1.jsx)("h1", { className: `text-3xl font-bold ${textPrimaryClass}`, children: "Security" }) }), (0, jsx_runtime_1.jsxs)("div", { className: `rounded-lg shadow-sm border p-6 ${cardBgClass} ${borderClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `text-xl font-semibold mb-4 ${textPrimaryClass}`, children: "Security Summary" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBgClass}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondaryClass}`, children: "2FA Status" }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center space-x-2", children: (0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${profileData?.two_factor_enabled ? 'bg-green-600 text-white' : 'bg-yellow-600 text-white'}`, children: profileData?.two_factor_enabled ? 'Enabled' : 'Not Active' }) }), !profileData?.two_factor_enabled && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-2 ${textMutedClass}`, children: "Self-service 2FA enrollment coming soon" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBgClass}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondaryClass}`, children: "Email Status" }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center space-x-2", children: (0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${profileData?.email_confirmed ? 'bg-green-600 text-white' : 'bg-yellow-600 text-white'}`, children: profileData?.email_confirmed ? 'Verified' : 'Not Verified' }) }), (0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-2 ${textPrimaryClass}`, children: profileData?.email })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${elevatedBgClass}`, children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium mb-2 ${textSecondaryClass}`, children: "Phone Status" }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center space-x-2", children: (0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${profileData?.phone_confirmed ? 'bg-green-600 text-white' : 'bg-yellow-600 text-white'}`, children: profileData?.phone_confirmed ? 'Verified' : 'Not Verified' }) }), profileData?.phone_number && ((0, jsx_runtime_1.jsx)("p", { className: `text-xs mt-2 ${textPrimaryClass}`, children: profileData.phone_number }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `rounded-lg shadow-sm border p-6 ${cardBgClass} ${borderClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `text-xl font-semibold mb-4 ${textPrimaryClass}`, children: "Change Password" }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondaryClass}`, children: "Current Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.current ? 'text' : 'password', value: formData.current_password, onChange: e => setFormData({ ...formData, current_password: e.target.value }), className: `w-full px-3 py-2 rounded-md shadow-sm border focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${inputBgClass} ${inputBorderClass} ${inputTextClass}`, placeholder: "Enter current password", disabled: submitting, required: true }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, current: !p.current })), className: `absolute inset-y-0 right-2 flex items-center ${textMutedClass}`, children: showPasswords.current ? '👁️' : '👁️🗨️' })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondaryClass}`, children: "New Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.new ? 'text' : 'password', value: formData.new_password, onChange: e => setFormData({ ...formData, new_password: e.target.value }), className: `w-full px-3 py-2 rounded-md shadow-sm border focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${inputBgClass} ${inputBorderClass} ${inputTextClass}`, placeholder: "Enter new password", disabled: submitting, required: true }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, new: !p.new })), className: `absolute inset-y-0 right-2 flex items-center ${textMutedClass}`, children: showPasswords.new ? '👁️' : '👁️🗨️' })] }), formData.new_password && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2", children: (0, jsx_runtime_1.jsx)(PasswordStrengthMeter, { score: newPasswordScore, failedRequirements: failedRequirements, tip: tip, isDarkMode: isDarkMode }) })), policy && formData.new_password && ((0, jsx_runtime_1.jsx)("div", { className: "mt-3", children: (0, jsx_runtime_1.jsx)(PasswordPolicyChecklist, { policy: policy, password: formData.new_password, isDarkMode: isDarkMode }) }))] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: `block text-sm font-medium mb-2 ${textSecondaryClass}`, children: "Confirm New Password" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: showPasswords.confirm ? 'text' : 'password', value: formData.confirm_password, onChange: e => setFormData({ ...formData, confirm_password: e.target.value }), className: `w-full px-3 py-2 rounded-md shadow-sm border focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${inputBgClass} ${inputBorderClass} ${inputTextClass}`, placeholder: "Confirm new password", disabled: submitting, required: true }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowPasswords(p => ({ ...p, confirm: !p.confirm })), className: `absolute inset-y-0 right-2 flex items-center ${textMutedClass}`, children: showPasswords.confirm ? '👁️' : '👁️🗨️' })] }), formData.confirm_password && !passwordsMatch && ((0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-red-400", children: "Passwords do not match" })), formData.confirm_password && passwordsMatch && ((0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-green-400", children: "Passwords match" }))] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "bg-red-900/30 border border-red-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-400 text-sm", children: error }) })), success && ((0, jsx_runtime_1.jsx)("div", { className: "bg-green-900/30 border border-green-600 rounded-lg p-3", children: (0, jsx_runtime_1.jsx)("p", { className: "text-green-400 text-sm", children: success }) })), (0, jsx_runtime_1.jsx)("div", { className: "pt-2", children: (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: !canSubmit, className: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed", children: submitting ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4 text-white", 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 12 a 8 8 0 0 1 8 -8 v 4 a 4 4 0 0 0 -4 4 H4 z" })] }), "Changing..."] })) : ('Change Password') }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `rounded-lg shadow-sm border p-6 ${cardBgClass} ${borderClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `text-xl font-semibold mb-4 ${textPrimaryClass}`, children: "Additional Security Features" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-4 rounded-lg opacity-60 ${elevatedBgClass}`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium ${textPrimaryClass}`, children: "Active Sessions" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMutedClass}`, children: "View and manage your active login sessions" })] }), (0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${isDarkMode ? 'bg-slate-600 text-slate-300' : 'bg-gray-200 text-gray-600'}`, children: "Coming Soon" })] }), (0, jsx_runtime_1.jsxs)("div", { className: `flex items-center justify-between p-4 rounded-lg opacity-60 ${elevatedBgClass}`, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `text-sm font-medium ${textPrimaryClass}`, children: "2FA Management" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${textMutedClass}`, children: "Enable or configure two-factor authentication" })] }), (0, jsx_runtime_1.jsx)("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${isDarkMode ? 'bg-slate-600 text-slate-300' : 'bg-gray-200 text-gray-600'}`, children: "Coming Soon" })] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-center", children: (0, jsx_runtime_1.jsx)("a", { href: "/account/profile", className: `text-sm hover:underline ${textMutedClass}`, children: "\u2190 Back to Profile" }) })] }) }));
|
|
212
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|