Merge version_2 into main #4
180
src/app/auth/forgot-password/page.tsx
Normal file
180
src/app/auth/forgot-password/page.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
"use client"
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import FooterSimple from '@/components/sections/footer/FooterSimple';
|
||||
import { useState } from 'react';
|
||||
import { Mail, Heart, ArrowLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const validateEmail = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
if (!email.trim()) newErrors.email = 'Email is required';
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) newErrors.email = 'Invalid email address';
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateEmail()) return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
// In real app, would make API request here to send reset email
|
||||
console.log('Password reset email sent to:', email);
|
||||
setSubmitted(true);
|
||||
} catch (error) {
|
||||
setErrors({ submit: 'Failed to send reset email. Please try again.' });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="elastic-effect"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="fluid"
|
||||
cardStyle="soft-shadow"
|
||||
primaryButtonStyle="diagonal-gradient"
|
||||
secondaryButtonStyle="solid"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="little hearts"
|
||||
navItems={[
|
||||
{ name: "Features", id: "/" },
|
||||
{ name: "How It Works", id: "/" },
|
||||
{ name: "Stories", id: "/" },
|
||||
{ name: "FAQ", id: "/" },
|
||||
{ name: "Contact", id: "/" }
|
||||
]}
|
||||
button={{ text: "Sign In", href: "/auth/login" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<div className="text-center">
|
||||
<Heart className="mx-auto h-12 w-12 text-primary" />
|
||||
<h2 className="mt-6 text-3xl font-bold tracking-tight">Reset your password</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
We'll send you a link to reset your password
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{submitted ? (
|
||||
<div className="rounded-lg bg-green-50 p-6 text-center space-y-4">
|
||||
<div className="text-green-800">
|
||||
<h3 className="font-semibold text-lg">Check your email</h3>
|
||||
<p className="mt-2 text-sm">
|
||||
We've sent a password reset link to <strong>{email}</strong>
|
||||
</p>
|
||||
<p className="mt-4 text-sm text-green-700">
|
||||
The link will expire in 24 hours. If you don't see the email, check your spam folder.
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/auth/login" className="inline-flex items-center gap-2 text-green-700 hover:text-green-800 font-medium">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Back to sign in
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
|
||||
{errors.submit && (
|
||||
<div className="rounded-lg bg-red-50 p-4 text-sm text-red-800">
|
||||
{errors.submit}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium">
|
||||
Email Address
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<Mail className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
if (errors.email) setErrors(prev => ({ ...prev, email: '' }));
|
||||
}}
|
||||
className="pl-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
|
||||
>
|
||||
{isLoading ? 'Sending...' : 'Send Reset Link'}
|
||||
</button>
|
||||
|
||||
<div className="text-center">
|
||||
<Link href="/auth/login" className="inline-flex items-center gap-2 text-sm text-primary hover:text-primary/90">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Back to sign in
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterSimple
|
||||
columns={[
|
||||
{
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/" },
|
||||
{ label: "How It Works", href: "/" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Blog", href: "/" },
|
||||
{ label: "Careers", href: "/" },
|
||||
{ label: "Contact", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Legal", items: [
|
||||
{ label: "Privacy Policy", href: "/" },
|
||||
{ label: "Terms of Service", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
bottomLeftText="© 2025 little hearts. All rights reserved."
|
||||
bottomRightText="Crafted with care for couples everywhere."
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
224
src/app/auth/login/page.tsx
Normal file
224
src/app/auth/login/page.tsx
Normal file
@@ -0,0 +1,224 @@
|
||||
"use client"
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import FooterSimple from '@/components/sections/footer/FooterSimple';
|
||||
import { useState } from 'react';
|
||||
import { Mail, Lock, Heart, Eye, EyeOff } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function LoginPage() {
|
||||
const [formData, setFormData] = useState({
|
||||
email: '',
|
||||
password: ''
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
if (!formData.email.trim()) newErrors.email = 'Email is required';
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) newErrors.email = 'Invalid email address';
|
||||
if (!formData.password) newErrors.password = 'Password is required';
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
// In real app, would make API request here
|
||||
console.log('Login attempt:', { ...formData, rememberMe });
|
||||
// Redirect to dashboard
|
||||
} catch (error) {
|
||||
setErrors({ submit: 'Failed to sign in. Please check your credentials.' });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
if (errors[name]) {
|
||||
setErrors(prev => ({ ...prev, [name]: '' }));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="elastic-effect"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="fluid"
|
||||
cardStyle="soft-shadow"
|
||||
primaryButtonStyle="diagonal-gradient"
|
||||
secondaryButtonStyle="solid"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="little hearts"
|
||||
navItems={[
|
||||
{ name: "Features", id: "/" },
|
||||
{ name: "How It Works", id: "/" },
|
||||
{ name: "Stories", id: "/" },
|
||||
{ name: "FAQ", id: "/" },
|
||||
{ name: "Contact", id: "/" }
|
||||
]}
|
||||
button={{ text: "Sign Up", href: "/auth/signup" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<div className="text-center">
|
||||
<Heart className="mx-auto h-12 w-12 text-primary" />
|
||||
<h2 className="mt-6 text-3xl font-bold tracking-tight">Welcome back</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Sign in to continue your relationship journey
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
|
||||
{errors.submit && (
|
||||
<div className="rounded-lg bg-red-50 p-4 text-sm text-red-800">
|
||||
{errors.submit}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Email Field */}
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium">
|
||||
Email Address
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<Mail className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className="pl-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="password" className="block text-sm font-medium">
|
||||
Password
|
||||
</label>
|
||||
<Link href="/auth/forgot-password" className="text-sm text-primary hover:text-primary/90">
|
||||
Forgot password?
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-1 relative">
|
||||
<Lock className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
className="pl-10 pr-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-3 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
|
||||
</button>
|
||||
</div>
|
||||
{errors.password && <p className="mt-1 text-sm text-red-600">{errors.password}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Remember Me Checkbox */}
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="remember"
|
||||
name="remember"
|
||||
type="checkbox"
|
||||
checked={rememberMe}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
className="h-4 w-4 text-primary focus:ring-primary border-input rounded"
|
||||
/>
|
||||
<label htmlFor="remember" className="ml-2 block text-sm text-muted-foreground">
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
|
||||
>
|
||||
{isLoading ? 'Signing in...' : 'Sign In'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Sign Up Link */}
|
||||
<p className="text-center text-sm text-muted-foreground">
|
||||
Don't have an account?{' '}
|
||||
<Link href="/auth/signup" className="font-medium text-primary hover:text-primary/90">
|
||||
Create one
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterSimple
|
||||
columns={[
|
||||
{
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/" },
|
||||
{ label: "How It Works", href: "/" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Blog", href: "/" },
|
||||
{ label: "Careers", href: "/" },
|
||||
{ label: "Contact", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Legal", items: [
|
||||
{ label: "Privacy Policy", href: "/" },
|
||||
{ label: "Terms of Service", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
bottomLeftText="© 2025 little hearts. All rights reserved."
|
||||
bottomRightText="Crafted with care for couples everywhere."
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
256
src/app/auth/signup/page.tsx
Normal file
256
src/app/auth/signup/page.tsx
Normal file
@@ -0,0 +1,256 @@
|
||||
"use client"
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import FooterSimple from '@/components/sections/footer/FooterSimple';
|
||||
import { useState } from 'react';
|
||||
import { Mail, Lock, User, Heart, Eye, EyeOff } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function SignupPage() {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
if (!formData.name.trim()) newErrors.name = 'Name is required';
|
||||
if (!formData.email.trim()) newErrors.email = 'Email is required';
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) newErrors.email = 'Invalid email address';
|
||||
if (!formData.password) newErrors.password = 'Password is required';
|
||||
else if (formData.password.length < 8) newErrors.password = 'Password must be at least 8 characters';
|
||||
if (formData.password !== formData.confirmPassword) newErrors.confirmPassword = 'Passwords do not match';
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
// In real app, would make API request here
|
||||
console.log('Account created:', formData);
|
||||
// Redirect to dashboard or login
|
||||
} catch (error) {
|
||||
setErrors({ submit: 'Failed to create account. Please try again.' });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
if (errors[name]) {
|
||||
setErrors(prev => ({ ...prev, [name]: '' }));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="elastic-effect"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="fluid"
|
||||
cardStyle="soft-shadow"
|
||||
primaryButtonStyle="diagonal-gradient"
|
||||
secondaryButtonStyle="solid"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="little hearts"
|
||||
navItems={[
|
||||
{ name: "Features", id: "/" },
|
||||
{ name: "How It Works", id: "/" },
|
||||
{ name: "Stories", id: "/" },
|
||||
{ name: "FAQ", id: "/" },
|
||||
{ name: "Contact", id: "/" }
|
||||
]}
|
||||
button={{ text: "Sign In", href: "/auth/login" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<div className="text-center">
|
||||
<Heart className="mx-auto h-12 w-12 text-primary" />
|
||||
<h2 className="mt-6 text-3xl font-bold tracking-tight">Create your account</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Join little hearts and strengthen your relationship
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
|
||||
{errors.submit && (
|
||||
<div className="rounded-lg bg-red-50 p-4 text-sm text-red-800">
|
||||
{errors.submit}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Name Field */}
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium">
|
||||
Full Name
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<User className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className="pl-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
{/* Email Field */}
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium">
|
||||
Email Address
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<Mail className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className="pl-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium">
|
||||
Password
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<Lock className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
className="pl-10 pr-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-3 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
|
||||
</button>
|
||||
</div>
|
||||
{errors.password && <p className="mt-1 text-sm text-red-600">{errors.password}</p>}
|
||||
</div>
|
||||
|
||||
{/* Confirm Password Field */}
|
||||
<div>
|
||||
<label htmlFor="confirmPassword" className="block text-sm font-medium">
|
||||
Confirm Password
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<Lock className="absolute left-3 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<input
|
||||
id="confirmPassword"
|
||||
name="confirmPassword"
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleChange}
|
||||
className="pl-10 pr-10 w-full px-4 py-2 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
className="absolute right-3 top-3 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{showConfirmPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
|
||||
</button>
|
||||
</div>
|
||||
{errors.confirmPassword && <p className="mt-1 text-sm text-red-600">{errors.confirmPassword}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
|
||||
>
|
||||
{isLoading ? 'Creating account...' : 'Create Account'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Sign In Link */}
|
||||
<p className="text-center text-sm text-muted-foreground">
|
||||
Already have an account?{' '}
|
||||
<Link href="/auth/login" className="font-medium text-primary hover:text-primary/90">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterSimple
|
||||
columns={[
|
||||
{
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/" },
|
||||
{ label: "How It Works", href: "/" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Blog", href: "/" },
|
||||
{ label: "Careers", href: "/" },
|
||||
{ label: "Contact", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Legal", items: [
|
||||
{ label: "Privacy Policy", href: "/" },
|
||||
{ label: "Terms of Service", href: "/" },
|
||||
{ label: "Security", href: "/" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
bottomLeftText="© 2025 little hearts. All rights reserved."
|
||||
bottomRightText="Crafted with care for couples everywhere."
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import TestimonialCardTwelve from '@/components/sections/testimonial/Testimonial
|
||||
import FaqSplitText from '@/components/sections/faq/FaqSplitText';
|
||||
import ContactFaq from '@/components/sections/contact/ContactFaq';
|
||||
import FooterSimple from '@/components/sections/footer/FooterSimple';
|
||||
import { Heart, TrendingUp, Sparkles, Shield } from 'lucide-react';
|
||||
import { Heart, TrendingUp, Sparkles, Shield, Lock, KeyRound, Eye, Database } from 'lucide-react';
|
||||
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
@@ -32,6 +32,7 @@ export default function LandingPage() {
|
||||
{ name: "Features", id: "features" },
|
||||
{ name: "How It Works", id: "about" },
|
||||
{ name: "Stories", id: "testimonials" },
|
||||
{ name: "Security", id: "security" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
]}
|
||||
@@ -120,6 +121,36 @@ export default function LandingPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="security" data-section="security">
|
||||
<FeatureCardNine
|
||||
title="Privacy & Security You Can Trust"
|
||||
description="Your relationship is sacred. Every conversation is protected with enterprise-grade security, privacy-first architecture, and granular permission controls designed specifically for intimate partnerships."
|
||||
tag="Data Protection"
|
||||
tagAnimation="slide-up"
|
||||
textboxLayout="default"
|
||||
useInvertedBackground={true}
|
||||
animationType="scale-rotate"
|
||||
showStepNumbers={true}
|
||||
features={[
|
||||
{
|
||||
id: 1,
|
||||
title: "End-to-End Encryption", description: "Military-grade encryption (AES-256) secures all conversations, mood data, and milestones. Only you and your partner hold the encryption keys. No one—not even our team—can access your private conversations without your explicit consent.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-photo/security-guard-standing-vigilant-night_23-2150135779.jpg" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=1" }
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Granular Permission Management", description: "Control exactly what data each partner can access and modify. Set permissions for conversations, mood tracking, milestone sharing, and invite management. Changes take effect instantly. Both partners can audit access logs anytime.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-photo/afro-american-couple-park_23-2148012796.jpg?_wi=2" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=2" }
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Secure Data Storage & Deletion", description: "Data is stored in isolated, encrypted containers with immutable audit trails. Choose automatic expiration for sensitive conversations (7-90 days). Full data export or permanent deletion available anytime—your data is yours to keep or remove.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=3" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-photo/afro-american-couple-park_23-2148012796.jpg?_wi=3" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
<TestimonialCardTwelve
|
||||
cardTitle="Thousands of couples are already strengthening their relationships on little hearts"
|
||||
@@ -211,8 +242,8 @@ export default function LandingPage() {
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "#features" },
|
||||
{ label: "How It Works", href: "#about" },
|
||||
{ label: "Pricing", href: "#" },
|
||||
{ label: "Security", href: "#" }
|
||||
{ label: "Security", href: "#security" },
|
||||
{ label: "Pricing", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -235,7 +266,7 @@ export default function LandingPage() {
|
||||
title: "Legal", items: [
|
||||
{ label: "Privacy Policy", href: "#" },
|
||||
{ label: "Terms of Service", href: "#" },
|
||||
{ label: "Security", href: "#" },
|
||||
{ label: "Security", href: "#security" },
|
||||
{ label: "Cookies", href: "#" }
|
||||
]
|
||||
}
|
||||
|
||||
148
src/app/profile/page.tsx
Normal file
148
src/app/profile/page.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
"use client"
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import HeroBillboardTestimonial from '@/components/sections/hero/HeroBillboardTestimonial';
|
||||
import FeatureCardNine from '@/components/sections/feature/FeatureCardNine';
|
||||
import FooterSimple from '@/components/sections/footer/FooterSimple';
|
||||
import { Heart, User, Lock, Users } from 'lucide-react';
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="elastic-effect"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="rounded"
|
||||
contentWidth="small"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="fluid"
|
||||
cardStyle="soft-shadow"
|
||||
primaryButtonStyle="diagonal-gradient"
|
||||
secondaryButtonStyle="solid"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="little hearts"
|
||||
navItems={[
|
||||
{ name: "Features", id: "features" },
|
||||
{ name: "How It Works", id: "about" },
|
||||
{ name: "Stories", id: "testimonials" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Profile", id: "profile" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="profile-hero" data-section="profile-hero">
|
||||
<HeroBillboardTestimonial
|
||||
title="Create Your Profile & Connect"
|
||||
description="Personalize your experience with a custom avatar, complete your profile setup, and connect securely with your partner. Your journey together starts here."
|
||||
tag="Profile Setup"
|
||||
tagAnimation="slide-up"
|
||||
buttons={[
|
||||
{ text: "Start Profile Setup", href: "#profile-setup" },
|
||||
{ text: "Back Home", href: "/" }
|
||||
]}
|
||||
buttonAnimation="slide-up"
|
||||
imageSrc="http://img.b2bpic.net/free-photo/young-couple-talking-while-having-relationship-problems-bedroom_637285-3339.jpg"
|
||||
imageAlt="Profile setup illustration"
|
||||
mediaAnimation="slide-up"
|
||||
testimonials={[
|
||||
{
|
||||
name: "Sarah & Mark", handle: "First-Time Users", testimonial: "The profile setup took just 2 minutes. Choosing our avatars together felt like the first step of our journey.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/portrait-young-woman-shirt-beach_1303-9826.jpg"
|
||||
},
|
||||
{
|
||||
name: "Alex & Jordan", handle: "Long-Distance Couple", testimonial: "The partner link feature is genius. No complicated email confirmations. We were connected in seconds.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-cheerful-winner_1149-192.jpg"
|
||||
},
|
||||
{
|
||||
name: "Emma & Chris", handle: "New Relationship", testimonial: "Having a private space that's personalized to us made it feel official. Like we were really investing in us.", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/expressive-young-girl-posing-studio_176474-79855.jpg"
|
||||
},
|
||||
{
|
||||
name: "Lisa & David", handle: "Long-Term Couple", testimonial: "We loved creating matching avatars. It's a subtle touch that makes our space feel like 'ours.'", rating: 5,
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/portrait-handsome-businessman-wearing-glasses_329181-677.jpg"
|
||||
}
|
||||
]}
|
||||
testimonialRotationInterval={5000}
|
||||
useInvertedBackground={false}
|
||||
background={{ variant: "glowing-orb" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="profile-setup" data-section="profile-setup">
|
||||
<FeatureCardNine
|
||||
title="Your Profile Setup Journey"
|
||||
description="We've designed a smooth onboarding flow that respects your privacy and makes setup feel personal and intentional."
|
||||
tag="3 Simple Steps"
|
||||
tagAnimation="slide-up"
|
||||
textboxLayout="default"
|
||||
useInvertedBackground={true}
|
||||
animationType="scale-rotate"
|
||||
showStepNumbers={true}
|
||||
features={[
|
||||
{
|
||||
id: 1,
|
||||
title: "Avatar Selection", description: "Choose from 20+ avatar styles that represent you. Pick a style that feels authentic—from minimalist to expressive. Your avatar is your identity in your shared space and appears in all your interactions.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-photo/afro-american-couple-park_23-2148012796.jpg?_wi=1" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=1" }
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Partner Connection", description: "One of you generates a secure 7-day invite link. Share it directly with your partner. They sign up and scan the link to connect instantly. No email verification needed. No data collection beyond what you choose to share.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-photo/afro-american-couple-park_23-2148012796.jpg?_wi=2" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=2" }
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Profile Customization", description: "Add a profile bio, relationship anniversary, and shared goals. Customize your relationship profile with your story. Set privacy preferences for what you share outside the app. Your profile is yours to shape—as minimal or detailed as you want.", phoneOne: { imageSrc: "http://img.b2bpic.net/free-vector/worksheet-template-design_742173-16185.jpg?_wi=3" },
|
||||
phoneTwo: { imageSrc: "http://img.b2bpic.net/free-photo/afro-american-couple-park_23-2148012796.jpg?_wi=3" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterSimple
|
||||
columns={[
|
||||
{
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/" },
|
||||
{ label: "How It Works", href: "/" },
|
||||
{ label: "Pricing", href: "#" },
|
||||
{ label: "Security", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Contact", href: "/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Resources", items: [
|
||||
{ label: "Help Center", href: "#" },
|
||||
{ label: "FAQ", href: "/" },
|
||||
{ label: "Community", href: "#" },
|
||||
{ label: "Relationship Tips", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Legal", items: [
|
||||
{ label: "Privacy Policy", href: "#" },
|
||||
{ label: "Terms of Service", href: "#" },
|
||||
{ label: "Security", href: "#" },
|
||||
{ label: "Cookies", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
bottomLeftText="© 2025 little hearts. All rights reserved."
|
||||
bottomRightText="Crafted with care for couples everywhere."
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user