0
import { z } from "zod";

/* --- helpers ------------------------------------------------------------ */
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const intlPhoneRegex = /^\+?\d{10,15}$/;   // e.g. +923001234567

/* --- main schema -------------------------------------------------------- */
export const checkoutFormSchema = z.object({
  /** Contact */
  emailOrPhone: z
    .string()
    .trim()
    .min(1, "Email or mobile phone is required")
    .refine(
      (v) => emailRegex.test(v) || intlPhoneRegex.test(v),
      "Enter a valid email address or phone number"
    ),
  newsletter: z.boolean().optional(),

  /** Name */
  firstName: z.string().trim().min(1, "First name is required"),
  lastName: z.string().trim().min(1, "Last name is required"),

  /** Screenshot (exactly one image file) */
  screenshot: z.instanceof(File, {
    message: "Screenshot is required",
  }).refine((file) => file.size <= 5 * 1024 * 1024, "Max file size is 5MB"),

  /** Plan (readonly in the UI but still validated) */
  plan: z.string().min(1, "Plan is required"),
});

export const checkoutTier = async (userId: string, values: z.infer<typeof checkoutFormSchema>) => {
    const isValidated = checkoutFormSchema.safeParse(values);

    if(!isValidated.success) return { error: isValidated.error.errors[0].message };

    const {emailOrPhone, newsletter, firstName, lastName, screenshot, plan} = isValidated.data;

    try {
        console.log(emailOrPhone, newsletter, firstName, lastName, screenshot, plan);
        return {success: true, message: "Checkout Successfully!!"}
    } catch (error) {
        handleError(error)
    }
};

"use client";
import React, { useTransition } from 'react'
import { Input } from '../ui/input';
import { Checkbox } from '../ui/checkbox';
import { Button } from '../ui/button';
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { checkoutFormSchema } from '@/schemas/checkout';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form';
import { checkoutTier } from '@/lib/actions/transaction.actions';
import { useUser } from '@clerk/nextjs';
import { toast } from '@/hooks/use-toast';
import { ToastAction } from '../ui/toast';
import { useRouter } from 'next/navigation';
import { LoaderCircle } from 'lucide-react';
import { z } from 'zod';

const CheckoutForm = ({ name }: { name: string }) => {
    const [isPending, startTransition] = useTransition();

    const { user } = useUser();

    const router = useRouter();

    const UserId = user?.id as string;

    const form = useForm<z.infer<typeof checkoutFormSchema>>({
        resolver: zodResolver(checkoutFormSchema),
        defaultValues: {
            emailOrPhone: "",
            newsletter: false,
            firstName: "",
            lastName: "",
            screenshot: undefined,
            plan: name,
        }
    });

    function onSubmit(data: z.infer<typeof checkoutFormSchema>) {
        startTransition(async () => {
            console.log(`data: ${Object.values(data)}`);
            checkoutTier(UserId, data)
                .then((res) => {
                    if (res?.error) {
                        toast({
                            title: "Form Failed",
                            description: res?.error,
                            duration: 2000,
                            variant: "destructive",
                            action: (
                                <ToastAction altText="Dismiss">Dismiss</ToastAction>
                            )
                        })
                    }

                    if (res?.success) {
                        toast({
                            title: "Successfully Created!",
                            description: res.message,
                            duration: 2000,
                            variant: "success",
                            action: (
                                <ToastAction altText="Close">Close</ToastAction>
                            ),
                        })
                        router.push(`/checkout/success`);
                    }
                }).catch((err) => {
                    toast({
                        title: "Error!",
                        description: err?.message,
                        duration: 2000,
                        variant: "destructive",
                        action: (
                            <ToastAction altText="Close">Close</ToastAction>
                        ),
                    });
                    console.error(`Error: ${err}`);
                })
                .finally(() => {
                    form.reset();
                });
        })
    };

    return (
        <Form {...form}>
            <form
                className="space-y-8 bg-gray-100 p-6 border rounded-lg"
                onSubmit={form.handleSubmit(onSubmit)}
            >
                {/* Contact Section */}
                <div className="space-y-4">
                    <h2 className="text-xl font-bold">
                        Contact
                    </h2>

                    <FormField
                        control={form.control}
                        name="emailOrPhone"
                        render={({ field }) => (
                            <FormItem className='flex flex-col gap-2 w-full'>
                                <FormControl>
                                    <Input
                                        {...field}
                                        type="text"
                                        placeholder="Email or mobile phone number"
                                        className="w-full"
                                        onChange={(e) => field.onChange(e.target.value)}
                                        disabled={isPending}
                                    />
                                </FormControl>
                                <FormMessage />
                            </FormItem>
                        )}
                    />
                    {/* Newsletter */}
                    <FormField
                        control={form.control}
                        name="newsletter"
                        render={({ field }) => (
                            <FormItem className="flex items-center gap-2">
                                <FormControl>
                                    <Checkbox
                                        id="newsletter"
                                        checked={field.value}             // boolean ✔
                                        onCheckedChange={field.onChange} // (bool) => void ✔
                                        ref={field.ref}                  // preserve ref
                                        className="mt-1"
                                    />
                                </FormControl>

                                <FormLabel
                                    htmlFor="newsletter"
                                    className="flex items-center gap-2 font-normal text-sm"
                                >
                                    Email me with news and offers
                                </FormLabel>

                                <FormMessage />
                            </FormItem>
                        )}
                    />

                </div>

                <div className="space-y-4">
                    {/* Name */}
                    <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                        <FormField
                            control={form.control}
                            name="firstName"
                            render={({ field }) => (
                                <FormItem className="flex flex-col gap-1">
                                    <FormLabel
                                        htmlFor="firstName"
                                        className="font-medium text-lg"
                                    >
                                        First Name
                                    </FormLabel>
                                    <FormControl>
                                        <Input
                                            {...field}
                                            type="text"
                                            id='firstName'
                                            placeholder="First Name"
                                            className="w-full"
                                            onChange={(e) => field.onChange(e.target.value)}
                                            disabled={isPending}
                                        />
                                    </FormControl>
                                    <FormMessage />
                                </FormItem>
                            )}
                        />

                        <FormField
                            control={form.control}
                            name="lastName"
                            render={({ field }) => (
                                <FormItem className="flex flex-col gap-1">
                                    <FormLabel
                                        htmlFor="lastName"
                                        className="font-medium text-lg"
                                    >
                                        Last Name
                                    </FormLabel>
                                    <FormControl>
                                        <Input
                                            {...field}
                                            id='lastName'
                                            type="text"
                                            placeholder="Last Name"
                                            className="w-full"
                                            onChange={(e) => field.onChange(e.target.value)}
                                            disabled={isPending}
                                        />
                                    </FormControl>


                                    <FormMessage />
                                </FormItem>
                            )}
                        />
                    </div>

                    {/* ScreenShot */}
                    <FormField
                        control={form.control}
                        name="screenshot"
                        render={({ field }) => {
                            const { value, ...rest } = field;
                            return (
                                <FormItem className="flex flex-col gap-1">
                                    <FormLabel
                                        htmlFor="screenshot"
                                        className="font-medium text-lg"
                                    >
                                        ScreenShot
                                    </FormLabel>
                                    <FormControl>
                                        <Input
                                            {...rest}           // name, ref, onBlur
                                            id="screenshot"
                                            type="file"
                                            accept="image/*"
                                            multiple={false}
                                            value={undefined}   // keep it uncontrolled ←✅
                                            onChange={(e) =>
                                                rest.onChange((e.target.files && e.target.files[0] as File) /* File | undefined */)
                                            }
                                            disabled={isPending}
                                        />
                                    </FormControl>
                                    <FormMessage />
                                </FormItem>
                            )
                        }}
                    />

                    {/* Plan */}
                    <FormField
                        control={form.control}
                        name="plan"
                        render={({ field }) => (
                            <FormItem className="flex flex-col gap-1">
                                <FormLabel
                                    htmlFor="plan"
                                    className="font-medium text-lg"
                                >
                                    Plan
                                </FormLabel>
                                <FormControl>
                                    <Input
                                        {...field}
                                        id='plan'
                                        type="text"
                                        placeholder="Last Name"
                                        value={field.value}
                                        className="w-full"
                                        disabled={true}
                                    />
                                </FormControl>


                                <FormMessage />
                            </FormItem>
                        )}
                    />

                    <Button disabled={isPending}>
                        {
                            isPending ? (
                                <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />
                            ) : (
                                <span>Checkout</span>
                            )
                        }
                    </Button>
                </div>
            </form>
        </Form>
    )
}

export default CheckoutForm;

Browsor Console: 1-) data: [email protected],true,Muhammad,Ahsaan Abbasi,[object File],Monthly 2) CheckoutForm.tsx:78 Error: Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.

This error is that I want to see that file is uploaded and shown on the server console, and then upload it to the S3 bucket before on server action I don't want to upload it anywhere I want to send all the requested data on server then proceed and used it before don't

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.