feat(frontend): add unified header and footer to public pages
Implemented consistent navigation across all public-facing pages with a reusable layout system. Created PublicLayout component that wraps pages with a header containing the logo and a footer with navigation links. Changes: - Created PublicHeader component with logo linking to homepage - Created PublicFooter component with Product, Account, and Support sections - Created PublicLayout wrapper component using flex layout - Updated all public pages to use PublicLayout: - LoginPage, RegisterPage, ForgotPasswordPage, ResetPasswordPage - VerifyEmailPage, ContactPage, AboutUsPage, HowItWorksPage - NotFoundPage - Fixed gradient background pages to use min-h-full for proper height - Fixed content pages to avoid min-h-screen conflicts with flex-grow - Updated About Us content
This commit is contained in:
@@ -1,26 +1,9 @@
|
||||
# About Us
|
||||
Hi, I’m Radek – a software engineer, a West Coast Swing dancer and the person behind **spotlight.cam**.
|
||||
|
||||
## Lorem Ipsum
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Spotlight.cam is a project built by someone who actually stands in the same registration lines, dances in the same heats and scrolls through the same event pages as you :P
|
||||
|
||||
## Dolor Sit Amet
|
||||
|
||||
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat:
|
||||
If we ever meet at an event, I’ll probably be somewhere near the dance floor, probably pressing “record” on someone’s spotlight.
|
||||
|
||||
- **Duis aute irure** - Dolor in reprehenderit in voluptate velit esse cillum dolore
|
||||
- **Eu fugiat nulla** - Pariatur excepteur sint occaecat cupidatat non proident
|
||||
- **Sunt in culpa** - Qui officia deserunt mollit anim id est laborum
|
||||
- **Sed ut perspiciatis** - Unde omnis iste natus error sit voluptatem
|
||||
|
||||
## Consectetur Adipiscing
|
||||
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
|
||||
|
||||
## Nemo Enim
|
||||
|
||||
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
|
||||
|
||||
---
|
||||
|
||||
*For questions or feedback, [contact us](/contact).*
|
||||
To be fair, the original idea for this service was actually dropped on me by a friend who was tired of hunting for someone to film her dances – I just did what any backend developer / DevOps / Linux admin would do: said “okay, that shouldn’t be that hard… right?”, opened my editor, set up a few servers and scripts… and suddenly we had a new project on our hands 😅 I also had a not-so-secret teammate: AI. Without it, this would probably still be stuck in my “one day” folder for about a year 😄
|
||||
71
frontend/src/components/layout/PublicFooter.jsx
Normal file
71
frontend/src/components/layout/PublicFooter.jsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PublicFooter = () => {
|
||||
return (
|
||||
<footer className="bg-white border-t border-gray-200 mt-auto">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Product */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 uppercase tracking-wider mb-4">
|
||||
Product
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link to="/about-us" className="text-gray-600 hover:text-primary-600 transition-colors">
|
||||
About Us
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/how-it-works" className="text-gray-600 hover:text-primary-600 transition-colors">
|
||||
How It Works
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Account */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 uppercase tracking-wider mb-4">
|
||||
Account
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link to="/login" className="text-gray-600 hover:text-primary-600 transition-colors">
|
||||
Sign In
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/register" className="text-gray-600 hover:text-primary-600 transition-colors">
|
||||
Register
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Support */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 uppercase tracking-wider mb-4">
|
||||
Support
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link to="/contact" className="text-gray-600 hover:text-primary-600 transition-colors">
|
||||
Contact Us
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 pt-8 border-t border-gray-200">
|
||||
<p className="text-center text-gray-500 text-sm">
|
||||
© {new Date().getFullYear()} spotlight.cam. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicFooter;
|
||||
17
frontend/src/components/layout/PublicHeader.jsx
Normal file
17
frontend/src/components/layout/PublicHeader.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Video } from 'lucide-react';
|
||||
|
||||
const PublicHeader = () => {
|
||||
return (
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<Link to="/" className="flex items-center space-x-2 hover:opacity-80 transition-opacity">
|
||||
<Video className="w-8 h-8 text-primary-600" />
|
||||
<span className="text-xl font-bold text-gray-900">spotlight.cam</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicHeader;
|
||||
16
frontend/src/components/layout/PublicLayout.jsx
Normal file
16
frontend/src/components/layout/PublicLayout.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import PublicHeader from './PublicHeader';
|
||||
import PublicFooter from './PublicFooter';
|
||||
|
||||
const PublicLayout = ({ children }) => {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<PublicHeader />
|
||||
<main className="flex-grow">
|
||||
{children}
|
||||
</main>
|
||||
<PublicFooter />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicLayout;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
export default function AboutUsPage() {
|
||||
const [content, setContent] = useState('');
|
||||
@@ -29,32 +29,32 @@ export default function AboutUsPage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 flex items-center justify-center py-12">
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 border-4 border-primary-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 flex items-center justify-center py-12">
|
||||
<div className="text-center">
|
||||
<p className="text-red-600">{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<article className="bg-white rounded-lg shadow-sm p-8 md:p-12 prose prose-lg max-w-none">
|
||||
<ReactMarkdown
|
||||
@@ -86,6 +86,6 @@ export default function AboutUsPage() {
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Send, Mail, User, MessageSquare } from 'lucide-react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { publicAPI } from '../services/api';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
export default function ContactPage() {
|
||||
const { user } = useAuth();
|
||||
@@ -125,7 +125,7 @@ export default function ContactPage() {
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<Layout>
|
||||
<PublicLayout>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center px-4">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-sm p-8 text-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
@@ -138,12 +138,12 @@ export default function ContactPage() {
|
||||
<p className="text-sm text-gray-500">Redirecting to homepage...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<PublicLayout>
|
||||
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{/* Header */}
|
||||
@@ -312,6 +312,6 @@ export default function ContactPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { authAPI } from '../services/api';
|
||||
import { Video, Mail, ArrowLeft, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
const ForgotPasswordPage = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -26,7 +27,8 @@ const ForgotPasswordPage = () => {
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
|
||||
@@ -51,11 +53,13 @@ const ForgotPasswordPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<Video className="w-16 h-16 text-primary-600 mb-4" />
|
||||
@@ -128,6 +132,7 @@ const ForgotPasswordPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
export default function HowItWorksPage() {
|
||||
const [content, setContent] = useState('');
|
||||
@@ -29,32 +29,32 @@ export default function HowItWorksPage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 flex items-center justify-center py-12">
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 border-4 border-primary-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 flex items-center justify-center py-12">
|
||||
<div className="text-center">
|
||||
<p className="text-red-600">{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<article className="bg-white rounded-lg shadow-sm p-8 md:p-12 prose prose-lg max-w-none">
|
||||
<ReactMarkdown
|
||||
@@ -86,6 +86,6 @@ export default function HowItWorksPage() {
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Video, Mail, Lock } from 'lucide-react';
|
||||
import FormInput from '../components/common/FormInput';
|
||||
import LoadingButton from '../components/common/LoadingButton';
|
||||
import Alert from '../components/common/Alert';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
const LoginPage = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -30,7 +31,8 @@ const LoginPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-8">
|
||||
<Video className="w-16 h-16 text-primary-600 mb-4" />
|
||||
@@ -95,6 +97,7 @@ const LoginPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Home, ArrowLeft } from 'lucide-react';
|
||||
import { publicAPI } from '../services/api';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
export default function NotFoundPage() {
|
||||
const location = useLocation();
|
||||
@@ -21,18 +22,8 @@ export default function NotFoundPage() {
|
||||
}, [location.pathname, location.search]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Simple header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<Link to="/" className="flex items-center gap-2 text-primary-600 hover:text-primary-700">
|
||||
<Home size={24} />
|
||||
<span className="text-xl font-bold">spotlight.cam</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="min-h-screen flex items-center justify-center px-4 -mt-16">
|
||||
<PublicLayout>
|
||||
<div className="bg-gray-50 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full text-center">
|
||||
{/* 404 Icon */}
|
||||
<div className="mb-8">
|
||||
@@ -80,6 +71,6 @@ export default function NotFoundPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import PasswordStrengthIndicator from '../components/common/PasswordStrengthIndi
|
||||
import FormInput from '../components/common/FormInput';
|
||||
import LoadingButton from '../components/common/LoadingButton';
|
||||
import Alert from '../components/common/Alert';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
const RegisterPage = () => {
|
||||
// Step management
|
||||
@@ -218,7 +219,8 @@ const RegisterPage = () => {
|
||||
// Step 1: WSDC ID Check
|
||||
if (step === 1) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-8">
|
||||
<Video className="w-16 h-16 text-primary-600 mb-4" />
|
||||
@@ -355,12 +357,14 @@ const RegisterPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Registration Form
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-8">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<Video className="w-12 h-12 text-primary-600 mb-3" />
|
||||
@@ -479,6 +483,7 @@ const RegisterPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams, Link } from 'react-router-dom';
|
||||
import { authAPI } from '../services/api';
|
||||
import { Video, Lock, CheckCircle, XCircle, Loader2 } from 'lucide-react';
|
||||
import PasswordStrengthIndicator from '../components/common/PasswordStrengthIndicator';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
const ResetPasswordPage = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -50,7 +51,8 @@ const ResetPasswordPage = () => {
|
||||
// Success state
|
||||
if (success) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
|
||||
@@ -71,13 +73,15 @@ const ResetPasswordPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Invalid token state
|
||||
if (!token) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4">
|
||||
@@ -98,12 +102,14 @@ const ResetPasswordPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Reset password form
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<Video className="w-16 h-16 text-primary-600 mb-4" />
|
||||
@@ -190,6 +196,7 @@ const ResetPasswordPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams, Link } from 'react-router-dom';
|
||||
import { authAPI } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { Video, Mail, CheckCircle, XCircle, Loader2, ArrowRight } from 'lucide-react';
|
||||
import PublicLayout from '../components/layout/PublicLayout';
|
||||
|
||||
const VerifyEmailPage = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -101,7 +102,8 @@ const VerifyEmailPage = () => {
|
||||
// Success state
|
||||
if (success) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
|
||||
@@ -123,13 +125,15 @@ const VerifyEmailPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Loading state (for token verification)
|
||||
if (loading && verificationMode === 'token') {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<Loader2 className="w-16 h-16 text-primary-600 mb-4 animate-spin" />
|
||||
@@ -142,12 +146,14 @@ const VerifyEmailPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Code verification form
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4">
|
||||
<PublicLayout>
|
||||
<div className="min-h-full bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center px-4 py-12">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-xl p-8">
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<Video className="w-16 h-16 text-primary-600 mb-4" />
|
||||
@@ -242,6 +248,7 @@ const VerifyEmailPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PublicLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user