Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,424 @@
|
|||
// File: monorepo/web/maplefile-frontend/src/pages/Anonymous/Register/VerifySuccess.jsx
|
||||
// UIX version - Registration Success (100% UIX Components)
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Alert,
|
||||
GDPRFooter,
|
||||
Navigation,
|
||||
PageContainer,
|
||||
} from "../../../components/UIX";
|
||||
import { useUIXTheme } from "../../../components/UIX/themes/useUIXTheme";
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
CheckCircleIcon,
|
||||
LockClosedIcon,
|
||||
ShieldCheckIcon,
|
||||
UserIcon,
|
||||
DocumentCheckIcon,
|
||||
KeyIcon,
|
||||
CloudArrowUpIcon,
|
||||
SparklesIcon,
|
||||
InformationCircleIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
const VerifySuccess = () => {
|
||||
const navigate = useNavigate();
|
||||
const { getThemeClasses } = useUIXTheme();
|
||||
|
||||
// Refs for cleanup
|
||||
const isMountedRef = useRef(true);
|
||||
const timerRef = useRef(null);
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [userRole, setUserRole] = useState(null);
|
||||
const [countdown, setCountdown] = useState(10);
|
||||
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
|
||||
// Get data from sessionStorage
|
||||
const registeredEmail = sessionStorage.getItem("registeredEmail");
|
||||
const storedUserRole = sessionStorage.getItem("userRole");
|
||||
|
||||
if (!registeredEmail || !storedUserRole) {
|
||||
// Redirect back to registration if no data found
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("[VerifySuccess] No registration data found, redirecting to register");
|
||||
}
|
||||
navigate("/register");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate email format to prevent XSS/tampering
|
||||
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (!emailRegex.test(registeredEmail)) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error("[VerifySuccess] Invalid email format in sessionStorage");
|
||||
}
|
||||
sessionStorage.removeItem("registeredEmail");
|
||||
sessionStorage.removeItem("userRole");
|
||||
navigate("/register");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate user role (must be 1, 2, or 3)
|
||||
const roleNumber = parseInt(storedUserRole, 10);
|
||||
if (isNaN(roleNumber) || roleNumber < 1 || roleNumber > 3) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error("[VerifySuccess] Invalid user role in sessionStorage");
|
||||
}
|
||||
sessionStorage.removeItem("registeredEmail");
|
||||
sessionStorage.removeItem("userRole");
|
||||
navigate("/register");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMountedRef.current) {
|
||||
setEmail(registeredEmail);
|
||||
setUserRole(roleNumber);
|
||||
}
|
||||
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
if (timerRef.current) {
|
||||
clearInterval(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, [navigate]);
|
||||
|
||||
// Auto-redirect countdown
|
||||
useEffect(() => {
|
||||
timerRef.current = setInterval(() => {
|
||||
setCountdown((prev) => {
|
||||
if (prev <= 1) {
|
||||
navigate("/login");
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
clearInterval(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// navigate is stable but we only want this to run once on mount
|
||||
|
||||
const getUserRoleText = useCallback((role) => {
|
||||
switch (role) {
|
||||
case 1:
|
||||
return {
|
||||
text: "Root User",
|
||||
className: "bg-purple-100 text-purple-700",
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
text: "Company User",
|
||||
className: "bg-blue-100 text-blue-700",
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
text: "Individual User",
|
||||
className: "bg-green-100 text-green-700",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
text: "Unknown",
|
||||
className: "bg-gray-100 text-gray-700",
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleRegisterAnother = useCallback(() => {
|
||||
// Clear session storage
|
||||
sessionStorage.removeItem("registrationResult");
|
||||
sessionStorage.removeItem("registeredEmail");
|
||||
sessionStorage.removeItem("userRole");
|
||||
navigate("/register");
|
||||
}, [navigate]);
|
||||
|
||||
const handleGoToLogin = useCallback(() => {
|
||||
// Clear session storage
|
||||
sessionStorage.removeItem("registrationResult");
|
||||
sessionStorage.removeItem("registeredEmail");
|
||||
sessionStorage.removeItem("userRole");
|
||||
// Navigate to login page
|
||||
navigate("/login");
|
||||
}, [navigate]);
|
||||
|
||||
// Memoize role info
|
||||
const roleInfo = useMemo(() => getUserRoleText(userRole), [userRole, getUserRoleText]);
|
||||
|
||||
// Memoize navigation links
|
||||
const navLinks = useMemo(() => [], []); // No links on success page
|
||||
|
||||
if (!email || userRole === null) {
|
||||
return (
|
||||
<PageContainer>
|
||||
<Card className="text-center p-8 max-w-md mx-auto mt-20">
|
||||
<h2 className={`text-2xl font-bold ${getThemeClasses("text-primary")} mb-4`}>
|
||||
Loading...
|
||||
</h2>
|
||||
<p className={getThemeClasses("text-secondary")}>
|
||||
Loading success page...
|
||||
</p>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer showBlobs>
|
||||
{/* Navigation */}
|
||||
<Navigation icon={LockClosedIcon} logoText="MapleFile" links={navLinks}>
|
||||
<div className={`flex items-center space-x-2 text-sm ${getThemeClasses("text-success")}`}>
|
||||
<CheckCircleIcon className="h-5 w-5" />
|
||||
<span className="font-semibold">Registration Complete</span>
|
||||
</div>
|
||||
</Navigation>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
<div className="max-w-2xl w-full space-y-8">
|
||||
{/* Success Animation */}
|
||||
<div className="text-center animate-fade-in-up">
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="relative">
|
||||
<div className="flex items-center justify-center h-20 w-20 bg-gradient-to-br from-green-500 to-green-600 rounded-full shadow-lg animate-bounce-once">
|
||||
<CheckCircleIcon className="h-12 w-12 text-white" />
|
||||
</div>
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-green-500 to-green-600 rounded-full blur opacity-30 animate-pulse"></div>
|
||||
<div className="absolute -top-1 -right-1">
|
||||
<SparklesIcon className="h-6 w-6 text-yellow-500 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className={`text-4xl font-black ${getThemeClasses("text-primary")} mb-2`}>
|
||||
Welcome to MapleFile!
|
||||
</h2>
|
||||
<p className={`text-xl ${getThemeClasses("text-secondary")}`}>
|
||||
Your account has been successfully created
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* GDPR Notice */}
|
||||
<Alert type="info">
|
||||
<div className="text-xs space-y-1">
|
||||
<p className="font-semibold">Data Processing Notice</p>
|
||||
<p>
|
||||
Your account data is stored securely and processed in compliance with GDPR.
|
||||
You have the right to access, rectify, or delete your data at any time.
|
||||
(Legal basis: Contract - necessary for service provision)
|
||||
</p>
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
{/* Account Details Card */}
|
||||
<Card className="shadow-2xl animate-fade-in-up-delay">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className={`flex items-center justify-center h-12 w-12 ${getThemeClasses("bg-gradient-secondary")} rounded-xl mr-4`}>
|
||||
<UserIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<h3 className={`text-xl font-semibold ${getThemeClasses("text-primary")}`}>
|
||||
Account Details
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className={`flex items-center justify-between p-3 rounded-lg ${getThemeClasses("bg-card")}`}>
|
||||
<span className={`text-sm font-medium ${getThemeClasses("text-secondary")}`}>
|
||||
Email Address
|
||||
</span>
|
||||
<span className={`text-sm font-mono ${getThemeClasses("text-primary")}`}>{email}</span>
|
||||
</div>
|
||||
<div className={`flex items-center justify-between p-3 rounded-lg ${getThemeClasses("bg-card")}`}>
|
||||
<span className={`text-sm font-medium ${getThemeClasses("text-secondary")}`}>
|
||||
User Role
|
||||
</span>
|
||||
<span className={`text-sm font-semibold px-3 py-1 rounded-full ${roleInfo.className}`}>
|
||||
{roleInfo.text}
|
||||
</span>
|
||||
</div>
|
||||
<div className={`flex items-center justify-between p-3 rounded-lg ${getThemeClasses("bg-card")}`}>
|
||||
<span className={`text-sm font-medium ${getThemeClasses("text-secondary")}`}>
|
||||
Account Status
|
||||
</span>
|
||||
<span className={`text-sm font-semibold ${getThemeClasses("text-success")} flex items-center`}>
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1" />
|
||||
Active
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* What's Next Section */}
|
||||
<Alert type="info" className="animate-fade-in-up-delay-2">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
What's Next?
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="flex items-start">
|
||||
<div className={`flex items-center justify-center h-8 w-8 ${getThemeClasses("bg-card")} rounded-lg mr-3 flex-shrink-0`}>
|
||||
<KeyIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className={`text-sm font-semibold ${getThemeClasses("text-primary")}`}>
|
||||
Secure Your Recovery Phrase
|
||||
</h4>
|
||||
<p className={`text-xs ${getThemeClasses("text-secondary")} mt-1`}>
|
||||
Keep your 12-word recovery phrase in a safe place
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<div className={`flex items-center justify-center h-8 w-8 ${getThemeClasses("bg-card")} rounded-lg mr-3 flex-shrink-0`}>
|
||||
<CloudArrowUpIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className={`text-sm font-semibold ${getThemeClasses("text-primary")}`}>
|
||||
Start Uploading Files
|
||||
</h4>
|
||||
<p className={`text-xs ${getThemeClasses("text-secondary")} mt-1`}>
|
||||
Your files are encrypted before leaving your device
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<div className={`flex items-center justify-center h-8 w-8 ${getThemeClasses("bg-card")} rounded-lg mr-3 flex-shrink-0`}>
|
||||
<DocumentCheckIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className={`text-sm font-semibold ${getThemeClasses("text-primary")}`}>
|
||||
Organize Your Documents
|
||||
</h4>
|
||||
<p className={`text-xs ${getThemeClasses("text-secondary")} mt-1`}>
|
||||
Create collections to organize your files
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<div className={`flex items-center justify-center h-8 w-8 ${getThemeClasses("bg-card")} rounded-lg mr-3 flex-shrink-0`}>
|
||||
<ShieldCheckIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className={`text-sm font-semibold ${getThemeClasses("text-primary")}`}>
|
||||
Share Securely
|
||||
</h4>
|
||||
<p className={`text-xs ${getThemeClasses("text-secondary")} mt-1`}>
|
||||
Share files with end-to-end encryption
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
{/* Security Reminder */}
|
||||
<Alert type="warning" className="animate-fade-in-up-delay-3">
|
||||
<div className="flex items-start">
|
||||
<ShieldCheckIcon className="h-6 w-6 mr-3 flex-shrink-0 mt-1" />
|
||||
<div className="flex-1">
|
||||
<h3 className="text-sm font-semibold mb-2">
|
||||
Security Reminder
|
||||
</h3>
|
||||
<p className="text-sm">
|
||||
Your account uses end-to-end encryption. Your recovery phrase
|
||||
is the <strong>only way</strong> to recover your data if you
|
||||
forget your password. Make sure it's stored securely in a
|
||||
physical location!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
{/* Auto-redirect Notice */}
|
||||
<div className={`text-center p-4 rounded-lg ${getThemeClasses("bg-card")} animate-fade-in-up-delay-3`}>
|
||||
<p className={`text-sm ${getThemeClasses("text-secondary")}`}>
|
||||
Redirecting to login page in{" "}
|
||||
<span className={`font-semibold ${getThemeClasses("text-primary")}`}>{countdown}</span>{" "}
|
||||
seconds...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 animate-fade-in-up-delay-3">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleRegisterAnother}
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
>
|
||||
Register Another Account
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleGoToLogin}
|
||||
variant="primary"
|
||||
fullWidth
|
||||
>
|
||||
<LockClosedIcon className="mr-2 h-5 w-5" />
|
||||
Sign In Now
|
||||
<ArrowRightIcon className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<GDPRFooter />
|
||||
|
||||
{/* CSS Animations */}
|
||||
<style>{`
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce-once {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fade-in-up 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animate-fade-in-up-delay {
|
||||
animation: fade-in-up 0.6s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
.animate-fade-in-up-delay-2 {
|
||||
animation: fade-in-up 0.6s ease-out 0.4s both;
|
||||
}
|
||||
|
||||
.animate-fade-in-up-delay-3 {
|
||||
animation: fade-in-up 0.6s ease-out 0.6s both;
|
||||
}
|
||||
|
||||
.animate-bounce-once {
|
||||
animation: bounce-once 0.8s ease-out;
|
||||
}
|
||||
`}</style>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default VerifySuccess;
|
||||
Loading…
Add table
Add a link
Reference in a new issue