1- 'use client'
2- import { useState , useEffect , useRef } from 'react'
3- import { useRouter } from 'next/navigation'
4- import toast , { Toaster } from 'react-hot-toast'
5- import Typewriter from 'typewriter-effect'
6- import Loader from '@/components/Loader'
7- import Footer from '@/components/Footer'
8-
9- function Home ( ) {
10- const [ username , setUserName ] = useState ( '' )
11- const [ loading , setLoading ] = useState ( false )
12- const router = useRouter ( )
13- const inputRef = useRef ( null )
14-
15- useEffect ( ( ) => {
16- inputRef . current . focus ( )
17- } )
1+ "use client" ;
182
3+ import { useState } from "react" ;
4+ import Link from "next/link" ;
5+ import { useRouter } from "next/navigation" ;
6+ import toast , { Toaster } from "react-hot-toast" ;
7+ import Typewriter from "typewriter-effect" ;
8+ import Loader from "@/components/Loader" ;
9+ import Footer from "@/components/Footer" ;
10+
11+ export default function Home ( ) {
12+ const [ open , setOpen ] = useState ( false ) ;
13+ const [ username , setUserName ] = useState ( "" ) ;
14+ const [ loading , setLoading ] = useState ( false ) ;
15+ const router = useRouter ( ) ;
16+
17+ /* ---------------- HANDLERS ---------------- */
1918 const handleSubmit = async ( e ) => {
20- e . preventDefault ( )
19+ e . preventDefault ( ) ;
2120
22- const val = username . trim ( )
21+ const val = username . trim ( ) ;
2322 if ( ! val ) {
24- toast . error ( ' Please enter a valid username.' )
25- return
23+ toast . error ( " Please enter a valid username." ) ;
24+ return ;
2625 }
2726
28- if ( username . trim ( ) ) {
29- try {
30- setLoading ( true )
31- const response = await fetch ( '/api/user' , {
32- method : 'POST' ,
33- headers : {
34- 'Content-Type' : 'application/json' ,
35- } ,
36- body : JSON . stringify ( { username : username . trim ( ) } ) ,
37- } )
38-
39- if ( response . ok ) {
40- const data = await response . json ( )
41- if ( data . exists ) {
42- // Show loader while navigating to profile page
43- router . push ( `/${ username . trim ( ) } ` )
44- } else {
45- toast . error ( 'Username does not exist on GitHub' )
46- }
27+ try {
28+ setLoading ( true ) ;
29+ const response = await fetch ( "/api/user" , {
30+ method : "POST" ,
31+ headers : { "Content-Type" : "application/json" } ,
32+ body : JSON . stringify ( { username : val } ) ,
33+ } ) ;
34+
35+ if ( response . ok ) {
36+ const data = await response . json ( ) ;
37+ if ( data . exists ) {
38+ router . push ( `/${ val } ` ) ;
4739 } else {
48- toast . error ( 'Error verifying username. Please try again.' )
40+ toast . error ( "Username does not exist on GitHub" ) ;
4941 }
50- } catch ( error ) {
51- toast . error ( 'An unexpected error occurred. Please try again.' )
52- } finally {
53- // Keep loading state true briefly to allow route transition loading UI to appear
54- setTimeout ( ( ) => setLoading ( false ) , 800 )
42+ } else {
43+ toast . error ( "Error verifying username." ) ;
5544 }
56- } else {
57- toast . error ( 'Please enter a username.' )
45+ } catch {
46+ toast . error ( "Unexpected error occurred." ) ;
47+ } finally {
48+ setTimeout ( ( ) => setLoading ( false ) , 500 ) ;
5849 }
59- }
50+ } ;
6051
61- const handleUser = async ( e ) => {
62- const val = e . target . value
63- setUserName ( val )
64- }
52+ const handleUser = ( e ) => setUserName ( e . target . value ) ;
6553
54+ /* ---------------- JSX ---------------- */
6655 return (
6756 < >
68- < Toaster />
69- < div className = 'flex flex-col justify-between min-h-[92vh] md:min-h-[94vh] lg:min-h-[90vh] xl:min-h-[91vh]' >
70- { /* Main Content */ }
71- < main className = 'flex flex-col justify-center items-center flex-grow px-8 sm:px-4' >
72- < >
73- < h1 className = 'text-4xl font-light text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600 tracking-widest mb-8' >
57+ { /* ================= NAVBAR ================= */ }
58+ < nav className = "fixed top-0 z-50 w-full bg-black text-white shadow-md" >
59+ < div className = "mx-auto flex max-w-7xl items-center justify-between px-4 py-3" >
60+ < Link href = "/" className = "text-xl font-bold" >
61+ Gityzer
62+ </ Link >
63+
64+ { /* Desktop Menu */ }
65+ < div className = "hidden md:flex gap-6" >
66+ < Link href = "#features" > Features</ Link >
67+ < Link href = "#demo" > Demo</ Link >
68+ < Link href = "#docs" > Docs</ Link >
69+ </ div >
70+
71+ { /* Mobile Hamburger */ }
72+ < button
73+ className = "md:hidden text-2xl"
74+ onClick = { ( ) => setOpen ( ( prev ) => ! prev ) }
75+ aria-label = "Toggle menu"
76+ >
77+ ☰
78+ </ button >
79+ </ div >
80+
81+ { /* Mobile Menu */ }
82+ { open && (
83+ < div className = "md:hidden px-4 pb-4 space-y-2 bg-black" >
84+ < Link onClick = { ( ) => setOpen ( false ) } href = "#features" className = "block" >
85+ Features
86+ </ Link >
87+ < Link onClick = { ( ) => setOpen ( false ) } href = "#demo" className = "block" >
88+ Demo
89+ </ Link >
90+ < Link onClick = { ( ) => setOpen ( false ) } href = "#docs" className = "block" >
91+ Docs
92+ </ Link >
93+ </ div >
94+ ) }
95+ </ nav >
96+
97+ { /* ================= PAGE CONTENT ================= */ }
98+ < main className = "pt-16" >
99+ < Toaster />
100+
101+ < div className = "flex flex-col justify-between min-h-[92vh] md:min-h-[94vh] lg:min-h-[90vh]" >
102+ < main className = "flex flex-col justify-center items-center flex-grow px-8 sm:px-4" >
103+ < h1 className = "text-4xl font-light text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600 tracking-widest mb-8" >
74104 < Typewriter
75105 options = { {
76- cursor : "<span style='color: #fff; animation: blink 1s infinite;'>|</span>" ,
106+ cursor :
107+ "<span style='color:#fff; animation: blink 1s infinite;'>|</span>" ,
77108 } }
78109 onInit = { ( typewriter ) => {
79110 const animate = ( ) => {
@@ -82,21 +113,22 @@ function Home() {
82113 . pauseFor ( 1000 )
83114 . deleteAll ( )
84115 . pauseFor ( 1000 )
85- . start ( )
86- setTimeout ( animate , 2000 ) // Adjust delay
87- }
88- animate ( )
116+ . start ( ) ;
117+ setTimeout ( animate , 2000 ) ;
118+ } ;
119+ animate ( ) ;
89120 } }
90121 />
91122 </ h1 >
92- < form
93- className = 'flex items-center w-full sm:w-2/3 lg:w-1/3 h-1/3'
94- onSubmit = { handleSubmit }
95- >
96- < div className = 'flex items-center bg-white rounded-full shadow-md p-2 w-full group' >
123+
124+ { /* ================= SEARCH FORM ================= */ }
125+ < form
126+ onSubmit = { handleSubmit }
127+ >
128+ < div className = 'flex items-center bg-white rounded-full shadow-md p-2 w-full group' >
97129 { /* GitHub Logo inside search bar */ }
98130 < div className = 'px-3 group-hover:animate-spin motion-reduce:group-hover:animate-none' >
99- < svg
131+ < svg
100132 xmlns = 'http://www.w3.org/2000/svg'
101133 className = 'h-7 w-7 text-gray-600'
102134 viewBox = '0 0 24 24'
@@ -107,51 +139,30 @@ function Home() {
107139 </ div >
108140
109141 { /* Search input */ }
142+
110143 < input
111- type = 'text'
112- placeholder = 'Enter GitHub username'
113- className = 'w-full px-4 py-2 text-gray-700 bg-white focus:outline-none text-base md:text-lg'
144+ type = "text"
145+ placeholder = "Search GitHub username..."
114146 value = { username }
115147 onChange = { handleUser }
116- ref = { inputRef }
117- disabled = { loading }
148+ className = "flex-1 outline-none px-2"
118149 />
119150
120- { /* Submit button */ }
121151 < button
122- type = ' submit'
152+ type = " submit"
123153 disabled = { ! username . trim ( ) || loading }
124- aria-busy = { loading }
125- className = 'relative bg-purple-500 text-white p-3 rounded-full hover:bg-purple-600 focus:outline-none focus:ring-4 focus:ring-purple-500 focus:ring-opacity-50 transition duration-200 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed'
154+ className = "ml-2 bg-purple-500 text-white p-3 rounded-full hover:bg-purple-600 disabled:opacity-50"
126155 >
127- { loading ? (
128- < span className = 'h-5 w-5 rounded-full border-2 border-white border-t-transparent animate-spin' />
129- ) : (
130- < svg
131- xmlns = 'http://www.w3.org/2000/svg'
132- className = 'h-5 w-5'
133- fill = 'none'
134- viewBox = '0 0 24 24'
135- stroke = 'currentColor'
136- strokeWidth = '2'
137- >
138- < path strokeLinecap = 'round' strokeLinejoin = 'round' d = 'M9 5l7 7-7 7' />
139- </ svg >
140- ) }
156+ →
141157 </ button >
142158 </ div >
143159 </ form >
144- </ >
145- </ main >
160+ </ main >
146161
147- { loading && (
148- < Loader message = 'Verifying username...' />
149- ) }
150-
151- < Footer />
152- </ div >
162+ { loading && < Loader message = "Verifying username..." /> }
163+ < Footer />
164+ </ div >
165+ </ main >
153166 </ >
154- )
167+ ) ;
155168}
156-
157- export default Home
0 commit comments