434 lines
15 KiB
JavaScript
434 lines
15 KiB
JavaScript
// 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) {
|
|
return 0;
|
|
}
|
|
return prev - 1;
|
|
});
|
|
}, 1000);
|
|
|
|
return () => {
|
|
if (timerRef.current) {
|
|
clearInterval(timerRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// Separate effect to handle navigation when countdown reaches 0
|
|
useEffect(() => {
|
|
if (countdown === 0) {
|
|
// Clear session storage before redirecting
|
|
sessionStorage.removeItem("registrationResult");
|
|
sessionStorage.removeItem("registeredEmail");
|
|
sessionStorage.removeItem("userRole");
|
|
navigate("/login");
|
|
}
|
|
}, [countdown, navigate]);
|
|
|
|
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
|
|
className="whitespace-nowrap"
|
|
>
|
|
<LockClosedIcon className="mr-2 h-5 w-5 inline-block" />
|
|
<span className="inline-block">Sign In Now</span>
|
|
<ArrowRightIcon className="ml-2 h-4 w-4 inline-block" />
|
|
</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;
|