import React, { useEffect, useState, useMemo, useCallback, useRef, ChangeEvent } from 'react'; import clsx from 'clsx'; import { Icon, LoaderSpinner } from '@akinon/next/components'; import { useAppSelector } from '@akinon/next/redux/hooks'; import { useSetDeliveryOptionMutation, useSetRetailStoreMutation, useSetShippingOptionMutation, useSetAddressesMutation } from '@akinon/next/data/client/checkout'; import { RootState } from 'redux/store'; export interface ClickCollectTranslationsProps { deliveryFromTheStore: string; deliveryStore: string; } export interface ClickCollectRendererProps { renderContainer?: (props: { children: React.ReactNode; isActive: boolean; handleActive: () => void; handleDeactivate: () => void; }) => React.ReactNode; renderInactiveState?: (props: { translations: ClickCollectTranslationsProps; }) => React.ReactNode; renderActiveState?: (props: { translations: ClickCollectTranslationsProps; cities: any[]; stores: any[]; selectedCity: any; handleCityChange: (e: ChangeEvent) => void; handleStoreChange: (e: ChangeEvent) => void; handleDeactivate: () => void; }) => React.ReactNode; renderCloseButton?: (props: { handleDeactivate: () => void; }) => React.ReactNode; renderLoader?: () => React.ReactNode; } const defaultTranslations = { deliveryFromTheStore: 'DELIVERY FROM THE STORE', deliveryStore: 'Delivery Store' }; const DefaultContainer = ({ children, isActive, handleActive }: { children: React.ReactNode; isActive: boolean; handleActive: () => void; }) => (
{ !isActive && handleActive(); }} className={clsx( 'relative w-full min-h-[8rem] border shadow p-4', "hover:after:content-[''] hover:after:border-4 hover:after:opacity-30 hover:after:transition-opacity", 'after:border-secondary-400 after:absolute after:inset-0 after:opacity-0 after:duration-150 after:-z-10' )} > {children}
); const DefaultInactiveState = ({ translations }: { translations: ClickCollectTranslationsProps; }) => (
{translations.deliveryFromTheStore}
); const DefaultActiveState = ({ translations, cities, stores, handleCityChange, handleStoreChange }: { translations: ClickCollectTranslationsProps; cities: any[]; stores: any[]; selectedCity: any; handleCityChange: (e: ChangeEvent) => void; handleStoreChange: (e: ChangeEvent) => void; handleDeactivate: () => void; }) => (
); const DefaultCloseButton = ({ handleDeactivate }: { handleDeactivate: () => void; }) => (
); const DefaultLoader = () => (
); export function ClickCollect({ addressTypeParam, translations, renderer = {} }: { addressTypeParam: string; translations: ClickCollectTranslationsProps; renderer?: ClickCollectRendererProps; }) { const _translations = { ...defaultTranslations, ...translations }; const [loading, setLoading] = useState(false); const { deliveryOptions, retailStores } = useAppSelector( (state: RootState) => state.checkout ); const { billing_address, shipping_address, retail_store, delivery_option } = useAppSelector((state: RootState) => state.checkout.preOrder) ?? {}; const cityPkRef = useRef(null); const retailStore = useMemo( () => deliveryOptions?.find( (value) => value?.delivery_option_type === 'retail_store' ), [deliveryOptions] ); const [stores, setStores] = useState([]); const [setDeliveryOption, { isLoading: deliveryLoading }] = useSetDeliveryOptionMutation(); const [setRetailStore, { isLoading: retailLoading }] = useSetRetailStoreMutation(); const [setShippingOption, { isLoading: shippingLoading }] = useSetShippingOptionMutation(); const [setAddresses, { isLoading: addressLoading }] = useSetAddressesMutation(); const [selectedCity, setSelectedCity] = useState(null); const [selectedStore, setSelectedStore] = useState(null); const [isActive, setIsActive] = useState(false); const [retailStoresForCity, setRetailStoresForCity] = useState([]); const defaultDeliveryOption = useMemo( () => deliveryOptions?.find( (option) => option?.delivery_option_type === 'customer' ), [deliveryOptions] ); const retailStoreDeliveryOption = useMemo( () => deliveryOptions?.find( (option) => option?.delivery_option_type === 'retail_store' ), [deliveryOptions] ); const postRetailStore = async ({ retailStorePk, billingAddressPk }) => { await setRetailStore({ retailStorePk: retailStorePk, billingAddressPk: billingAddressPk }); }; const handleDeactivate = useCallback(async () => { try { await setDeliveryOption(defaultDeliveryOption?.pk); await setAddresses({ shippingAddressPk: shipping_address?.pk, billingAddressPk: billing_address?.pk }); } catch (error) { console.error('Error updating details:', error); } }, [setDeliveryOption, setAddresses]); const handleActive = useCallback(async () => { if (retailStore) { await setShippingOption(retailStore.pk).unwrap(); } const response = await setDeliveryOption( retailStoreDeliveryOption.pk ).unwrap(); const retailStores = response?.context_list?.find( (context) => context.page_slug === 'retailstoreselectionpage' ); setStores(retailStores?.page_context['retail_stores']); postRetailStore({ retailStorePk: retail_store ? retail_store.pk : retailStores.page_context['retail_stores'][0]?.pk, billingAddressPk: billing_address ? billing_address.pk : null }); }, [ retailStore, retailStoreDeliveryOption, setStores, postRetailStore, setShippingOption, setDeliveryOption, retail_store, billing_address ]); const handleCityChange = useCallback( async (e: ChangeEvent) => { const cityPk = parseInt(e.target.value); cityPkRef.current = cityPk; const correspondingCity = retailStores.find( (store) => store?.township?.city?.pk === cityPk )?.township.city; setSelectedCity(correspondingCity ? correspondingCity.name : null); const storesInCity = retailStores.filter( (store) => store?.township?.city?.pk === cityPk ); setRetailStoresForCity(storesInCity); if (storesInCity.length > 0) { setSelectedStore(storesInCity[0].pk); await postRetailStore({ retailStorePk: storesInCity[0].pk, billingAddressPk: billing_address?.pk }); } }, [retailStores] ); const handleStoreChange = useCallback( (e: ChangeEvent) => { const storePk = parseInt(e.target.value); setSelectedStore(storePk); postRetailStore({ retailStorePk: storePk, billingAddressPk: billing_address?.pk }); }, [postRetailStore, billing_address?.pk] ); const getUniqueCities = useMemo(() => { const uniqueCities = []; retailStores.forEach((store: any) => { if ( !uniqueCities.some((city) => city?.pk === store?.township?.city?.pk) ) { uniqueCities.push(store?.township?.city); } }); return uniqueCities; }, [retailStores]); useEffect(() => { setLoading( [deliveryLoading, retailLoading, shippingLoading, addressLoading].some( Boolean ) ); }, [deliveryLoading, retailLoading, shippingLoading, addressLoading]); useEffect(() => { setIsActive(delivery_option?.pk === retailStoreDeliveryOption?.pk); }, [delivery_option?.pk, retailStoreDeliveryOption]); useEffect(() => { if (retailStores.length > 0) { const defaultCity = retailStores[0]?.township?.city; const defaultStore = retailStores.find( (store) => store.township.city.pk === defaultCity.pk ); if (!selectedCity) { setSelectedCity(defaultCity.pk); cityPkRef.current = defaultCity.pk; } if (!selectedStore) { setSelectedStore(defaultStore.pk); postRetailStore({ retailStorePk: defaultStore.pk, billingAddressPk: billing_address?.pk }); } if (cityPkRef.current) { const storesInCity = retailStores.filter( (store) => store.township.city.pk === cityPkRef.current ); setRetailStoresForCity(storesInCity); } } }, [retailStores, cityPkRef.current]); if (addressTypeParam !== 'shippingAddressPk') return; const RenderContainer = renderer.renderContainer || DefaultContainer; const RenderInactiveState = renderer.renderInactiveState || DefaultInactiveState; const RenderActiveState = renderer.renderActiveState || DefaultActiveState; const RenderCloseButton = renderer.renderCloseButton || DefaultCloseButton; const RenderLoader = renderer.renderLoader || DefaultLoader; return (
{isActive && } {loading && } {!isActive ? ( ) : ( )}
); }