@@ -4,11 +4,79 @@ import { Logo } from '@/components/shared/ui/Logo';
44import { theme , fonts } from '@/components/user-dashboard/connect/ui/theme' ;
55import { useCallback } from 'react' ;
66
7+ interface FeaturedAppCardProps {
8+ app : App ;
9+ handleAppClick : ( appId : number ) => void ;
10+ }
11+
712interface FeaturedAppsProps {
813 apps : App [ ] ;
914 onNavigate ?: ( path : string ) => void ;
1015}
1116
17+ function FeaturedAppCard ( { app, handleAppClick } : FeaturedAppCardProps ) {
18+ return (
19+ < div
20+ onClick = { ( ) => handleAppClick ( app . appId ) }
21+ className = "group relative overflow-hidden rounded-xl md:rounded-2xl border border-gray-200 dark:border-white/10 hover:border-gray-300 dark:hover:border-white/20 transition-all duration-300 cursor-pointer h-[300px] md:h-[400px]"
22+ >
23+ { /* Image at the top with fade effect */ }
24+ < div className = "absolute inset-0 z-0" >
25+ { /* Gradient overlay that fades from solid color to transparent */ }
26+ < div
27+ className = "absolute inset-0 z-10"
28+ style = { {
29+ background : `linear-gradient(to top, ${ 'rgba(255, 66, 5, 0.85)' } 0%, ${ 'rgba(255, 66, 5, 0.7)' } 15%, ${ 'rgba(255, 66, 5, 0.4)' } 30%, transparent 50%, transparent 100%)` ,
30+ } }
31+ />
32+
33+ { /* App logo/image */ }
34+ < div className = "absolute inset-0 flex items-start justify-center pt-12 opacity-60 group-hover:opacity-80 transition-opacity duration-300" >
35+ < div className = "w-48 h-48 rounded-2xl overflow-hidden" >
36+ < Logo logo = { app . logo } alt = { `${ app . name } logo` } className = "w-full h-full object-cover" />
37+ </ div >
38+ </ div >
39+ </ div >
40+
41+ { /* Content at the bottom */ }
42+ < div className = "absolute bottom-0 left-0 right-0 p-8 z-20" >
43+ < div className = "flex items-start justify-between mb-3" >
44+ < div className = "flex-1" >
45+ < h3 className = { `text-2xl font-semibold ${ theme . text } mb-2` } style = { fonts . heading } >
46+ { app . name }
47+ </ h3 >
48+ < p className = { `${ theme . text } text-sm line-clamp-2 opacity-80` } style = { fonts . body } >
49+ { app . description || 'No description available' }
50+ </ p >
51+ </ div >
52+ </ div >
53+
54+ < div className = "flex items-center gap-3" >
55+ < span
56+ className = { `px-3 py-1 rounded-full text-xs font-medium ${ theme . bg } ${ theme . text } backdrop-blur-sm ${ theme . cardBorder } border` }
57+ style = { fonts . heading }
58+ >
59+ v{ app . activeVersion }
60+ </ span >
61+ < span
62+ className = { `px-3 py-1 rounded-full text-xs font-medium ${ theme . bg } ${ theme . text } backdrop-blur-sm ${ theme . cardBorder } border` }
63+ style = { fonts . heading }
64+ >
65+ { app . deploymentStatus === 'prod'
66+ ? 'LIVE'
67+ : app . deploymentStatus === 'test'
68+ ? 'BETA'
69+ : app . deploymentStatus ?. toUpperCase ( ) }
70+ </ span >
71+ </ div >
72+ </ div >
73+
74+ { /* Hover effect overlay */ }
75+ < div className = "absolute inset-0 bg-black/0 group-hover:bg-black/5 transition-colors duration-300 z-30" />
76+ </ div >
77+ ) ;
78+ }
79+
1280export function FeaturedApps ( { apps, onNavigate } : FeaturedAppsProps ) {
1381 const navigate = useNavigate ( ) ;
1482
@@ -35,13 +103,59 @@ export function FeaturedApps({ apps, onNavigate }: FeaturedAppsProps) {
35103 const found = apps . find ( ( app : App ) => String ( app . appId ) === String ( appId ) ) ;
36104 return found ;
37105 } )
38- . filter ( ( app : App | undefined ) : app is App => app !== undefined )
39- . slice ( 0 , 2 ) ; // Limit to 2 featured apps
106+ . filter ( ( app : App | undefined ) : app is App => app !== undefined ) ;
40107
41108 if ( featuredApps . length === 0 ) {
42109 return null ;
43110 }
44111
112+ // Determine grid layout based on number of apps
113+ const getGridClasses = ( count : number ) => {
114+ switch ( count ) {
115+ case 1 :
116+ return 'grid-cols-1' ;
117+ case 2 :
118+ return 'grid-cols-1 md:grid-cols-2' ;
119+ case 3 :
120+ return 'grid-cols-1 md:grid-cols-3' ;
121+ case 4 :
122+ return 'grid-cols-1 md:grid-cols-2' ;
123+ default :
124+ return 'grid-cols-1 md:grid-cols-2' ;
125+ }
126+ } ;
127+
128+ // For 5 apps, split into two rows (2 apps in first row, 3 in second)
129+ if ( featuredApps . length === 5 ) {
130+ const firstRowApps = featuredApps . slice ( 0 , 2 ) ;
131+ const secondRowApps = featuredApps . slice ( 2 , 5 ) ;
132+
133+ return (
134+ < div className = "mb-8 md:mb-12" >
135+ < h2
136+ className = { `text-2xl md:text-3xl font-semibold ${ theme . text } mb-4 md:mb-6` }
137+ style = { fonts . heading }
138+ >
139+ Featured Apps
140+ </ h2 >
141+ < div className = "space-y-6" >
142+ { /* First row: 2 apps */ }
143+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
144+ { firstRowApps . map ( ( app : App ) => (
145+ < FeaturedAppCard key = { app . appId } app = { app } handleAppClick = { handleAppClick } />
146+ ) ) }
147+ </ div >
148+ { /* Second row: 3 apps */ }
149+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-6" >
150+ { secondRowApps . map ( ( app : App ) => (
151+ < FeaturedAppCard key = { app . appId } app = { app } handleAppClick = { handleAppClick } />
152+ ) ) }
153+ </ div >
154+ </ div >
155+ </ div >
156+ ) ;
157+ }
158+
45159 return (
46160 < div className = "mb-8 md:mb-12" >
47161 < h2
@@ -50,73 +164,9 @@ export function FeaturedApps({ apps, onNavigate }: FeaturedAppsProps) {
50164 >
51165 Featured Apps
52166 </ h2 >
53- < div
54- className = { `grid ${ featuredApps . length === 2 ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-1' } gap-6` }
55- >
167+ < div className = { `grid ${ getGridClasses ( featuredApps . length ) } gap-6` } >
56168 { featuredApps . map ( ( app : App ) => (
57- < div
58- key = { app . appId }
59- onClick = { ( ) => handleAppClick ( app . appId ) }
60- className = "group relative overflow-hidden rounded-xl md:rounded-2xl border border-gray-200 dark:border-white/10 hover:border-gray-300 dark:hover:border-white/20 transition-all duration-300 cursor-pointer h-[300px] md:h-[400px]"
61- >
62- { /* Image at the top with fade effect */ }
63- < div className = "absolute inset-0 z-0" >
64- { /* Gradient overlay that fades from solid color to transparent */ }
65- < div
66- className = "absolute inset-0 z-10"
67- style = { {
68- background : `linear-gradient(to top, ${ 'rgba(255, 66, 5, 0.85)' } 0%, ${ 'rgba(255, 66, 5, 0.7)' } 15%, ${ 'rgba(255, 66, 5, 0.4)' } 30%, transparent 50%, transparent 100%)` ,
69- } }
70- />
71-
72- { /* App logo/image */ }
73- < div className = "absolute inset-0 flex items-start justify-center pt-12 opacity-60 group-hover:opacity-80 transition-opacity duration-300" >
74- < div className = "w-48 h-48 rounded-2xl overflow-hidden" >
75- < Logo
76- logo = { app . logo }
77- alt = { `${ app . name } logo` }
78- className = "w-full h-full object-cover"
79- />
80- </ div >
81- </ div >
82- </ div >
83-
84- { /* Content at the bottom */ }
85- < div className = "absolute bottom-0 left-0 right-0 p-8 z-20" >
86- < div className = "flex items-start justify-between mb-3" >
87- < div className = "flex-1" >
88- < h3 className = { `text-2xl font-semibold ${ theme . text } mb-2` } style = { fonts . heading } >
89- { app . name }
90- </ h3 >
91- < p className = { `${ theme . text } text-sm line-clamp-2 opacity-80` } style = { fonts . body } >
92- { app . description || 'No description available' }
93- </ p >
94- </ div >
95- </ div >
96-
97- < div className = "flex items-center gap-3" >
98- < span
99- className = { `px-3 py-1 rounded-full text-xs font-medium ${ theme . bg } ${ theme . text } backdrop-blur-sm ${ theme . cardBorder } border` }
100- style = { fonts . heading }
101- >
102- v{ app . activeVersion }
103- </ span >
104- < span
105- className = { `px-3 py-1 rounded-full text-xs font-medium ${ theme . bg } ${ theme . text } backdrop-blur-sm ${ theme . cardBorder } border` }
106- style = { fonts . heading }
107- >
108- { app . deploymentStatus === 'prod'
109- ? 'LIVE'
110- : app . deploymentStatus === 'test'
111- ? 'BETA'
112- : app . deploymentStatus ?. toUpperCase ( ) }
113- </ span >
114- </ div >
115- </ div >
116-
117- { /* Hover effect overlay */ }
118- < div className = "absolute inset-0 bg-black/0 group-hover:bg-black/5 transition-colors duration-300 z-5" />
119- </ div >
169+ < FeaturedAppCard key = { app . appId } app = { app } handleAppClick = { handleAppClick } />
120170 ) ) }
121171 </ div >
122172 </ div >
0 commit comments