diff --git a/public/assets/images/bg_cta.png b/public/assets/images/bg_cta.png new file mode 100644 index 0000000..cb98398 Binary files /dev/null and b/public/assets/images/bg_cta.png differ diff --git a/public/assets/images/bg_features.png b/public/assets/images/bg_features.png new file mode 100644 index 0000000..0a9390d Binary files /dev/null and b/public/assets/images/bg_features.png differ diff --git a/public/assets/images/bg_hero.png b/public/assets/images/bg_hero.png new file mode 100644 index 0000000..f15b6b3 Binary files /dev/null and b/public/assets/images/bg_hero.png differ diff --git a/src/App.tsx b/src/App.tsx index b8cad4b..b9b2cf7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,9 +6,9 @@ import { ProtectedRoute } from './components/ProtectedRoute'; // Pages import Editor from './pages/Editor'; import Home from './pages/Home'; -import Auth from './pages/Auth'; import Dashboard from './pages/Dashboard'; import Admin from './pages/Admin'; +import { AuthModal } from './components/AuthModal'; export default function App() { const { initialize, isLoading } = useAuthStore(); @@ -27,10 +27,10 @@ export default function App() { return ( + {/* Public Routes */} } /> - } /> {/* Protected Routes (User) */} }> diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx new file mode 100644 index 0000000..94b9e68 --- /dev/null +++ b/src/components/AuthModal.tsx @@ -0,0 +1,152 @@ +import React, { useState } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { motion, AnimatePresence } from 'framer-motion'; +import { api } from '../lib/api'; +import { useAuthStore } from '../store/authStore'; +import { Box, Mail, Lock, Loader2, X } from 'lucide-react'; + +export function AuthModal() { + const { isAuthModalOpen, closeAuthModal, authMode, openAuthModal, setSession } = useAuthStore(); + const navigate = useNavigate(); + const location = useLocation(); + + const isRegister = authMode === 'register'; + const from = location.state?.from?.pathname || '/dashboard'; + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(''); + setSuccess(''); + + try { + if (isRegister) { + const res = await api.register(email, password); + if (res.status === 'pending') { + setSuccess(res.message || 'Account created successfully! Please contact an admin to activate your account.'); + setEmail(''); + setPassword(''); + } else { + setSession(res.token, res.profile); + closeAuthModal(); + navigate(from, { replace: true }); + } + } else { + const { token, profile } = await api.login(email, password); + setSession(token, profile); + closeAuthModal(); + navigate(from, { replace: true }); + } + } catch (err: any) { + setError(err.message || 'An error occurred during authentication.'); + } finally { + setLoading(false); + } + }; + + return ( + + {isAuthModalOpen && ( +
+ + + {/* Glowing orb background effect */} +
+ + + +
+
+ +
+
+ +

+ {isRegister ? 'Create an Account' : 'Welcome Back'} +

+

+ {isRegister ? 'Sign up to start building 3D scenes.' : 'Sign in to access your projects.'} +

+ + {error && ( +
+ {error} +
+ )} + + {success && ( +
+ {success} +
+ )} + +
+
+ + setEmail(e.target.value)} + className="w-full bg-zinc-950/50 border border-zinc-700/50 rounded-xl py-3 pl-10 pr-4 text-white placeholder-zinc-500 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all" + /> +
+ +
+ + setPassword(e.target.value)} + className="w-full bg-zinc-950/50 border border-zinc-700/50 rounded-xl py-3 pl-10 pr-4 text-white placeholder-zinc-500 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all" + /> +
+ + +
+ +
+ {isRegister ? ( +

Already have an account?

+ ) : ( +

Don't have an account?

+ )} +
+ +
+ )} + + ); +} diff --git a/src/components/home/Overlay.tsx b/src/components/home/Overlay.tsx new file mode 100644 index 0000000..3cf00c0 --- /dev/null +++ b/src/components/home/Overlay.tsx @@ -0,0 +1,102 @@ +import { Scroll, Image } from '@react-three/drei'; +import { useAuthStore } from '../../store/authStore'; +import { Box, Layers, Zap, Globe, Sparkles } from 'lucide-react'; + +export function Overlay() { + const { openAuthModal } = useAuthStore(); + + return ( + <> + {/* 3D Background Images Parallax Layer */} + + + + + + + {/* HTML UI Layer */} + + {/* Navigation */} + + + {/* Section 1: Hero */} +
+
+ + Next-Gen 3D Configurator +
+

+ Build The Future in 3D. +

+

+ An ultra-premium, AI-driven platform to visualize, construct, and deploy stunning 3D assets directly in your browser. +

+ +
+ + {/* Section 2: Features */} +
+

+ Unleash Unprecedented Power. +

+ +
+
+
+

Real-time Rendering

+

Experience photorealistic materials and lighting instantly.

+
+
+ +
+
+ +
+
+

AI Asset Generation

+

Turn text prompts into high-fidelity 3D models in seconds.

+
+
+ +
+
+
+
+ + {/* Section 3: Call to Action */} +
+
+ + +

+ Ready to Deploy? +

+

+ Join elite creators building the next generation of spatial computing experiences. +

+ +
+
+ + ); +} diff --git a/src/components/home/Scene.tsx b/src/components/home/Scene.tsx new file mode 100644 index 0000000..e387069 --- /dev/null +++ b/src/components/home/Scene.tsx @@ -0,0 +1,46 @@ +import { useRef } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { Environment, Float, Sparkles } from '@react-three/drei'; +import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing'; +import { Models } from './Models'; + +export function Scene() { + const group = useRef(null); + + useFrame((state) => { + if (group.current) { + // Gentle floating animation for the entire scene group + group.current.rotation.y = Math.sin(state.clock.elapsedTime * 0.1) * 0.2; + } + }); + + return ( + <> + + + {/* Lighting */} + + + + + + + + {/* Main Content */} + + + + + + {/* Floating Particles */} + + + + {/* Post Processing for Premium Aesthetic */} + + + + + + ); +} diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx deleted file mode 100644 index 2f747f9..0000000 --- a/src/pages/Auth.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useState } from 'react'; -import { useSearchParams, useNavigate, useLocation, Link } from 'react-router-dom'; -import { api } from '../lib/api'; -import { useAuthStore } from '../store/authStore'; -import { Box, Mail, Lock, Loader2 } from 'lucide-react'; - -export default function Auth() { - const [searchParams] = useSearchParams(); - const navigate = useNavigate(); - const location = useLocation(); - const { setSession } = useAuthStore(); - - const isRegister = searchParams.get('mode') === 'register'; - const from = location.state?.from?.pathname || '/dashboard'; - - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(''); - const [success, setSuccess] = useState(''); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setLoading(true); - setError(''); - setSuccess(''); - - try { - if (isRegister) { - const res = await api.register(email, password); - if (res.status === 'pending') { - setSuccess(res.message || 'Account created successfully! Please contact an admin to activate your account.'); - setEmail(''); - setPassword(''); - } else { - setSession(res.token, res.profile); - navigate(from, { replace: true }); - } - } else { - const { token, profile } = await api.login(email, password); - setSession(token, profile); - navigate(from, { replace: true }); - } - } catch (err: any) { - setError(err.message || 'An error occurred during authentication.'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-
-
- -
-
- -

- {isRegister ? 'Create an Account' : 'Welcome Back'} -

-

- {isRegister ? 'Sign up to start building 3D scenes.' : 'Sign in to access your projects.'} -

- - {error && ( -
- {error} -
- )} - - {success && ( -
- {success} -
- )} - -
-
- - setEmail(e.target.value)} - className="w-full bg-zinc-950 border border-zinc-800 rounded-xl py-3 pl-10 pr-4 text-white focus:outline-none focus:border-primary transition-colors" - /> -
- -
- - setPassword(e.target.value)} - className="w-full bg-zinc-950 border border-zinc-800 rounded-xl py-3 pl-10 pr-4 text-white focus:outline-none focus:border-primary transition-colors" - /> -
- - -
- -
- {isRegister ? ( -

Already have an account? Sign in

- ) : ( -

Don't have an account? Sign up

- )} -
-
-
- ); -} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index b3e4dca..92d9c13 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,45 +1,20 @@ -import { Link } from 'react-router-dom'; -import { useAuthStore } from '../store/authStore'; -import { Box } from 'lucide-react'; +import { Canvas } from '@react-three/fiber'; +import { ScrollControls } from '@react-three/drei'; +import { Scene } from '../components/home/Scene'; +import { Overlay } from '../components/home/Overlay'; +import { Suspense } from 'react'; export default function Home() { - const { user } = useAuthStore(); - return ( -
-
- -
-

- Welcome to Titan3d -

-

- The ultimate cloud-based 3D product configurator. Build, edit, and visualize 3D assets entirely in your browser with the power of AI. -

- - {user ? ( - - Go to Dashboard - - ) : ( -
- - Sign In - - - Create Account - -
- )} +
+ + + + + + + +
); } diff --git a/src/store/authStore.ts b/src/store/authStore.ts index 8398be3..1613050 100644 --- a/src/store/authStore.ts +++ b/src/store/authStore.ts @@ -17,12 +17,21 @@ interface AuthState { setSession: (token: string, profile: UserProfile) => void; signOut: () => void; setProfile: (profile: UserProfile) => void; + isAuthModalOpen: boolean; + openAuthModal: (mode?: 'login' | 'register') => void; + closeAuthModal: () => void; + authMode: 'login' | 'register'; } export const useAuthStore = create((set) => ({ user: null, profile: null, isLoading: true, + isAuthModalOpen: false, + authMode: 'login', + + openAuthModal: (mode = 'login') => set({ isAuthModalOpen: true, authMode: mode }), + closeAuthModal: () => set({ isAuthModalOpen: false }), initialize: async () => { try {