| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- "use client";
- import { FieldValues, useForm, Controller } from "react-hook-form";
- import { cn } from "@heroui/theme";
- import { Radio, RadioGroup } from "@heroui/radio";
- import { useState } from "react";
- import { ProceedToCheckout } from "../ProceedToCheckout";
- import { useCustomToast } from "@utils/hooks/useToast";
- import { useCheckout } from "@utils/hooks/useCheckout";
- import { CustomRadioProps, ShippingMethodType } from "@components/checkout/type";
- import { useDispatch } from "react-redux";
- import { updateCart } from "@/store/slices/cart-slice";
- export default function ShippingMethod({
- shippingMethod,
- selectedShippingRate,
- currentStep
- }: {
- shippingMethod?: ShippingMethodType[];
- selectedShippingRate?: any;
- methodDesc?: string;
- currentStep?: string;
- }) {
- const { isSaving, saveCheckoutShipping } = useCheckout();
- const { showToast } = useCustomToast();
- const [isOpen, setIsOpen] = useState(currentStep !== "shipping");
- const [prevValues, setPrevValues] = useState({ currentStep, selectedShippingRate });
- if (currentStep !== prevValues.currentStep || selectedShippingRate !== prevValues.selectedShippingRate) {
- setPrevValues({ currentStep, selectedShippingRate });
- if (currentStep === "shipping") {
- setIsOpen(false);
- } else if (selectedShippingRate) {
- setIsOpen(true);
- }
- }
- const { control, handleSubmit } = useForm({
- mode: "onSubmit",
- defaultValues: {
- method: selectedShippingRate ?? "",
- },
- });
- const selectedMethodTitle = shippingMethod?.find(
- (method) => method.method === selectedShippingRate
- )?.label;
- const selectedMethodPrice = shippingMethod?.find(
- (method) => method.method === selectedShippingRate
- )?.price;
- const dispatch = useDispatch();
- const onSubmit = async (data: FieldValues) => {
- if (!data?.method) {
- showToast("Please Choose the Shipping Method", "warning");
- return;
- }
- try {
- const selectedRate = shippingMethod?.find(m => m.method === data.method);
- if (selectedRate) {
- dispatch(updateCart({
- // shippingMethod: selectedRate?.method || "",
- selectedShippingRate: selectedRate?.method || "",
- selectedShippingRateTitle: selectedRate?.label || "",
- }));
- }
- await saveCheckoutShipping(data?.method);
- } catch {
- showToast("Failed to save shipping method. Please try again.");
- }
- };
- return (
- <div>
- {(selectedShippingRate) ? (
- isOpen ? (
- <>
- <div className="mt-4 justify-between hidden sm:flex">
- <div className="flex">
- <p className="w-auto text-base font-normal text-black/60 dark:text-white/60 sm:w-[192px]">
- Shipping Method
- </p>
- <p className="text-base font-normal">{selectedMethodTitle} (${selectedMethodPrice})</p>
- </div>
- <div className="flex">
- <button
- onClick={() => {
- setIsOpen(!isOpen);
- }}
- className="cursor-pointer text-base font-normal text-black/[60%] underline dark:text-neutral-300"
- >
- Change
- </button>
- </div>
- </div>
- <div className="mt-4 block sm:hidden flex flex-col justify-between sm:flex-row relative ">
- <div className="flex justify-between flex-1 wrap">
- <p className="w-auto text-base font-normal text-black/60 dark:text-white/60 sm:w-[192px]">
- Shipping Method
- </p>
- <p className="text-base font-normal">{selectedMethodTitle} (${selectedMethodPrice})</p>
- </div>
- <button
- onClick={() => {
- setIsOpen(!isOpen);
- }}
- className="cursor-pointer absolute right-0 text-base font-normal text-black/[60%] underline dark:text-neutral-300"
- style={{ top: "-36px" }}
- >
- Change
- </button>
- </div>
- </>
- ) : (
- <form className="mt-6" onSubmit={handleSubmit(onSubmit)}>
- <div className="flex flex-col gap-5">
- {Array.isArray(shippingMethod) && (
- <Controller
- control={control}
- name="method"
- render={({ field }) => (
- <RadioGroup
- {...field}
- label=""
- value={field.value ?? ""}
- onValueChange={field.onChange}
- >
- {shippingMethod.map((method: any) => (
- <CustomRadio
- key={method?.code}
- className="inset-0 my-1 border border-solid border-neutral-300 dark:border-neutral-500"
- description={"$" + method?.price}
- value={method?.method}
- >
- <span className="text-neutral-700 dark:text-white">
- {method?.label}
- </span>
- </CustomRadio>
- ))}
- </RadioGroup>
- )}
- />
- )}
- </div>
- <div className="my-6 justify-self-end">
- <ProceedToCheckout buttonName="Next" pending={isSaving} />
- </div>
- </form>
- )
- ) : (
- <form className="mt-6" onSubmit={handleSubmit(onSubmit)}>
- <div className="flex flex-col gap-5">
- {Array.isArray(shippingMethod) && (
- <Controller
- control={control}
- name="method"
- render={({ field }) => {
- return (
- <RadioGroup
- {...field}
- label=""
- value={field.value ?? ""}
- onValueChange={(value) => {
- field.onChange(value);
- }}
- >
- {shippingMethod.map((method : any) => (
- <CustomRadio
- key={method?.code}
- className="inset-0 my-1 border border-solid border-neutral-300 dark:border-neutral-500"
- description={"$" + method?.price}
- value={method?.method}
- >
- <span className="text-neutral-700 dark:text-white">
- {method?.label}
- </span>
- </CustomRadio>
- ))}
- </RadioGroup>
- )
- }}
- />
- )}
- </div>
- <div className="my-6 justify-self-end">
- <ProceedToCheckout buttonName="Next" pending={isSaving} />
- </div>
- </form>
- )}
- </div>
- );
- }
- const CustomRadio = (props: CustomRadioProps) => {
- const { children, className, ...otherProps } = props;
- return (
- <Radio
- {...otherProps}
- classNames={{
- base: cn(
- "inline-flex m-0 bg-transparent hover:bg-transparent items-center",
- "flex-row items-baseline max-w-full cursor-pointer rounded-lg gap-4 p-4 border-2 border-transparent",
- "data-[selected=true]:border-primary",
- className
- ),
- hiddenInput: "peer absolute h-0 w-0 opacity-0",
- }}
- >
- {children}
- </Radio>
- );
- };
|