/** * Default settings overrides form: default weight, tariff code, country of manufacture. * Owns state, validation (Zod), and errors. Notifies page via onChange and onInteraction(). */ import { __ } from '@wordpress/i18n'; import { memo, useState, useEffect, useCallback, useRef } from '@wordpress/element'; import { Flex } from '@wordpress/components'; import { SettingsField } from './settings-field'; import type { DefaultSettingsOverrides } from '../../types/settings'; import type { SettingsFieldSelectOption } from './settings-field'; import { defaultSettingsOverridesSchema } from './utils/schemas'; import { getSectionFieldErrors } from './utils/getSettingsFieldErrors'; import { formatDisplayNumber, getWeightBounds } from '../../shared/utils'; export const SETTINGS_OVERRIDES_FORM_ID = 'settings-overrides' as const; export interface DefaultSettingsOverridesFormProps { defaultValue: DefaultSettingsOverrides; weightUnit?: string; onChange: (formId: string, value: DefaultSettingsOverrides) => void; onInteraction?: () => void; resetTrigger?: number; submitErrors?: Record; countryOptions: SettingsFieldSelectOption[]; } function DefaultSettingsOverridesFormInner({ defaultValue, weightUnit = 'kg', onChange, onInteraction, resetTrigger = 0, submitErrors, countryOptions, }: DefaultSettingsOverridesFormProps) { const [value, setValue] = useState(() => ({ ...defaultValue, })); const [errors, setErrors] = useState>({}); const hasInteracted = useRef(false); const weightBounds = getWeightBounds(weightUnit); useEffect(() => { setValue({ ...defaultValue }); setErrors({}); hasInteracted.current = false; }, [defaultValue, resetTrigger]); useEffect(() => { const result = defaultSettingsOverridesSchema.safeParse(value); const nextErrors: Record = result.success ? {} : getSectionFieldErrors(result.error); if (value.defaultWeightKg > 0 && value.defaultWeightKg < weightBounds.min) { nextErrors.defaultWeightKg = __( `Default weight must be at least ${formatDisplayNumber(weightBounds.min)} ${weightUnit} (0.01kg equivalent).`, 'parcel2go-shipping' ); } if (result.success) { setErrors(nextErrors); } else { setErrors(nextErrors); } }, [value, weightBounds.min, weightUnit]); const displayErrors = submitErrors ?? errors; const handleChange = useCallback( (field: keyof DefaultSettingsOverrides, v: string | number) => { if (!hasInteracted.current) { hasInteracted.current = true; onInteraction?.(); } const next: DefaultSettingsOverrides = { ...value }; if (field === 'defaultWeightKg') { next.defaultWeightKg = typeof v === 'number' ? v : parseFloat(String(v)) || 0; } else if (field === 'defaultTariffCode') { next.defaultTariffCode = String(v); } else { next.defaultCountryOfManufacture = String(v); } setValue(next); onChange(SETTINGS_OVERRIDES_FORM_ID, next); }, [value, onChange, onInteraction] ); return ( handleChange('defaultWeightKg', parseFloat(v || '0') || 0) } type="number" min={weightBounds.min} step={weightBounds.step} error={displayErrors.defaultWeightKg} description={__( 'The default weight to apply when a product has no weight set.', 'parcel2go-shipping' )} /> handleChange('defaultTariffCode', v)} error={displayErrors.defaultTariffCode} description={__( 'The default tariff code to apply to your products that do not have a tariff code set. 10 characters max.', 'parcel2go-shipping' )} /> handleChange('defaultCountryOfManufacture', v)} options={countryOptions} error={displayErrors.defaultCountryOfManufacture} description={__( 'The default country of manufacture to apply to your products that do not have a country of manufacture set.', 'parcel2go-shipping' )} /> ); } export const DefaultSettingsOverridesForm = memo( DefaultSettingsOverridesFormInner );