@@ -25,7 +25,11 @@ import { BoundlessButton } from '../buttons';
2525import { Input } from '../ui/input' ;
2626import gsap from 'gsap' ;
2727import { useGSAP } from '@gsap/react' ;
28- import { newsletterSubscribe } from '@/lib/api/waitlist' ;
28+ import {
29+ newsletterSubscribe ,
30+ type NewsletterApiError ,
31+ type NewsletterTag ,
32+ } from '@/lib/api/waitlist' ;
2933import { Button } from '../ui/button' ;
3034
3135const formSchema = z . object ( {
@@ -42,6 +46,7 @@ const Newsletter = ({
4246} ) => {
4347 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
4448 const [ error , setError ] = useState < string | null > ( null ) ;
49+ const [ selectedTags , setSelectedTags ] = useState < NewsletterTag [ ] > ( [ ] ) ;
4550 const form = useForm < z . infer < typeof formSchema > > ( {
4651 resolver : zodResolver ( formSchema ) ,
4752 defaultValues : {
@@ -82,15 +87,23 @@ const Newsletter = ({
8287 const onSubmit = async ( values : z . infer < typeof formSchema > ) => {
8388 setError ( null ) ;
8489 setIsSubmitting ( true ) ;
85-
8690 try {
8791 await newsletterSubscribe ( {
8892 email : values . email ,
8993 name : values . name ,
94+ source : 'website' ,
95+ tags : selectedTags ,
9096 } ) ;
91- } catch {
92- setError ( 'Failed to submit form. Please try again.' ) ;
93- setIsSubmitting ( false ) ;
97+ onOpenChange ( false ) ;
98+ window . location . href = '/newsletter/confirmed' ;
99+ } catch ( err ) {
100+ const e = err as NewsletterApiError ;
101+ if ( e . code === 'ALREADY_SUBSCRIBED' )
102+ setError ( 'This email is already subscribed.' ) ;
103+ else if ( e . code === 'RATE_LIMITED' )
104+ setError ( 'Too many attempts. Please try again in a minute.' ) ;
105+ else if ( e . code === 'INVALID_TAGS' ) setError ( 'Invalid topic selection.' ) ;
106+ else setError ( 'Failed to submit form. Please try again.' ) ;
94107 } finally {
95108 setIsSubmitting ( false ) ;
96109 }
@@ -186,6 +199,37 @@ const Newsletter = ({
186199 </ FormItem >
187200 ) }
188201 />
202+
203+ < div className = 'mt-4 flex flex-wrap gap-3' >
204+ { (
205+ [
206+ 'bounties' ,
207+ 'hackathons' ,
208+ 'grants' ,
209+ 'updates' ,
210+ ] as NewsletterTag [ ]
211+ ) . map ( tag => (
212+ < label
213+ key = { tag }
214+ className = 'flex cursor-pointer items-center gap-1.5 text-sm text-[#D9D9D9]'
215+ >
216+ < input
217+ type = 'checkbox'
218+ className = 'accent-[#A7F950]'
219+ checked = { selectedTags . includes ( tag ) }
220+ onChange = { ( ) =>
221+ setSelectedTags ( p =>
222+ p . includes ( tag )
223+ ? p . filter ( t => t !== tag )
224+ : [ ...p , tag ]
225+ )
226+ }
227+ />
228+ { tag . charAt ( 0 ) . toUpperCase ( ) + tag . slice ( 1 ) }
229+ </ label >
230+ ) ) }
231+ </ div >
232+
189233 < BoundlessButton
190234 variant = { form . formState . isValid ? 'default' : 'outline' }
191235 type = 'submit'
0 commit comments