Add newsletter signup components

This commit introduces two new components for handling newsletter signups. 'NewsletterSignup' captures the form elements for newsletter subscriptions, and 'NewsletterSignupContainer' wraps the form and handles the subscription flow, including loading, success, and error states. These components have been exposed in the marketing module's index file.
This commit is contained in:
gbuomprisco
2024-07-20 09:06:20 +02:00
parent 86d82d889c
commit 41578b8b65
3 changed files with 159 additions and 0 deletions

View File

@@ -10,3 +10,5 @@ export * from './footer';
export * from './feature-showcase';
export * from './feature-grid';
export * from './feature-card';
export * from './newsletter-signup';
export * from './newsletter-signup-container';

View File

@@ -0,0 +1,86 @@
'use client';
import { useCallback, useState } from 'react';
import { Alert, AlertDescription, AlertTitle } from '../../shadcn/alert';
import { Heading } from '../../shadcn/heading';
import { cn } from '../../utils';
import { Spinner } from '../spinner';
import { NewsletterSignup } from './newsletter-signup';
interface NewsletterSignupContainerProps
extends React.HTMLAttributes<HTMLDivElement> {
onSignup: (email: string) => Promise<void>;
heading?: string;
description?: string;
successMessage?: string;
errorMessage?: string;
}
export function NewsletterSignupContainer({
onSignup,
heading = 'Subscribe to our newsletter',
description = 'Get the latest updates and offers directly to your inbox.',
successMessage = 'Thank you for subscribing!',
errorMessage = 'An error occurred. Please try again.',
className,
...props
}: NewsletterSignupContainerProps) {
const [status, setStatus] = useState<
'idle' | 'loading' | 'success' | 'error'
>('idle');
const handleSubmit = useCallback(
async (data: { email: string }) => {
setStatus('loading');
try {
await onSignup(data.email);
setStatus('success');
} catch (error) {
console.error('Newsletter signup error:', error);
setStatus('error');
}
},
[onSignup],
);
return (
<div
className={cn('flex flex-col items-center space-y-4', className)}
{...props}
>
<div className="text-center">
<Heading level={4}>{heading}</Heading>
<p className="text-muted-foreground">{description}</p>
</div>
{status === 'idle' && <NewsletterSignup onSignup={handleSubmit} />}
{status === 'loading' && (
<div className="flex justify-center">
<Spinner className="h-8 w-8" />
</div>
)}
{status === 'success' && (
<div>
<Alert variant="success">
<AlertTitle>Success!</AlertTitle>
<AlertDescription>{successMessage}</AlertDescription>
</Alert>
</div>
)}
{status === 'error' && (
<div>
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>{errorMessage}</AlertDescription>
</Alert>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,71 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '../../shadcn/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from '../../shadcn/form';
import { Input } from '../../shadcn/input';
import { cn } from '../../utils';
const NewsletterFormSchema = z.object({
email: z.string().email('Please enter a valid email address'),
});
type NewsletterFormValues = z.infer<typeof NewsletterFormSchema>;
interface NewsletterSignupProps extends React.HTMLAttributes<HTMLDivElement> {
onSignup: (data: NewsletterFormValues) => void;
buttonText?: string;
placeholder?: string;
}
export function NewsletterSignup({
onSignup,
buttonText = 'Subscribe',
placeholder = 'Enter your email',
className,
...props
}: NewsletterSignupProps) {
const form = useForm<NewsletterFormValues>({
resolver: zodResolver(NewsletterFormSchema),
defaultValues: {
email: '',
},
});
return (
<div className={cn('w-full max-w-sm', className)} {...props}>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSignup)}
className="flex flex-col space-y-2"
>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input placeholder={placeholder} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
{buttonText}
</Button>
</form>
</Form>
</div>
);
}