gui fixes

This commit is contained in:
Rodolfo Martinez 2025-12-09 22:02:14 -05:00
parent 3bf89fe2fa
commit b3e87772ec
4 changed files with 81 additions and 338 deletions

View file

@ -7,9 +7,9 @@
<!-- Security Headers (Defense in Depth - Backend should also set HTTP headers) -->
<!-- Note: connect-src includes localhost:8000 (API), localhost:8334 (MinIO/S3 dev), and production S3 endpoints -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' http://localhost:8000 http://localhost:8334 https://*.digitaloceanspaces.com https://*.amazonaws.com ws://localhost:*; frame-ancestors 'none'; base-uri 'self'; form-action 'self';">
<!-- Note: frame-ancestors and X-Frame-Options must be set via HTTP headers, not meta tags -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' http://localhost:8000 http://localhost:8334 https://*.digitaloceanspaces.com https://*.amazonaws.com ws://localhost:*; base-uri 'self'; form-action 'self';">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-Frame-Options" content="DENY">
<meta name="referrer" content="strict-origin-when-cross-origin">
<meta http-equiv="Permissions-Policy" content="geolocation=(), microphone=(), camera=()">

View file

@ -1,7 +1,7 @@
// File Path: web/frontend/src/components/Layout/Layout.jsx
// Fixed Layout Component - Mobile Menu Now Works Properly
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import TopNavbar from "./TopNavbar";
import Sidebar from "./Sidebar";
import { useInactivityTimeout } from "../../hooks/useInactivityTimeout";
@ -292,7 +292,16 @@ function Layout({ children }) {
return () => window.removeEventListener("resize", handleResize);
}, [isMobile]); // Include isMobile but use functional state updates to avoid loops
// Track last toggle time to prevent double-clicks
const lastToggleRef = useRef(0);
const handleMenuToggle = useCallback(() => {
const now = Date.now();
// Prevent double-clicks only (100ms window)
if (now - lastToggleRef.current < 100) {
return;
}
lastToggleRef.current = now;
setIsSidebarOpen(current => !current);
}, []);
@ -300,8 +309,17 @@ function Layout({ children }) {
setIsSidebarOpen(false);
}, []);
// Track last collapse toggle time to prevent double-clicks
const lastCollapseToggleRef = useRef(0);
// Handle collapse toggle for desktop view
const handleCollapseToggle = useCallback(() => {
const now = Date.now();
// Prevent double-clicks only (100ms window)
if (now - lastCollapseToggleRef.current < 100) {
return;
}
lastCollapseToggleRef.current = now;
setSidebarCollapsed(current => {
const newCollapsedState = !current;
localStorage.setItem("sidebarCollapsed", newCollapsedState.toString());

View file

@ -11,6 +11,7 @@ import {
UserCircleIcon,
ArrowRightOnRectangleIcon,
ChevronDownIcon,
LockClosedIcon,
} from "@heroicons/react/24/outline";
import { Modal, Button, useUIXTheme } from "../UIX";
@ -248,12 +249,20 @@ function TopNavbar({
${isMobile ? "justify-center" : "justify-start ml-4"}
`}
>
<img
src="/img/compressed-logo.png"
alt="MapleFile Logo"
className="h-8 w-auto sm:h-9 md:h-10"
draggable="false"
/>
<Link
to="/dashboard"
className="flex items-center gap-2 hover:opacity-90 transition-opacity"
aria-label="Go to Dashboard"
>
{/* Lock icon with theme-aware background */}
<div className={`${getThemeClasses("bg-gradient-secondary")} p-1.5 sm:p-2 rounded-lg`}>
<LockClosedIcon className="h-5 w-5 sm:h-6 sm:w-6 text-white" aria-hidden="true" />
</div>
{/* MapleFile text - hidden on mobile */}
<span className="hidden sm:inline text-white font-semibold text-base md:text-lg tracking-tight">
MapleFile
</span>
</Link>
</div>
{/* Spacer for mobile to balance the layout */}

View file

@ -6,13 +6,10 @@ import Layout from "../../../components/Layout/Layout";
import { DetailLiteView, Button, useUIXTheme } from "../../../components/UIX";
import {
ArrowDownTrayIcon,
InformationCircleIcon,
ComputerDesktopIcon,
ShieldCheckIcon,
DocumentTextIcon,
CodeBracketIcon,
HomeIcon,
UserIcon,
ArrowTopRightOnSquareIcon,
} from "@heroicons/react/24/outline";
// Constant empty object to prevent re-renders (reused across renders)
@ -21,34 +18,6 @@ const EMPTY_ENTITY_DATA = {};
const ExportData = () => {
const { getThemeClasses } = useUIXTheme();
// Theme-aware section styles
const sectionStyles = useMemo(() => ({
success: {
container: getThemeClasses("export-section-success-bg"),
border: getThemeClasses("export-section-success-border"),
icon: getThemeClasses("export-section-success-icon"),
title: getThemeClasses("export-section-success-title"),
text: getThemeClasses("export-section-success-text"),
muted: getThemeClasses("export-section-success-muted"),
},
info: {
container: getThemeClasses("export-section-info-bg"),
border: getThemeClasses("export-section-info-border"),
icon: getThemeClasses("export-section-info-icon"),
title: getThemeClasses("export-section-info-title"),
text: getThemeClasses("export-section-info-text"),
},
warning: {
container: getThemeClasses("export-section-warning-bg"),
border: getThemeClasses("export-section-warning-border"),
icon: getThemeClasses("export-section-warning-icon"),
title: getThemeClasses("export-section-warning-title"),
text: getThemeClasses("export-section-warning-text"),
muted: getThemeClasses("export-section-warning-muted"),
code: getThemeClasses("export-section-warning-code"),
},
}), [getThemeClasses]);
// Breadcrumb configuration
const breadcrumbItems = useMemo(
() => [
@ -74,7 +43,7 @@ const ExportData = () => {
const headerConfig = useMemo(
() => ({
title: "Export Your Data",
subtitle: "Download a complete copy of your account data in accordance with GDPR Article 20 (Right to Data Portability)",
subtitle: "Download a complete copy of your account data (GDPR Article 20)",
icon: ArrowDownTrayIcon,
}),
[],
@ -83,203 +52,36 @@ const ExportData = () => {
// Content sections
const contentSections = useMemo(() => {
return (
<div className="space-y-6" role="main" aria-label="Export data options">
{/* Quick Export Option - GDPR Compliance */}
<div className="space-y-6" role="main" aria-label="Export data information">
{/* Desktop App Required Notice */}
<section
aria-labelledby="quick-export-heading"
className={`${sectionStyles.success.container} border-2 ${sectionStyles.success.border} rounded-xl p-6`}
aria-labelledby="desktop-required-heading"
className={`${getThemeClasses("bg-accent-light")} border-2 ${getThemeClasses("border-accent")} rounded-xl p-6`}
>
<div className="flex items-start">
<ArrowDownTrayIcon className={`h-6 w-6 ${sectionStyles.success.icon} mr-3 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<ComputerDesktopIcon className={`h-8 w-8 ${getThemeClasses("text-accent")} mr-4 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<div className="flex-1">
<h3 id="quick-export-heading" className={`text-lg font-semibold ${sectionStyles.success.title} mb-2`}>
Quick Export (Web Browser)
<h3 id="desktop-required-heading" className={`text-xl font-semibold ${getThemeClasses("text-primary")} mb-3`}>
Desktop App Required
</h3>
<p className={`${sectionStyles.success.text} mb-3`}>
In compliance with GDPR Article 20, you can export your data directly from your web browser. This option is suitable for accounts with moderate amounts of data.
</p>
<p className={`${sectionStyles.success.text} mb-4`}>
<strong>What gets exported:</strong> Profile information, collection metadata, and file lists (metadata only - actual file downloads require desktop app due to E2EE).
</p>
<div className="flex space-x-3">
<Button
variant="primary"
icon={ArrowDownTrayIcon}
onClick={() => alert('This feature will be implemented by the backend team. It should generate a JSON export of user profile, collections, and file metadata.')}
>
Export Metadata (JSON)
</Button>
</div>
<p className={`text-xs ${sectionStyles.success.muted} mt-3`}>
<strong>Timeline:</strong> Your export will be available for download within 24 hours. Large exports may take up to 72 hours. You'll receive an email when ready.
</p>
</div>
</div>
</section>
{/* Information Notice */}
<section
aria-labelledby="desktop-info-heading"
className={`${sectionStyles.info.container} border-2 ${sectionStyles.info.border} rounded-xl p-6`}
>
<div className="flex items-start">
<InformationCircleIcon className={`h-6 w-6 ${sectionStyles.info.icon} mr-3 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<div className="flex-1">
<h3 id="desktop-info-heading" className={`text-lg font-semibold ${sectionStyles.info.title} mb-2`}>
Desktop Application for Complete Export
</h3>
<p className={`${sectionStyles.info.text} mb-3`}>
For exporting <strong>complete data including decrypted files</strong>, we recommend using the <strong>MapleFile Desktop Application</strong>. Due to end-to-end encryption, only the desktop app can decrypt and export your actual file contents.
</p>
<p className={sectionStyles.info.text}>
The desktop application efficiently manages the decryption and download of your complete data archive with all files in their original format.
</p>
</div>
</div>
</section>
{/* E2EE Explanation */}
<section
aria-labelledby="e2ee-heading"
className={`${getThemeClasses("bg-card")} rounded-xl shadow-lg border ${getThemeClasses("border-secondary")} p-6`}
>
<div className="flex items-start mb-4">
<ShieldCheckIcon className={`h-6 w-6 ${getThemeClasses("icon-success")} mr-3 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<div>
<h3 id="e2ee-heading" className={`text-lg font-semibold mb-2 ${getThemeClasses("text-primary")}`}>
Why End-to-End Encryption Matters
</h3>
<p className={`${getThemeClasses("text-secondary")} mb-3`}>
Your files are encrypted on your device before they're uploaded to our servers. This means:
</p>
<ul className={`space-y-2 ${getThemeClasses("text-secondary")}`} role="list" aria-label="Encryption benefits">
<li className="flex items-start">
<span className={`${getThemeClasses("icon-success")} mr-2`} aria-hidden="true"></span>
<span>Only you can decrypt and read your files</span>
</li>
<li className="flex items-start">
<span className={`${getThemeClasses("icon-success")} mr-2`} aria-hidden="true"></span>
<span>MapleFile servers cannot access your file contents</span>
</li>
<li className="flex items-start">
<span className={`${getThemeClasses("icon-success")} mr-2`} aria-hidden="true"></span>
<span>Your privacy is protected even if servers are compromised</span>
</li>
<li className="flex items-start">
<span className={`${getThemeClasses("icon-success")} mr-2`} aria-hidden="true"></span>
<span>Decryption requires your local encryption keys</span>
</li>
</ul>
</div>
</div>
</section>
{/* Desktop Application Installation */}
<section
aria-labelledby="install-heading"
className={`${getThemeClasses("bg-card")} rounded-xl shadow-lg border ${getThemeClasses("border-secondary")} p-6`}
>
<div className="flex items-start mb-4">
<ComputerDesktopIcon className={`h-6 w-6 mr-3 flex-shrink-0 mt-0.5 ${getThemeClasses("link-primary")}`} aria-hidden="true" />
<div className="flex-1">
<h3 id="install-heading" className={`text-lg font-semibold mb-2 ${getThemeClasses("text-primary")}`}>
Install MapleFile Desktop Application
</h3>
<p className={`${getThemeClasses("text-secondary")} mb-4`}>
The MapleFile Desktop Application is a native application that can export your data with full decryption support and an intuitive user interface.
</p>
{/* Repository Link */}
<div className={`${getThemeClasses("bg-muted")} border ${getThemeClasses("border-secondary")} rounded-lg p-4 mb-4`}>
<div className="flex items-start">
<CodeBracketIcon className={`h-5 w-5 ${getThemeClasses("text-muted")} mr-3 mt-0.5`} aria-hidden="true" />
<div className="flex-1">
<p className={`text-sm font-medium mb-2 ${getThemeClasses("text-primary")}`}>
Source Code & Installation Instructions
<p className={`${getThemeClasses("text-secondary")} mb-4 text-base`}>
Data export is only available through the <strong>MapleFile Desktop Application</strong>. Due to end-to-end encryption, your files can only be decrypted and exported using the native app.
</p>
<a
href="https://codeberg.org/mapleopentech/monorepo/src/branch/main/native/desktop/maplefile"
href="https://maplefile.com"
target="_blank"
rel="noopener noreferrer"
className={`${getThemeClasses("link-primary")} hover:underline text-sm font-mono break-all`}
aria-label="Download MapleFile Desktop App (opens in new tab)"
>
https://codeberg.org/mapleopentech/monorepo/src/branch/main/native/desktop/maplefile
<Button
variant="primary"
icon={ArrowTopRightOnSquareIcon}
>
Download at maplefile.com
</Button>
</a>
</div>
</div>
</div>
{/* Installation Steps */}
<div className="space-y-3">
<h4 className={`font-medium ${getThemeClasses("text-primary")}`}>Installation Steps:</h4>
<ol className={`space-y-3 ${getThemeClasses("text-secondary")}`} role="list" aria-label="Installation steps">
<li className="flex items-start">
<span className={`font-semibold mr-3 min-w-[24px] ${getThemeClasses("link-primary")}`} aria-hidden="true">1.</span>
<span>Visit the repository link above</span>
</li>
<li className="flex items-start">
<span className={`font-semibold mr-3 min-w-[24px] ${getThemeClasses("link-primary")}`} aria-hidden="true">2.</span>
<span>Follow the installation instructions in the README.md file</span>
</li>
<li className="flex items-start">
<span className={`font-semibold mr-3 min-w-[24px] ${getThemeClasses("link-primary")}`} aria-hidden="true">3.</span>
<span>Install the desktop application for your operating system (Windows, macOS, or Linux)</span>
</li>
<li className="flex items-start">
<span className={`font-semibold mr-3 min-w-[24px] ${getThemeClasses("link-primary")}`} aria-hidden="true">4.</span>
<span>Log in to your MapleFile account using the desktop application</span>
</li>
<li className="flex items-start">
<span className={`font-semibold mr-3 min-w-[24px] ${getThemeClasses("link-primary")}`} aria-hidden="true">5.</span>
<span>Use the export feature to download and decrypt your data</span>
</li>
</ol>
</div>
</div>
</div>
</section>
{/* What Gets Exported */}
<section
aria-labelledby="exported-data-heading"
className={`${getThemeClasses("bg-card")} rounded-xl shadow-lg border ${getThemeClasses("border-secondary")} p-6`}
>
<div className="flex items-start">
<DocumentTextIcon className={`h-6 w-6 ${getThemeClasses("text-muted")} mr-3 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<div className="flex-1">
<h3 id="exported-data-heading" className={`text-lg font-semibold mb-3 ${getThemeClasses("text-primary")}`}>
What Data Will Be Exported
</h3>
<p className={`${getThemeClasses("text-secondary")} mb-3`}>
The desktop application will export a complete archive of your MapleFile account, including:
</p>
<ul className={`space-y-2 ${getThemeClasses("text-secondary")}`} role="list" aria-label="Exported data types">
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span><strong>Profile Information:</strong> Your account details, settings, and preferences</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span><strong>Collections (Folders):</strong> All your collection metadata and structure</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span><strong>Files:</strong> All uploaded files, fully decrypted and in their original format</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span><strong>File Metadata:</strong> Creation dates, modification dates, file sizes, and descriptions</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span><strong>Sharing Information:</strong> Details about collections shared with you or by you</span>
</li>
</ul>
<p className={`${getThemeClasses("text-muted")} text-sm mt-4`}>
The export will be provided as a ZIP archive with a structured folder hierarchy matching your collections.
</p>
</div>
</div>
</section>
{/* GDPR Information */}
@ -290,99 +92,13 @@ const ExportData = () => {
<h3 id="gdpr-heading" className={`text-sm font-semibold mb-2 ${getThemeClasses("text-primary")}`}>
GDPR Article 20 - Right to Data Portability
</h3>
<p className={`text-sm ${getThemeClasses("text-secondary")} mb-2`}>
You have the right to receive the personal data concerning you, which you have provided to us, in a structured, commonly used and machine-readable format. You also have the right to transmit those data to another controller.
</p>
<p className={`text-sm ${getThemeClasses("text-muted")}`}>
The MapleFile Desktop Application ensures compliance with this right by providing your complete data archive in standard formats (JSON metadata, original file formats).
<p className={`text-sm ${getThemeClasses("text-secondary")}`}>
You have the right to receive your personal data in a structured, commonly used, and machine-readable format. The MapleFile Desktop Application provides your complete data archive in standard formats.
</p>
</section>
{/* Security Notice */}
<section
aria-labelledby="security-heading"
className={`${sectionStyles.warning.container} border-2 ${sectionStyles.warning.border} rounded-xl p-6`}
>
<div className="flex items-start">
<ShieldCheckIcon className={`h-6 w-6 ${sectionStyles.warning.icon} mr-3 flex-shrink-0 mt-0.5`} aria-hidden="true" />
<div className="flex-1">
<h3 id="security-heading" className={`text-lg font-semibold ${sectionStyles.warning.title} mb-2`}>
Security: Verify Your Download
</h3>
<p className={`${sectionStyles.warning.text} mb-3`}>
For your security, always verify the authenticity of downloaded software:
</p>
<ul className={`space-y-2 ${sectionStyles.warning.text}`} role="list" aria-label="Security verification steps">
<li className="flex items-start">
<span className={`${sectionStyles.warning.icon} mr-2`} aria-hidden="true"></span>
<span>Only download from the official Codeberg repository linked above</span>
</li>
<li className="flex items-start">
<span className={`${sectionStyles.warning.icon} mr-2`} aria-hidden="true"></span>
<span>Verify the repository URL matches: <code className={`${sectionStyles.warning.code} px-1 rounded`}>codeberg.org/mapleopentech</code></span>
</li>
<li className="flex items-start">
<span className={`${sectionStyles.warning.icon} mr-2`} aria-hidden="true"></span>
<span>Check that your browser shows a secure HTTPS connection (padlock icon)</span>
</li>
<li className="flex items-start">
<span className={`${sectionStyles.warning.icon} mr-2`} aria-hidden="true"></span>
<span>Review the release notes and commit history before installation</span>
</li>
</ul>
<p className={`text-sm ${sectionStyles.warning.muted} mt-3`}>
<strong>Warning:</strong> Never download MapleFile software from unofficial sources or third-party websites.
</p>
</div>
</div>
</section>
{/* Support Section */}
<section
aria-labelledby="support-heading"
className={`${getThemeClasses("bg-card")} rounded-xl shadow-lg border ${getThemeClasses("border-secondary")} p-6`}
>
<h3 id="support-heading" className={`text-lg font-semibold mb-3 ${getThemeClasses("text-primary")}`}>
Need Help?
</h3>
<p className={`${getThemeClasses("text-secondary")} mb-3`}>
If you encounter any issues installing or using the MapleFile Desktop Application, please:
</p>
<ul className={`space-y-2 ${getThemeClasses("text-secondary")}`} role="list" aria-label="Support options">
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span>Check the README.md file in the repository for detailed documentation</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span>Open an issue on the Codeberg repository for technical support</span>
</li>
<li className="flex items-start">
<span className={`mr-2 ${getThemeClasses("link-primary")}`} aria-hidden="true"></span>
<span>Review the application's help documentation and user guide</span>
</li>
</ul>
</section>
{/* Action Button */}
<div className="flex justify-center py-6">
<a
href="https://codeberg.org/mapleopentech/monorepo/src/branch/main/native/desktop/maplefile"
target="_blank"
rel="noopener noreferrer"
aria-label="Go to MapleFile Desktop Application Repository (opens in new tab)"
>
<Button
variant="primary"
icon={CodeBracketIcon}
>
Go to MapleFile Desktop Application Repository
</Button>
</a>
</div>
</div>
);
}, [getThemeClasses, sectionStyles]);
}, [getThemeClasses]);
// Field sections for DetailLiteView
const fieldSections = useMemo(() => {