11import { useState , useEffect } from "react" ;
22import { NavLink } from "react-router-dom" ;
3- import { Zap , ExternalLink , Sparkles , Wifi , Wrench , Eye } from "lucide-react" ;
3+ import {
4+ Zap ,
5+ ExternalLink ,
6+ Sparkles ,
7+ Wifi ,
8+ Wrench ,
9+ Eye ,
10+ SquarePlus ,
11+ } from "lucide-react" ;
412
513import earthIcon from "@/public/earth.svg" ;
614import cn from "@/utils/cn" ;
@@ -34,6 +42,12 @@ const features = [
3442 } ,
3543] ;
3644
45+ const formatNumber = ( num ) =>
46+ new Intl . NumberFormat ( "en-US" , {
47+ notation : "compact" ,
48+ maximumFractionDigits : 1 ,
49+ } ) . format ( num ) ;
50+
3751const HomePage = ( ) => (
3852 < >
3953 < HeroSection />
@@ -46,37 +60,30 @@ const HeroSection = () => {
4660 const [ stars , setStars ] = useState ( "30+" ) ;
4761
4862 useEffect ( ( ) => {
49- const getStarsCount = async ( ) => {
63+ ( async ( ) => {
5064 try {
51- const res = await fetch (
52- "https://api.github.com/repos/sabeerbikba/dev.tools"
65+ const resp = await fetch (
66+ "https://api.github.com/repos/sabeerbikba/dev.tools" ,
67+ {
68+ headers : { Accept : "application/vnd.github+json" } ,
69+ }
70+ ) ;
71+ if ( ! resp . ok ) throw new Error ( `GitHub API error: ${ resp . status } ` ) ;
72+ const data = await resp . json ( ) ;
73+ const rawCount = parseInt ( data ?. stargazers_count ) ;
74+ setStars (
75+ rawCount > 1000
76+ ? `${ formatNumber ( rawCount ) } +`
77+ : formatNumber ( rawCount )
5378 ) ;
54- if ( ! res . ok ) throw new Error ( `GitHub API error: ${ res . status } ` ) ;
55-
56- const data = await res . json ( ) ;
57- const rawCount = data ?. stargazers_count ;
58-
59- const formatNumber = ( num ) =>
60- new Intl . NumberFormat ( "en-US" , {
61- notation : "compact" ,
62- maximumFractionDigits : 1 ,
63- } ) . format ( num ) ;
64-
65- if ( rawCount > 1000 ) {
66- setStars ( formatNumber ( rawCount ) + "+" ) ;
67- } else {
68- setStars ( formatNumber ( rawCount ) ) ;
69- }
7079 } catch ( err ) {
7180 console . error ( "Failed to fetch stars:" , err ) ;
7281 }
73- } ;
74-
75- getStarsCount ( ) ;
82+ } ) ( ) ;
7683 } , [ ] ) ;
7784
7885 return (
79- < section className = "relative ! py-32 !px-4 text-center overflow-hidden" >
86+ < section className = "relative py-28 xl:py-32 max-sm:py-20 !px-4 text-center overflow-hidden" >
8087 < div className = "absolute inset-0 -z-10" >
8188 < div className = "absolute top-20 left-1/4 w-72 h-72 bg-primary/5 rounded-full blur-3xl" />
8289 < div className = "absolute top-40 right-1/4 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl" />
@@ -135,28 +142,28 @@ const HeroSection = () => {
135142} ;
136143
137144const Features = ( ) => (
138- < section className = "py-16 px-4 text-white" >
145+ < section className = "py-16 xl:py-14 max-sm:py-12 px-4 text-white" >
139146 < div className = "max-w-7xl mx-auto" >
140- < div className = "text-center mb-16" >
147+ < div className = "text-center mb-16 max-sm:mb-12 " >
141148 < div className = "text-white bg-[#4446a6]/15 inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary/10 text-primary text-sm mb-4" >
142149 < Zap className = "w-4 h-4" />
143150 Features
144151 </ div >
145- < h2 className = "text-4xl font-semibold mb-4" >
152+ < h2 className = "text-4xl max-sm:text-3xl font-semibold mb-4" >
146153 Everything you need for
147154 < span className = "block bg-gradient-to-r from-[#4446a6] to-white bg-clip-text text-transparent" >
148155 productive development
149156 </ span >
150157 </ h2 >
151158 </ div >
152159
153- < div className = "grid grid-cols-1 md:grid-cols-2 gap-8 " >
160+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6 " >
154161 { features . map ( ( feature , index ) => {
155162 const IconComponent = feature . icon ;
156163 return (
157164 < div
158165 key = { index }
159- className = "group relative p-6 rounded-xl border bg-card hover:shadow-lg transition-all duration-300 hover:-translate-y-1 border border-gray-500 hover:border-gray-600 bg-[#374151]/20 hover:bg-[#374151]/30"
166+ className = "group max-md:max-w-[510px] max-md:mx-auto relative p-6 rounded-xl border bg-card hover:shadow-lg transition-all duration-300 hover:-translate-y-1 border border-gray-500 hover:border-gray-600 bg-[#374151]/20 hover:bg-[#374151]/30"
160167 >
161168 < div className = "mb-4" >
162169 < div className = "w-12 h-12 rounded-lg bg-[#4446a6]/10 flex items-center justify-center group-hover:bg-[#4446a6]/20 transition-colors" >
@@ -181,7 +188,10 @@ const Features = () => (
181188) ;
182189
183190const Tools = ( ) => (
184- < section id = "tools" className = "!py-16 pt-[120px]! !px-4 relative" >
191+ < section
192+ id = "tools"
193+ className = "py-16 pt-[90px] xl:pt-[120px] max-sm:pt-[80px] px-4 relative"
194+ >
185195 < div className = "absolute inset-0 -z-10" >
186196 < div className = "absolute top-0 left-1/4 w-64 h-64 bg-purple-500/5 rounded-full blur-3xl" />
187197 < div className = "absolute bottom-0 right-1/4 w-80 h-80 bg-blue-500/5 rounded-full blur-3xl" />
@@ -197,7 +207,7 @@ const Tools = () => (
197207 < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 justify-items-center" >
198208 { routes
199209 . slice ( 1 , - 1 )
200- . map ( ( { name , description, path, category, icon, isPopular } , i ) => (
210+ . map ( ( { description, path, category, icon, isPopular, isNew } , i ) => (
201211 < ToolCard
202212 key = { i }
203213 name = { formatToolName ( path ) . titleCase }
@@ -206,6 +216,7 @@ const Tools = () => (
206216 category = { category }
207217 icon = { icon }
208218 isPopular = { isPopular }
219+ isNew = { isNew }
209220 />
210221 ) ) }
211222 </ div >
@@ -253,7 +264,15 @@ const CardContent = ({ className, ...props }) => (
253264 />
254265) ;
255266
256- const ToolCard = ( { name, description, path, category, icon, isPopular } ) => (
267+ const ToolCard = ( {
268+ name,
269+ description,
270+ path,
271+ category,
272+ icon,
273+ isPopular,
274+ isNew,
275+ } ) => (
257276 < Card className = "group max-w-[410px] hover:shadow-xl hover:shadow-primary/5 transition-all duration-300 hover:-translate-y-1 relative overflow-hidden" >
258277 { isPopular && (
259278 < div className = "absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-blue-500/5 pointer-events-none" />
@@ -269,12 +288,25 @@ const ToolCard = ({ name, description, path, category, icon, isPopular }) => (
269288 < CardTitle className = "group-hover:text-[#e6e6e6] transition-colors" >
270289 { name }
271290 </ CardTitle >
272- { isPopular && (
291+ { ( isPopular || isNew ) && (
273292 < div className = "flex items-center gap-1 !mt-1" >
274- < Sparkles className = "w-3 h-3 fill-yellow-500 text-yellow-500" />
275- < span className = "text-xs font-medium text-yellow-600" >
276- Popular
277- </ span >
293+ { isPopular && (
294+ < >
295+ < Sparkles className = "w-3 h-3 fill-yellow-500 text-yellow-500" />
296+ < span className = "text-xs font-medium text-yellow-600" >
297+ Popular
298+ </ span >
299+ </ >
300+ ) }
301+ { isNew && (
302+ < >
303+ { " " }
304+ < SquarePlus className = "w-3 h-3 text-red-400" />
305+ < span className = "text-xs font-medium text-red-500" >
306+ New
307+ </ span >
308+ </ >
309+ ) }
278310 </ div >
279311 ) }
280312 </ div >
@@ -286,9 +318,7 @@ const ToolCard = ({ name, description, path, category, icon, isPopular }) => (
286318 </ CardHeader >
287319
288320 < CardContent >
289- < p className = "!mb-4 leading-relaxed text-sm" >
290- { description }
291- </ p >
321+ < p className = "!mb-4 leading-relaxed text-sm" > { description } </ p >
292322 < NavLink
293323 className = "inline-flex-center group-hover:bg-[#4446a6] whitespace-nowrap text-sm font-medium border h-8 rounded-md !px-3 w-full gap-2 group-hover:bg-primary group-hover:text-[#4446a6]-foreground border-gray-200 group-hover:border-gray-300 transition-all duration-200"
294324 to = { path }
0 commit comments