22
33import { useState , useEffect } from "react" ;
44import { useTranslation , Trans } from "react-i18next" ;
5- import {
6- Bot ,
7- Globe ,
8- Zap ,
9- MessagesSquare ,
10- Unplug ,
11- TextQuote ,
12- AlertTriangle ,
13- } from "lucide-react" ;
145import { Button } from "@/components/ui/button" ;
15- import { Card , CardContent } from "@/components/ui/card" ;
16- import { Navbar } from "@/components/ui/navbar" ;
17- import Link from "next/link" ;
6+ import { NavigationLayout } from "@/components/navigation/NavigationLayout" ;
7+ import { HomepageContent } from "@/components/homepage/HomepageContent" ;
188import { LoginModal } from "@/components/auth/loginModal" ;
199import { RegisterModal } from "@/components/auth/registerModal" ;
2010import { useAuth } from "@/hooks/useAuth" ;
2111import { Modal , ConfigProvider } from "antd" ;
22- import { motion } from "framer-motion" ;
23- import { APP_VERSION } from "@/const/constants" ;
24- import { FOOTER_CONFIG } from "@/const/layoutConstants" ;
25- import { versionService } from "@/services/versionService" ;
26- import log from "@/lib/logger" ;
2712
2813export default function Home ( ) {
2914 const [ mounted , setMounted ] = useState ( false ) ;
@@ -55,27 +40,10 @@ export default function Home() {
5540 const [ loginPromptOpen , setLoginPromptOpen ] = useState ( false ) ;
5641 const [ adminRequiredPromptOpen , setAdminRequiredPromptOpen ] =
5742 useState ( false ) ;
58- const [ appVersion , setAppVersion ] = useState < string > ( "" ) ;
59-
60- // Get app version on mount
61- useEffect ( ( ) => {
62- const fetchAppVersion = async ( ) => {
63- try {
64- const version = await versionService . getAppVersion ( ) ;
65- setAppVersion ( version ) ;
66- } catch ( error ) {
67- log . error ( "Failed to fetch app version:" , error ) ;
68- setAppVersion ( APP_VERSION ) ; // Fallback
69- }
70- } ;
71-
72- fetchAppVersion ( ) ;
73- } , [ ] ) ;
7443
7544 // Handle operations that require login
76- const handleAuthRequired = ( e : React . MouseEvent ) => {
45+ const handleAuthRequired = ( ) => {
7746 if ( ! isSpeedMode && ! user ) {
78- e . preventDefault ( ) ;
7947 setLoginPromptOpen ( true ) ;
8048 }
8149 } ;
@@ -98,9 +66,8 @@ export default function Home() {
9866 } ;
9967
10068 // Handle operations that require admin privileges
101- const handleAdminRequired = ( e : React . MouseEvent ) => {
69+ const handleAdminRequired = ( ) => {
10270 if ( ! isSpeedMode && user ?. role !== "admin" ) {
103- e . preventDefault ( ) ;
10471 setAdminRequiredPromptOpen ( true ) ;
10572 }
10673 } ;
@@ -111,183 +78,16 @@ export default function Home() {
11178 } ;
11279
11380 return (
114- < div className = "min-h-screen flex flex-col bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800" >
115- { /* Top navigation bar */ }
116- < Navbar />
117-
118- { /* Main content */ }
119- < main className = "flex-1 pt-8 pb-8 flex flex-col justify-center my-8" >
120- { /* Hero area */ }
121- < section className = "relative w-full py-10 flex flex-col items-center justify-center text-center px-4" >
122- < div className = "absolute inset-0 bg-grid-slate-200 dark:bg-grid-slate-800 [mask-image:radial-gradient(ellipse_at_center,white_20%,transparent_75%)] -z-10" > </ div >
123- < motion . h2
124- initial = { { opacity : 0 , y : - 20 } }
125- animate = { { opacity : 1 , y : 0 } }
126- transition = { { duration : 0.8 , delay : 0.2 } }
127- className = "text-4xl md:text-5xl lg:text-6xl font-bold text-slate-900 dark:text-white mb-4 tracking-tight"
128- >
129- { t ( "page.title" ) }
130- < span className = "text-blue-600 dark:text-blue-500" >
131- { " " }
132- { t ( "page.subtitle" ) }
133- </ span >
134- </ motion . h2 >
135- < motion . p
136- initial = { { opacity : 0 , y : - 20 } }
137- animate = { { opacity : 1 , y : 0 } }
138- transition = { { duration : 0.8 , delay : 0.3 } }
139- className = "max-w-2xl text-slate-600 dark:text-slate-300 text-lg md:text-xl mb-8"
140- >
141- { t ( "page.description" ) }
142- </ motion . p >
143-
144- { /* Three parallel buttons */ }
145- < motion . div
146- initial = { { opacity : 0 , y : 20 } }
147- animate = { { opacity : 1 , y : 0 } }
148- transition = { { duration : 0.8 , delay : 0.4 } }
149- className = "flex flex-col sm:flex-row gap-4"
150- >
151- < Link href = { isSpeedMode || user ? "/chat" : "#" } onClick = { handleAuthRequired } >
152- < Button className = "bg-blue-600 hover:bg-blue-700 text-white px-8 py-6 rounded-full text-lg font-medium shadow-lg hover:shadow-xl transition-all duration-300 group" >
153- < Bot className = "mr-2 h-5 w-5 group-hover:animate-pulse" />
154- { t ( "page.startChat" ) }
155- </ Button >
156- </ Link >
157-
158- < Link
159- href = { isSpeedMode || user ?. role === "admin" ? "/setup" : "#" }
160- onClick = { handleAdminRequired }
161- >
162- < Button className = "bg-blue-600 hover:bg-blue-700 text-white px-8 py-6 rounded-full text-lg font-medium shadow-lg hover:shadow-xl transition-all duration-300 group" >
163- < Zap className = "mr-2 h-5 w-5 group-hover:animate-pulse" />
164- { t ( "page.quickConfig" ) }
165- </ Button >
166- </ Link >
167-
168- < Link href = { isSpeedMode || user ? "/space" : "#" } onClick = { handleAuthRequired } >
169- < Button className = "bg-blue-600 hover:bg-blue-700 text-white px-8 py-6 rounded-full text-lg font-medium shadow-lg hover:shadow-xl transition-all duration-300 group" >
170- < Globe className = "mr-2 h-5 w-5 group-hover:animate-pulse" />
171- { t ( "page.agentSpace" ) }
172- </ Button >
173- </ Link >
174- </ motion . div >
175-
176- { /* Data protection notice - only shown in full version */ }
177- { ! isSpeedMode && (
178- < motion . div
179- initial = { { opacity : 0 , y : 20 } }
180- animate = { { opacity : 1 , y : 0 } }
181- transition = { { duration : 0.8 , delay : 0.5 } }
182- className = "mt-12 flex items-center justify-center gap-2 text-sm text-slate-500 dark:text-slate-400"
183- >
184- < AlertTriangle className = "h-4 w-4" />
185- < span > { t ( "page.dataProtection" ) } </ span >
186- </ motion . div >
187- ) }
188- </ section >
189-
190- { /* Feature cards */ }
191- < motion . section
192- initial = { { opacity : 0 , y : 30 } }
193- animate = { { opacity : 1 , y : 0 } }
194- transition = { { duration : 0.8 , delay : 0.6 } }
195- className = "max-w-7xl mx-auto px-4 mb-6"
196- >
197- < motion . h3
198- initial = { { opacity : 0 , y : - 20 } }
199- animate = { { opacity : 1 , y : 0 } }
200- transition = { { duration : 0.8 , delay : 0.7 } }
201- className = "text-2xl font-bold text-slate-900 dark:text-white mb-8 text-center"
202- >
203- { t ( "page.coreFeatures" ) }
204- </ motion . h3 >
205- < motion . div
206- initial = { { opacity : 0 } }
207- animate = { { opacity : 1 } }
208- transition = { { duration : 0.8 , delay : 0.8 } }
209- className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-stretch"
210- >
211- { (
212- t ( "page.features" , { returnObjects : true } ) as Array < {
213- title : string ;
214- description : string ;
215- } >
216- ) . map ( ( feature , index : number ) => {
217- const icons = [
218- < Bot key = { 0 } className = "h-8 w-8 text-blue-500" /> ,
219- < TextQuote key = { 1 } className = "h-8 w-8 text-green-500" /> ,
220- < Zap key = { 2 } className = "h-8 w-8 text-blue-500" /> ,
221- < Globe key = { 3 } className = "h-8 w-8 text-emerald-500" /> ,
222- < Unplug key = { 4 } className = "h-8 w-8 text-amber-500" /> ,
223- < MessagesSquare
224- key = { 5 }
225- className = "h-8 w-8 text-purple-500"
226- /> ,
227- ] ;
228-
229- return (
230- < motion . div
231- key = { index }
232- initial = { { opacity : 0 , y : 20 } }
233- animate = { { opacity : 1 , y : 0 } }
234- transition = { {
235- duration : 0.6 ,
236- delay : 0.9 + index * 0.1 ,
237- } }
238- >
239- < FeatureCard
240- icon = {
241- icons [ index ] || (
242- < Bot className = "h-8 w-8 text-blue-500" />
243- )
244- }
245- title = { feature . title }
246- description = { feature . description }
247- />
248- </ motion . div >
249- ) ;
250- } ) }
251- </ motion . div >
252- </ motion . section >
253- </ main >
254-
255- { /* Footer */ }
256- < footer
257- className = "w-full py-4 px-4 flex items-center justify-center border-t border-slate-200 dark:border-slate-700 bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm"
258- style = { { height : FOOTER_CONFIG . HEIGHT } }
259- >
260- < div className = "max-w-7xl mx-auto w-full" >
261- < div className = "flex flex-col md:flex-row justify-between items-center h-full" >
262- < div className = "flex items-center gap-8" >
263- < span className = "text-sm text-slate-900 dark:text-white" >
264- { t ( "page.copyright" , { year : new Date ( ) . getFullYear ( ) } ) }
265- < span className = "ml-1" > · { appVersion || APP_VERSION } </ span >
266- </ span >
267- </ div >
268- < div className = "flex items-center gap-6" >
269- < Link
270- href = "https://github.com/nexent-hub/nexent?tab=License-1-ov-file#readme"
271- className = "text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
272- >
273- { t ( "page.termsOfUse" ) }
274- </ Link >
275- < Link
276- href = "http://nexent.tech/contact"
277- className = "text-sm text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white transition-colors"
278- >
279- { t ( "page.contactUs" ) }
280- </ Link >
281- < Link
282- href = "http://nexent.tech/about"
283- className = "text-sm text-slate-600 dark:text-slate-300 dark:hover:text-white transition-colors"
284- >
285- { t ( "page.aboutUs" ) }
286- </ Link >
287- </ div >
288- </ div >
289- </ div >
290- </ footer >
81+ < NavigationLayout
82+ onAuthRequired = { handleAuthRequired }
83+ onAdminRequired = { handleAdminRequired }
84+ showFooter = { true }
85+ contentMode = "centered"
86+ >
87+ < HomepageContent
88+ onAuthRequired = { handleAuthRequired }
89+ onAdminRequired = { handleAdminRequired }
90+ />
29191
29292 { /* Login prompt dialog - only shown in full version */ }
29393 { ! isSpeedMode && (
@@ -459,32 +259,7 @@ export default function Home() {
459259 </ div >
460260 </ Modal >
461261 ) }
462- </ div >
262+ </ NavigationLayout >
463263 ) ;
464264 }
465265}
466-
467- // Feature card component
468- interface FeatureCardProps {
469- icon : React . ReactNode ;
470- title : string ;
471- description : string ;
472- }
473-
474- function FeatureCard ( { icon, title, description } : FeatureCardProps ) {
475- return (
476- < Card className = "overflow-hidden border border-slate-200 dark:border-slate-700 transition-all duration-300 hover:shadow-md hover:border-blue-200 dark:hover:border-blue-900 group h-full" >
477- < CardContent className = "p-6 h-full flex flex-col" >
478- < div className = "mb-4 p-3 bg-slate-100 dark:bg-slate-800 rounded-full w-fit group-hover:bg-blue-100 dark:group-hover:bg-blue-900/30 transition-colors" >
479- { icon }
480- </ div >
481- < h4 className = "text-lg font-semibold text-slate-900 dark:text-white mb-2" >
482- { title }
483- </ h4 >
484- < p className = "text-slate-600 dark:text-slate-300 flex-grow" >
485- { description }
486- </ p >
487- </ CardContent >
488- </ Card >
489- ) ;
490- }
0 commit comments