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:
@@ -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';
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
71
packages/ui/src/makerkit/marketing/newsletter-signup.tsx
Normal file
71
packages/ui/src/makerkit/marketing/newsletter-signup.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user