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,315 @@
|
|||
// File: monorepo/web/maplefile-frontend/src/pages/Anonymous/Recovery/InitiateRecovery.jsx
|
||||
// UIX version - Theme-aware Account Recovery with UIX components
|
||||
import React, { useState, useCallback, useRef, useEffect } from "react";
|
||||
import { useNavigate, Link } from "react-router";
|
||||
import { useServices } from "../../../services/Services";
|
||||
import { Button, Input, Alert, Card, GDPRFooter, Badge } from "../../../components/UIX";
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
ArrowLeftIcon,
|
||||
LockClosedIcon,
|
||||
ShieldCheckIcon,
|
||||
CheckIcon,
|
||||
ExclamationTriangleIcon,
|
||||
InformationCircleIcon,
|
||||
EnvelopeIcon,
|
||||
KeyIcon,
|
||||
ClockIcon,
|
||||
ExclamationCircleIcon,
|
||||
DocumentTextIcon,
|
||||
ServerIcon,
|
||||
EyeSlashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
const InitiateRecovery = () => {
|
||||
const navigate = useNavigate();
|
||||
const { recoveryManager } = useServices();
|
||||
const [email, setEmail] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
// Refs for cleanup
|
||||
const isMountedRef = useRef(true);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
try {
|
||||
// Validate email
|
||||
if (!email) {
|
||||
throw new Error("Email address is required");
|
||||
}
|
||||
|
||||
if (!email.includes("@")) {
|
||||
throw new Error("Please enter a valid email address");
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("[InitiateRecovery] Starting recovery process");
|
||||
}
|
||||
const response = await recoveryManager.initiateRecovery(email);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("[InitiateRecovery] Recovery initiated successfully");
|
||||
console.log("[InitiateRecovery] Response:", response);
|
||||
console.log("[InitiateRecovery] session_id:", response.session_id);
|
||||
console.log("[InitiateRecovery] encrypted_challenge:", response.encrypted_challenge);
|
||||
}
|
||||
|
||||
// Check if session was actually created (user exists)
|
||||
if (!response.session_id || !response.encrypted_challenge) {
|
||||
// User not found - show generic message for security (prevents email enumeration)
|
||||
if (isMountedRef.current) {
|
||||
setError(
|
||||
"Unable to initiate recovery. Please ensure you entered the correct email address associated with your account. If you need assistance, please contact support.",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigate to verification step
|
||||
navigate("/recovery/verify");
|
||||
} catch (error) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error("[InitiateRecovery] Recovery initiation failed:", error);
|
||||
}
|
||||
if (isMountedRef.current) {
|
||||
setError(error.message);
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [email, recoveryManager, navigate]);
|
||||
|
||||
const handleBackToLogin = useCallback(() => {
|
||||
navigate("/login");
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-red-50 flex flex-col">
|
||||
{/* Navigation */}
|
||||
<nav className="bg-white/95 backdrop-blur-sm border-b border-gray-100">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center py-4">
|
||||
<Link to="/" className="flex items-center group">
|
||||
<div className="flex items-center justify-center h-10 w-10 bg-gradient-to-br from-red-800 to-red-900 rounded-lg mr-3 group-hover:scale-105 transition-transform duration-200">
|
||||
<LockClosedIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<span className="text-2xl font-bold bg-gradient-to-r from-gray-900 to-red-800 bg-clip-text text-transparent">
|
||||
MapleFile
|
||||
</span>
|
||||
</Link>
|
||||
<div className="flex items-center space-x-6">
|
||||
<Link to="/register">
|
||||
<Button variant="ghost" size="md">
|
||||
Need an account?
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
{/* Progress Indicator */}
|
||||
<div className="flex items-center justify-center mb-8">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center justify-center w-8 h-8 bg-gradient-to-r from-red-800 to-red-900 rounded-full text-white text-sm font-bold">
|
||||
1
|
||||
</div>
|
||||
<span className="ml-2 text-sm font-semibold text-gray-900">
|
||||
Email
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-12 h-0.5 bg-gray-300"></div>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center justify-center w-8 h-8 bg-gray-300 rounded-full text-gray-500 text-sm font-bold">
|
||||
2
|
||||
</div>
|
||||
<span className="ml-2 text-sm text-gray-500">Verify</span>
|
||||
</div>
|
||||
<div className="w-12 h-0.5 bg-gray-300"></div>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center justify-center w-8 h-8 bg-gray-300 rounded-full text-gray-500 text-sm font-bold">
|
||||
3
|
||||
</div>
|
||||
<span className="ml-2 text-sm text-gray-500">Reset</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
<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-16 w-16 bg-gradient-to-br from-red-800 to-red-900 rounded-2xl shadow-lg animate-pulse">
|
||||
<KeyIcon className="h-8 w-8 text-white" />
|
||||
</div>
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-red-800 to-red-900 rounded-2xl blur opacity-20 animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-3xl font-black text-gray-900">
|
||||
Account Recovery
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* Important Notice */}
|
||||
<Alert type="warning" className="animate-fade-in-up-delay">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold mb-1">
|
||||
Before You Begin
|
||||
</h3>
|
||||
<p className="text-sm">
|
||||
You'll need your <strong>12-word recovery phrase</strong> to
|
||||
complete this process. Make sure you have it ready before
|
||||
proceeding.
|
||||
</p>
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
{/* Form Card */}
|
||||
<Card className="shadow-2xl animate-fade-in-up-delay">
|
||||
<div className="p-8">
|
||||
{/* Error Message */}
|
||||
{error && (
|
||||
<Alert type="error" className="mb-6 animate-fade-in">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold mb-1">
|
||||
Recovery Error
|
||||
</h3>
|
||||
<p className="text-sm">{error}</p>
|
||||
</div>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
Email Address
|
||||
</label>
|
||||
<Input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={setEmail}
|
||||
placeholder="Enter your email address"
|
||||
required
|
||||
disabled={loading}
|
||||
autoComplete="email"
|
||||
icon={email && email.includes("@") && email.includes(".") ? CheckIcon : EnvelopeIcon}
|
||||
/>
|
||||
<p className="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
We'll use this to verify your identity and guide you through
|
||||
recovery
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || !email.includes("@")}
|
||||
variant="primary"
|
||||
size="lg"
|
||||
fullWidth
|
||||
loading={loading}
|
||||
loadingText="Starting Recovery..."
|
||||
className="group shadow-lg hover:shadow-xl transform hover:scale-105"
|
||||
>
|
||||
<span className="flex items-center">
|
||||
Start Account Recovery
|
||||
<ArrowRightIcon className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform duration-200" />
|
||||
</span>
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Back to Login Button */}
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleBackToLogin}
|
||||
disabled={loading}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
fullWidth
|
||||
>
|
||||
<span className="flex items-center justify-center whitespace-nowrap">
|
||||
<ArrowLeftIcon className="mr-2 h-4 w-4 flex-shrink-0" />
|
||||
Back to Login
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<GDPRFooter containerClassName="bg-white/75" />
|
||||
|
||||
{/* CSS Animations */}
|
||||
<style>{`
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.4s ease-out;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InitiateRecovery;
|
||||
Loading…
Add table
Add a link
Reference in a new issue