import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import clsx from 'clsx'; import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks'; import { useSetShippingOptionMutation, useSetAttributeBasedShippingOptionsMutation, useSetDataSourceShippingOptionsMutation } from '@akinon/next/data/client/checkout'; import { setSelectedShippingOptions } from '@akinon/next/redux/reducers/checkout'; import { useLocalization } from '@akinon/next/hooks'; import { Button, Price, Radio } from '@theme/components'; import { partAttrs } from '@akinon/pz-theme/src/utils/part-styles'; import type { RootState } from '@theme/redux/store'; import AccordionSection, { SectionStatus } from '../accordion-section'; import { ShippingOptionsSkeleton } from '../skeletons/shipping-skeleton'; interface ShippingSectionProps { status: SectionStatus; onEdit: () => void; onComplete: () => void; editable?: boolean; } const ShippingSection = ({ status, onEdit, onComplete, editable = true }: ShippingSectionProps) => { const { t } = useLocalization(); const dispatch = useAppDispatch(); const { shippingOptions, attributeBasedShippingOptions, dataSourceShippingOptions, preOrder, selectedShippingOptions } = useAppSelector((state: RootState) => state.checkout); const { shipping_option, attribute_based_shipping_options, data_source_shipping_options } = preOrder ?? {}; const [setShippingOption, { isLoading: isSettingPlain }] = useSetShippingOptionMutation(); const [setAttributeBasedShippingOptions, { isLoading: isSettingAttribute }] = useSetAttributeBasedShippingOptionsMutation(); const [setDataSourceShippingOption, { isLoading: isSettingDataSource }] = useSetDataSourceShippingOptionsMutation(); const isSetting = isSettingPlain || isSettingAttribute || isSettingDataSource; const prevAttributeBasedRef = useRef(attributeBasedShippingOptions); const [selectedDataSourcePks, setSelectedDataSourcePks] = useState< { dataSourcePk: number; optionPk: number }[] | null >(null); const initializeAttributeOptions = useCallback(() => { if (!attribute_based_shipping_options) return; const next = { ...selectedShippingOptions }; let changed = false; Object.entries(attributeBasedShippingOptions).forEach(([key, options]) => { if (!next[key] || !options.some((opt) => opt.pk === next[key])) { next[key] = options[0].pk; changed = true; } }); if (changed) { dispatch(setSelectedShippingOptions(next)); setAttributeBasedShippingOptions(next); } }, [ attributeBasedShippingOptions, selectedShippingOptions, attribute_based_shipping_options, dispatch, setAttributeBasedShippingOptions ]); useEffect(() => { if (!data_source_shipping_options) return; setSelectedDataSourcePks( data_source_shipping_options.map((option) => ({ dataSourcePk: option.data_source.pk, optionPk: option.pk })) ); }, [data_source_shipping_options]); useEffect(() => { if ( JSON.stringify(prevAttributeBasedRef.current) !== JSON.stringify(attributeBasedShippingOptions) || Object.keys(selectedShippingOptions).length === 0 ) { initializeAttributeOptions(); prevAttributeBasedRef.current = attributeBasedShippingOptions; } }, [ attributeBasedShippingOptions, selectedShippingOptions, initializeAttributeOptions ]); const handlePlainOption = (pk: number) => { setShippingOption(pk); }; const handleAttributeOption = (key: string, pk: number) => { const next = { ...selectedShippingOptions, [key]: pk }; dispatch(setSelectedShippingOptions(next)); setAttributeBasedShippingOptions(next); }; const handleDataSourceOption = (dataSourcePk: number, optionPk: number) => { const updated = selectedDataSourcePks?.map((item) => item.dataSourcePk === dataSourcePk ? { ...item, optionPk } : item ); if (!updated) return; setSelectedDataSourcePks(updated); setDataSourceShippingOption(updated.map((item) => item.optionPk)); }; const summary = shipping_option ? (