'use client'; import { zodResolver } from '@hookform/resolvers/zod'; import { ArrowRightIcon } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { BillingSchema } from '@kit/billing'; import { Button } from '@kit/ui/button'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@kit/ui/form'; import { Label } from '@kit/ui/label'; import { RadioGroup, RadioGroupItem, RadioGroupItemLabel, } from '@kit/ui/radio-group'; import { Trans } from '@kit/ui/trans'; import { cn } from '@kit/ui/utils'; export function PlanPicker( props: React.PropsWithChildren<{ config: z.infer; onSubmit: (data: { planId: string }) => void; pending?: boolean; }>, ) { const intervals = props.config.products.reduce((acc, item) => { return Array.from( new Set([...acc, ...item.plans.map((plan) => plan.interval)]), ); }, []); const form = useForm({ reValidateMode: 'onChange', mode: 'onChange', resolver: zodResolver( z .object({ planId: z.string(), interval: z.string(), }) .refine( (data) => { const planFound = props.config.products .flatMap((item) => item.plans) .some((plan) => plan.id === data.planId); if (!planFound) { return false; } return intervals.includes(data.interval); }, { message: `Please pick a plan to continue`, path: ['planId'] }, ), ), defaultValues: { interval: intervals[0], planId: '', }, }); const { interval: selectedInterval } = form.watch(); return (
{ return ( Choose your billing interval
{intervals.map((interval) => { const selected = field.value === interval; return ( ); })}
); }} /> ( Pick your preferred plan {props.config.products.map((item) => { const variant = item.plans.find( (plan) => plan.interval === selectedInterval, ); if (!variant) { throw new Error('No plan found'); } return ( { form.setValue('planId', variant.id, { shouldValidate: true, }); }} />
{formatCurrency( item.currency.toLowerCase(), variant.price, )}
per {variant.interval}
); })}
)} />
); } function Price(props: React.PropsWithChildren) { return ( {props.children} ); } function formatCurrency(currencyCode: string, value: string) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currencyCode, }).format(value); }