1+ import { Box , Button , Grid , GridItem , Heading , Stack , Text , useToast } from '@chakra-ui/react' ;
2+ import { FC , useEffect , useState } from 'react' ;
3+
4+ import { PageText } from '../UI' ;
5+ import { WishlistItem } from './schemas/Wishlist' ;
6+
7+ interface WishlistSelectionProps {
8+ onSelectWishlist : ( wishlistItem : WishlistItem ) => void ;
9+ }
10+
11+ export const WishlistSelection : FC < WishlistSelectionProps > = ( { onSelectWishlist } ) => {
12+ const [ wishlistItems , setWishlistItems ] = useState < WishlistItem [ ] > ( [ ] ) ;
13+ const [ isLoading , setIsLoading ] = useState ( true ) ;
14+ const [ selectedItem , setSelectedItem ] = useState < WishlistItem | null > ( null ) ;
15+ const toast = useToast ( ) ;
16+
17+ useEffect ( ( ) => {
18+ const fetchWishlistItems = async ( ) => {
19+ try {
20+ const response = await fetch ( '/api/wishlist-items' ) ;
21+ if ( ! response . ok ) {
22+ throw new Error ( 'Failed to fetch wishlist items' ) ;
23+ }
24+ const items = await response . json ( ) ;
25+ setWishlistItems ( items ) ;
26+ } catch ( error ) {
27+ console . error ( 'Error fetching wishlist items:' , error ) ;
28+ toast ( {
29+ title : 'Error loading wishlist items' ,
30+ description : 'Please refresh the page and try again.' ,
31+ status : 'error' ,
32+ duration : 5000 ,
33+ isClosable : true
34+ } ) ;
35+ } finally {
36+ setIsLoading ( false ) ;
37+ }
38+ } ;
39+
40+ fetchWishlistItems ( ) ;
41+ } , [ toast ] ) ;
42+
43+ const handleSelectItem = ( item : WishlistItem ) => {
44+ setSelectedItem ( item ) ;
45+ } ;
46+
47+ const handleContinue = ( ) => {
48+ if ( selectedItem ) {
49+ onSelectWishlist ( selectedItem ) ;
50+ }
51+ } ;
52+
53+ if ( isLoading ) {
54+ return (
55+ < Stack spacing = { 8 } align = "center" py = { 16 } >
56+ < Heading size = "lg" color = "brand.heading" >
57+ Loading Wishlist Items...
58+ </ Heading >
59+ < Text > Please wait while we fetch the available opportunities.</ Text >
60+ </ Stack >
61+ ) ;
62+ }
63+
64+ if ( wishlistItems . length === 0 ) {
65+ return (
66+ < Stack spacing = { 8 } align = "center" py = { 16 } >
67+ < Heading size = "lg" color = "brand.heading" >
68+ No Wishlist Items Available
69+ </ Heading >
70+ < Text > There are currently no active wishlist items available for application.</ Text >
71+ </ Stack >
72+ ) ;
73+ }
74+
75+ return (
76+ < Stack spacing = { 8 } >
77+ < Box textAlign = "center" >
78+ < Heading size = "lg" mb = { 4 } color = "brand.heading" >
79+ Select a Wishlist Item
80+ </ Heading >
81+ < PageText mb = { 6 } >
82+ Choose from the wishlist items below that best matches your project or interests.
83+ Each item represents a specific need or opportunity that the Ethereum ecosystem is looking to address.
84+ </ PageText >
85+ </ Box >
86+
87+ < Grid templateColumns = { { base : '1fr' , md : 'repeat(2, 1fr)' , lg : 'repeat(3, 1fr)' } } gap = { 6 } >
88+ { wishlistItems . map ( ( item ) => (
89+ < GridItem key = { item . Id } >
90+ < Box
91+ p = { 6 }
92+ border = "2px solid"
93+ borderColor = { selectedItem ?. Id === item . Id ? 'brand.orange.100' : 'brand.border' }
94+ borderRadius = "lg"
95+ cursor = "pointer"
96+ transition = "all 0.2s"
97+ bg = { selectedItem ?. Id === item . Id ? 'brand.orange.10' : 'white' }
98+ _hover = { {
99+ borderColor : 'brand.orange.100' ,
100+ transform : 'translateY(-2px)' ,
101+ shadow : 'md'
102+ } }
103+ onClick = { ( ) => handleSelectItem ( item ) }
104+ h = "full"
105+ >
106+ < Stack spacing = { 3 } h = "full" >
107+ < Heading size = "md" color = "brand.heading" noOfLines = { 2 } >
108+ { item . Name }
109+ </ Heading >
110+
111+ { item . Category__c && (
112+ < Text fontSize = "sm" color = "brand.orange.100" fontWeight = "600" >
113+ { item . Category__c }
114+ </ Text >
115+ ) }
116+
117+ < Text fontSize = "sm" color = "brand.paragraph" flex = "1" noOfLines = { 4 } >
118+ { item . Description__c }
119+ </ Text >
120+
121+ { item . Skills_Required__c && (
122+ < Box >
123+ < Text fontSize = "xs" color = "brand.helpText" fontWeight = "600" mb = { 1 } >
124+ Skills Required:
125+ </ Text >
126+ < Text fontSize = "xs" color = "brand.helpText" noOfLines = { 2 } >
127+ { item . Skills_Required__c }
128+ </ Text >
129+ </ Box >
130+ ) }
131+
132+ { item . Estimated_Effort__c && (
133+ < Text fontSize = "xs" color = "brand.helpText" >
134+ < Text as = "span" fontWeight = "600" > Estimated Effort:</ Text > { item . Estimated_Effort__c }
135+ </ Text >
136+ ) }
137+ </ Stack >
138+ </ Box >
139+ </ GridItem >
140+ ) ) }
141+ </ Grid >
142+
143+ { selectedItem && (
144+ < Box
145+ mt = { 8 }
146+ p = { 6 }
147+ bg = "brand.newsletter.bgGradient.start"
148+ borderRadius = "lg"
149+ borderLeft = "4px solid"
150+ borderLeftColor = "brand.orange.100"
151+ >
152+ < Stack spacing = { 3 } >
153+ < Heading size = "md" color = "brand.heading" >
154+ Selected: { selectedItem . Name }
155+ </ Heading >
156+ < Text color = "brand.paragraph" >
157+ { selectedItem . Description__c }
158+ </ Text >
159+ { selectedItem . Expected_Deliverables__c && (
160+ < Box >
161+ < Text fontWeight = "600" color = "brand.heading" mb = { 1 } >
162+ Expected Deliverables:
163+ </ Text >
164+ < Text color = "brand.paragraph" >
165+ { selectedItem . Expected_Deliverables__c }
166+ </ Text >
167+ </ Box >
168+ ) }
169+ </ Stack >
170+ </ Box >
171+ ) }
172+
173+ < Box textAlign = "center" mt = { 8 } >
174+ < Button
175+ isDisabled = { ! selectedItem }
176+ onClick = { handleContinue }
177+ bg = "brand.orange.100"
178+ color = "white"
179+ px = { 8 }
180+ py = { 6 }
181+ fontSize = "input"
182+ fontWeight = { 700 }
183+ borderRadius = { 0 }
184+ _hover = { { bg : 'brand.orange.hover' } }
185+ _disabled = { {
186+ bg : 'gray.300' ,
187+ cursor : 'not-allowed'
188+ } }
189+ >
190+ Continue with Application
191+ </ Button >
192+ </ Box >
193+ </ Stack >
194+ ) ;
195+ } ;
0 commit comments