diff --git a/web/maplefile-frontend/src/components/UIX/GDPRFooter/GDPRFooter.jsx b/web/maplefile-frontend/src/components/UIX/GDPRFooter/GDPRFooter.jsx index 03fedfa..054fa52 100644 --- a/web/maplefile-frontend/src/components/UIX/GDPRFooter/GDPRFooter.jsx +++ b/web/maplefile-frontend/src/components/UIX/GDPRFooter/GDPRFooter.jsx @@ -16,10 +16,12 @@ import { * * @param {string} className - Additional CSS classes * @param {string} containerClassName - Additional classes for the outer container + * @param {boolean} showSecurityFeatures - Whether to show the security features section (default: true) */ const GDPRFooter = memo(function GDPRFooter({ className = "", - containerClassName = "" + containerClassName = "", + showSecurityFeatures = true, }) { const { getThemeClasses } = useUIXTheme(); @@ -36,29 +38,31 @@ const GDPRFooter = memo(function GDPRFooter({
{/* Security Features */} -
-
- - - ChaCha20-Poly1305 Encryption - + {showSecurityFeatures && ( +
+
+ + + ChaCha20-Poly1305 Encryption + +
+
+ + Canadian Hosted +
+
+ + Privacy First +
+
+ + Made in Canada +
-
- - Canadian Hosted -
-
- - Privacy First -
-
- - Made in Canada -
-
+ )} {/* GDPR Information */} -
+

Data Controller: Maple Open Tech Inc. |{" "} Location: Canada (Adequate protection under GDPR Art. 45) diff --git a/web/maplefile-frontend/src/components/UIX/themes/index.js b/web/maplefile-frontend/src/components/UIX/themes/index.js index 0e9b766..cc83427 100644 --- a/web/maplefile-frontend/src/components/UIX/themes/index.js +++ b/web/maplefile-frontend/src/components/UIX/themes/index.js @@ -227,6 +227,45 @@ const getThemeConfigs = () => { "success-bg": "bg-green-50", "success-border": "border-green-200", "success-text": "text-green-800", + + // Help page section colors + "help-section-blue-text": "text-blue-600", + "help-section-blue-bg": "bg-blue-50", + "help-section-green-text": "text-green-600", + "help-section-green-bg": "bg-green-50", + "help-section-purple-text": "text-purple-600", + "help-section-purple-bg": "bg-purple-50", + "help-section-pink-text": "text-pink-600", + "help-section-pink-bg": "bg-pink-50", + "help-section-red-text": "text-red-600", + "help-section-red-bg": "bg-red-50", + + // Export page section colors - Success (green) + "export-section-success-bg": "bg-green-50", + "export-section-success-border": "border-green-200", + "export-section-success-icon": "text-green-600", + "export-section-success-title": "text-green-900", + "export-section-success-text": "text-green-800", + "export-section-success-muted": "text-green-700", + + // Export page section colors - Info (blue) + "export-section-info-bg": "bg-blue-50", + "export-section-info-border": "border-blue-200", + "export-section-info-icon": "text-blue-600", + "export-section-info-title": "text-blue-900", + "export-section-info-text": "text-blue-800", + + // Export page section colors - Warning (yellow) + "export-section-warning-bg": "bg-yellow-50", + "export-section-warning-border": "border-yellow-200", + "export-section-warning-icon": "text-yellow-600", + "export-section-warning-title": "text-yellow-900", + "export-section-warning-text": "text-yellow-800", + "export-section-warning-muted": "text-yellow-700", + "export-section-warning-code": "bg-yellow-100", + + // Muted background + "bg-muted": "bg-gray-50", }, }, @@ -434,6 +473,45 @@ const getThemeConfigs = () => { "success-bg": "bg-green-50", "success-border": "border-green-200", "success-text": "text-green-800", + + // Help page section colors + "help-section-blue-text": "text-blue-600", + "help-section-blue-bg": "bg-blue-50", + "help-section-green-text": "text-green-600", + "help-section-green-bg": "bg-green-50", + "help-section-purple-text": "text-purple-600", + "help-section-purple-bg": "bg-purple-50", + "help-section-pink-text": "text-pink-600", + "help-section-pink-bg": "bg-pink-50", + "help-section-red-text": "text-red-600", + "help-section-red-bg": "bg-red-50", + + // Export page section colors - Success (green) + "export-section-success-bg": "bg-green-50", + "export-section-success-border": "border-green-200", + "export-section-success-icon": "text-green-600", + "export-section-success-title": "text-green-900", + "export-section-success-text": "text-green-800", + "export-section-success-muted": "text-green-700", + + // Export page section colors - Info (blue) + "export-section-info-bg": "bg-blue-50", + "export-section-info-border": "border-blue-200", + "export-section-info-icon": "text-blue-600", + "export-section-info-title": "text-blue-900", + "export-section-info-text": "text-blue-800", + + // Export page section colors - Warning (yellow) + "export-section-warning-bg": "bg-yellow-50", + "export-section-warning-border": "border-yellow-200", + "export-section-warning-icon": "text-yellow-600", + "export-section-warning-title": "text-yellow-900", + "export-section-warning-text": "text-yellow-800", + "export-section-warning-muted": "text-yellow-700", + "export-section-warning-code": "bg-yellow-100", + + // Muted background + "bg-muted": "bg-gray-50", }, }, @@ -642,6 +720,45 @@ const getThemeConfigs = () => { "success-bg": "bg-green-50", "success-border": "border-green-200", "success-text": "text-green-800", + + // Help page section colors + "help-section-blue-text": "text-blue-600", + "help-section-blue-bg": "bg-blue-50", + "help-section-green-text": "text-green-600", + "help-section-green-bg": "bg-green-50", + "help-section-purple-text": "text-purple-600", + "help-section-purple-bg": "bg-purple-50", + "help-section-pink-text": "text-pink-600", + "help-section-pink-bg": "bg-pink-50", + "help-section-red-text": "text-red-600", + "help-section-red-bg": "bg-red-50", + + // Export page section colors - Success (green) + "export-section-success-bg": "bg-green-50", + "export-section-success-border": "border-green-200", + "export-section-success-icon": "text-green-600", + "export-section-success-title": "text-green-900", + "export-section-success-text": "text-green-800", + "export-section-success-muted": "text-green-700", + + // Export page section colors - Info (blue) + "export-section-info-bg": "bg-blue-50", + "export-section-info-border": "border-blue-200", + "export-section-info-icon": "text-blue-600", + "export-section-info-title": "text-blue-900", + "export-section-info-text": "text-blue-800", + + // Export page section colors - Warning (yellow) + "export-section-warning-bg": "bg-yellow-50", + "export-section-warning-border": "border-yellow-200", + "export-section-warning-icon": "text-yellow-600", + "export-section-warning-title": "text-yellow-900", + "export-section-warning-text": "text-yellow-800", + "export-section-warning-muted": "text-yellow-700", + "export-section-warning-code": "bg-yellow-100", + + // Muted background + "bg-muted": "bg-gray-50", }, }, @@ -849,6 +966,45 @@ const getThemeConfigs = () => { "success-bg": "bg-green-50", "success-border": "border-green-200", "success-text": "text-green-800", + + // Help page section colors + "help-section-blue-text": "text-blue-600", + "help-section-blue-bg": "bg-blue-50", + "help-section-green-text": "text-green-600", + "help-section-green-bg": "bg-green-50", + "help-section-purple-text": "text-purple-600", + "help-section-purple-bg": "bg-purple-50", + "help-section-pink-text": "text-pink-600", + "help-section-pink-bg": "bg-pink-50", + "help-section-red-text": "text-red-600", + "help-section-red-bg": "bg-red-50", + + // Export page section colors - Success (green) + "export-section-success-bg": "bg-green-50", + "export-section-success-border": "border-green-200", + "export-section-success-icon": "text-green-600", + "export-section-success-title": "text-green-900", + "export-section-success-text": "text-green-800", + "export-section-success-muted": "text-green-700", + + // Export page section colors - Info (blue) + "export-section-info-bg": "bg-blue-50", + "export-section-info-border": "border-blue-200", + "export-section-info-icon": "text-blue-600", + "export-section-info-title": "text-blue-900", + "export-section-info-text": "text-blue-800", + + // Export page section colors - Warning (yellow) + "export-section-warning-bg": "bg-yellow-50", + "export-section-warning-border": "border-yellow-200", + "export-section-warning-icon": "text-yellow-600", + "export-section-warning-title": "text-yellow-900", + "export-section-warning-text": "text-yellow-800", + "export-section-warning-muted": "text-yellow-700", + "export-section-warning-code": "bg-yellow-100", + + // Muted background + "bg-muted": "bg-gray-50", }, }, @@ -1056,6 +1212,45 @@ const getThemeConfigs = () => { "success-bg": "bg-green-50", "success-border": "border-green-200", "success-text": "text-green-800", + + // Help page section colors + "help-section-blue-text": "text-blue-600", + "help-section-blue-bg": "bg-blue-50", + "help-section-green-text": "text-green-600", + "help-section-green-bg": "bg-green-50", + "help-section-purple-text": "text-purple-600", + "help-section-purple-bg": "bg-purple-50", + "help-section-pink-text": "text-pink-600", + "help-section-pink-bg": "bg-pink-50", + "help-section-red-text": "text-red-600", + "help-section-red-bg": "bg-red-50", + + // Export page section colors - Success (green) + "export-section-success-bg": "bg-green-50", + "export-section-success-border": "border-green-200", + "export-section-success-icon": "text-green-600", + "export-section-success-title": "text-green-900", + "export-section-success-text": "text-green-800", + "export-section-success-muted": "text-green-700", + + // Export page section colors - Info (blue) + "export-section-info-bg": "bg-blue-50", + "export-section-info-border": "border-blue-200", + "export-section-info-icon": "text-blue-600", + "export-section-info-title": "text-blue-900", + "export-section-info-text": "text-blue-800", + + // Export page section colors - Warning (yellow) + "export-section-warning-bg": "bg-yellow-50", + "export-section-warning-border": "border-yellow-200", + "export-section-warning-icon": "text-yellow-600", + "export-section-warning-title": "text-yellow-900", + "export-section-warning-text": "text-yellow-800", + "export-section-warning-muted": "text-yellow-700", + "export-section-warning-code": "bg-yellow-100", + + // Muted background + "bg-muted": "bg-gray-50", }, }, }; diff --git a/web/maplefile-frontend/src/components/pages/Anonymous/Login/LoginPageUIX.jsx b/web/maplefile-frontend/src/components/pages/Anonymous/Login/LoginPageUIX.jsx index 85165a8..87d0e62 100644 --- a/web/maplefile-frontend/src/components/pages/Anonymous/Login/LoginPageUIX.jsx +++ b/web/maplefile-frontend/src/components/pages/Anonymous/Login/LoginPageUIX.jsx @@ -33,6 +33,7 @@ function LoginPageUIX() { const authManager = useAuthManager(); const navigate = useNavigate(); const emailInputRef = useRef(null); + const isMountedRef = useRef(true); // UIX Theme support - defaults to blue theme const { getThemeClasses } = useUIXTheme(); @@ -47,6 +48,14 @@ function LoginPageUIX() { const [showPassword, setShowPassword] = useState(false); const [hasInitialized, setHasInitialized] = useState(false); + // Cleanup on unmount - prevents state updates after unmount + useEffect(() => { + isMountedRef.current = true; + return () => { + isMountedRef.current = false; + }; + }, []); + // Split useEffect 1: Basic initialization useEffect(() => { if (hasInitialized) return; @@ -145,9 +154,12 @@ function LoginPageUIX() { return newErrors; }; - const handleSubmit = async (e) => { + const handleSubmit = useCallback(async (e) => { e.preventDefault(); + // Prevent state updates if component is unmounted + if (!isMountedRef.current) return; + const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); @@ -177,6 +189,8 @@ function LoginPageUIX() { password: formData.password, }); + if (!isMountedRef.current) return; + if (import.meta.env.DEV) { console.log("LoginPage: Login successful"); } @@ -203,6 +217,8 @@ function LoginPageUIX() { navigate("/login/2fa"); } } catch (error) { + if (!isMountedRef.current) return; + if (import.meta.env.DEV) { console.error("LoginPage: Login failed", error); } @@ -215,16 +231,18 @@ function LoginPageUIX() { form?.classList.add("animate-shake"); setTimeout(() => form?.classList.remove("animate-shake"), 500); } finally { - setLoading(false); + if (isMountedRef.current) { + setLoading(false); + } } - }; + }, [formData, rememberMe, authManager, navigate, validateForm]); // Handle Enter key submission - memoized to prevent Input component re-renders const handleKeyDown = useCallback((e) => { if (e.key === "Enter" && !loading) { handleSubmit(e); } - }, [loading]); // Depends on loading state to disable during submission + }, [loading, handleSubmit]); // Depends on loading state and handleSubmit // Memoized password toggle button - prevents Input re-renders const passwordSuffix = useMemo(() => ( @@ -350,7 +368,7 @@ function LoginPageUIX() { setRememberMe(e.target.checked)} + onChange={(checked) => setRememberMe(checked)} disabled={loading} className="text-sm" /> diff --git a/web/maplefile-frontend/src/components/pages/Anonymous/TwoFA/ValidationPage.jsx b/web/maplefile-frontend/src/components/pages/Anonymous/TwoFA/ValidationPage.jsx index 3912b70..c7d4355 100644 --- a/web/maplefile-frontend/src/components/pages/Anonymous/TwoFA/ValidationPage.jsx +++ b/web/maplefile-frontend/src/components/pages/Anonymous/TwoFA/ValidationPage.jsx @@ -1,5 +1,5 @@ // File Path: monorepo/web/frontend/src/pages/Anonymous/TwoFA/ValidationPage.jsx -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback, useRef } from "react"; import { Link, useNavigate } from "react-router"; import { useAuthManager, @@ -11,6 +11,7 @@ import { useUIXTheme, useMobileOptimizations, OTPInput, + Button, } from "../../../components/UIX"; import { ShieldCheckIcon, @@ -47,6 +48,15 @@ function TwoFAValidationPageContent() { const [errors, setErrors] = useState({}); const [isLoading, setIsLoading] = useState(false); const [token, setToken] = useState(""); + const isMountedRef = useRef(true); + + // Cleanup on unmount - prevents state updates after unmount + useEffect(() => { + isMountedRef.current = true; + return () => { + isMountedRef.current = false; + }; + }, []); //// //// Event handling. @@ -62,6 +72,9 @@ function TwoFAValidationPageContent() { const handleSubmit = useCallback(async (e) => { e.preventDefault(); + // Prevent state updates if component is unmounted + if (!isMountedRef.current) return; + // Clear previous errors setErrors({}); @@ -89,6 +102,8 @@ function TwoFAValidationPageContent() { onUnauthorized, ); + if (!isMountedRef.current) return; + if (import.meta.env.DEV) { console.log("TwoFAValidationPage: OTP validation successful"); } @@ -110,13 +125,17 @@ function TwoFAValidationPageContent() { navigate("/dashboard"); } } catch (error) { + if (!isMountedRef.current) return; + if (import.meta.env.DEV) { console.error("TwoFAValidationPage: OTP validation failed", error); } setErrors(error); window.scrollTo(0, 0); } finally { - setIsLoading(false); + if (isMountedRef.current) { + setIsLoading(false); + } } }, [token, twoFactorAuthManager, onUnauthorized, navigate]); @@ -258,27 +277,21 @@ function TwoFAValidationPageContent() { Use Backup Code - + {isLoading && "Verifying..."} +

{/* Back to Login */} @@ -328,7 +341,7 @@ function TwoFAValidationPageContent() { {/* Copyright */}

- © 2024 Flashpoint Training + © 2025 Flashpoint Training

diff --git a/web/maplefile-frontend/src/pages/Anonymous/Download/DownloadPage.jsx b/web/maplefile-frontend/src/pages/Anonymous/Download/DownloadPage.jsx index bb31c73..0e47e26 100644 --- a/web/maplefile-frontend/src/pages/Anonymous/Download/DownloadPage.jsx +++ b/web/maplefile-frontend/src/pages/Anonymous/Download/DownloadPage.jsx @@ -1,7 +1,7 @@ // File: monorepo/web/maplefile-frontend/src/pages/Anonymous/Download/DownloadPage.jsx // Simple download page for MapleFile desktop application import { Link } from "react-router"; -import { Button, Card } from "../../../components/UIX"; +import { Button, Card, useUIXTheme } from "../../../components/UIX"; import { ArrowRightIcon, ArrowDownTrayIcon, @@ -9,6 +9,7 @@ import { } from "@heroicons/react/24/outline"; function DownloadPage() { + const { getThemeClasses } = useUIXTheme(); const currentVersion = "1.0.0"; const downloads = [ @@ -20,30 +21,30 @@ function DownloadPage() { ]; return ( -
+
{/* Navigation */} -