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