import { Section, SectionHeader, Spinner } from '@woocommerce/components'; import { __ } from '@wordpress/i18n'; import { useState, useCallback, useMemo, Suspense, useEffect, useRef, } from '@wordpress/element'; import { Button, Card, CardBody, Flex, Icon, Notice, } from '@wordpress/components'; import { LazyBookingPrereqsNotice } from '../shared/components'; import { update, shipping, comment, help, settings as settingsIcon } from '@wordpress/icons'; import { WelcomeCard, ActionCard, FeedbackModal, LandingDashboardCards, OrderTable, OnboardingCard, } from '../features/orders'; import { getSettingsPageUrl, getPluginSettingsPageUrl, getShipmentsPageUrl, validateAddresses, } from '../shared/utils'; import { useWindowSize, useOrders, useSettings, useParcel2goAuth, usePrepay, } from '../shared/hooks'; import BulkShip from '../features/orders/bulk-ship'; import { PrePayBalance } from '../types'; const ROWS_PER_PAGE = 20; interface LandingPageProps { onShipNow: (orderId: number) => void; } const LandingPage = ({ onShipNow }: LandingPageProps) => { const { orders, total, page, loading, error, fetchOrders } = useOrders(ROWS_PER_PAGE); const { startLogin, isLoading, isLinked, error: loginError, } = useParcel2goAuth(); const { settings, saveOnboardingSettings, refetch: refetchSettings, loading: settingsLoading } = useSettings(); const { fetchBalance, makePrepayPayment, createTopUp, isPrepayLoading, prepayError, } = usePrepay(); const [prepayBalance, setPrepayBalance] = useState( null ); const [isFeedbackModalOpen, setIsFeedbackModalOpen] = useState(false); const [selectedOrderIds, setSelectedOrderIds] = useState([]); const onboardingSyncStepRef = useRef(null); useEffect(() => { if (!isLinked) return; let cancelled = false; fetchBalance().then((balance) => { if (!cancelled && balance) setPrepayBalance(balance); }); return () => { cancelled = true; }; }, [fetchBalance, isLinked]); const store = useMemo(() => { return settings?.shipping ?? null; }, [settings]); const handlePageChange = useCallback( (newPage: number) => { fetchOrders(newPage); }, [fetchOrders] ); const handleRefresh = useCallback(() => { fetchOrders(page); refetchSettings(); }, [fetchOrders, page, refetchSettings]); const handleGiveFeedback = useCallback(() => { setIsFeedbackModalOpen(true); }, []); const handleSelectionChange = useCallback((ids: number[]) => { setSelectedOrderIds(ids); }, []); const addressValidation = useMemo( () => validateAddresses(store, null), [store] ); const handleDismissOnboarding = useCallback(() => { void saveOnboardingSettings({ onboarding: { dismissed: true, currentStep: settings?.onboarding?.currentStep ?? 1, }, }); }, [saveOnboardingSettings, settings?.onboarding?.currentStep]); const handleSkipOnboardingStep = useCallback( (step: number) => { const nextStep = Math.min(step + 1, 3); void saveOnboardingSettings({ onboarding: { dismissed: false, currentStep: nextStep, }, }); }, [saveOnboardingSettings] ); const hasDefaultServiceSettings = useMemo(() => { return Object.keys(settings?.defaultServiceCouriers || {}).length > 0; }, [settings?.defaultServiceCouriers]); useEffect(() => { if (!settings) return; const persistedStep = settings.onboarding?.currentStep ?? 1; const derivedStep = hasDefaultServiceSettings ? 3 : isLinked ? 2 : 1; if (persistedStep >= derivedStep) { onboardingSyncStepRef.current = null; return; } if (onboardingSyncStepRef.current === derivedStep) { return; } onboardingSyncStepRef.current = derivedStep; void saveOnboardingSettings({ onboarding: { dismissed: settings.onboarding?.dismissed ?? false, currentStep: derivedStep, }, }).catch(() => { onboardingSyncStepRef.current = null; }); }, [hasDefaultServiceSettings, isLinked, saveOnboardingSettings, settings]); const showOnboarding = settings && !settings.onboarding?.dismissed; // Auto-select orders without bulkError when orders load useEffect(() => { if (loading || !Array.isArray(orders)) return; const selectableOrderIds = orders .filter((order) => !order.bulkError) .map((order) => order.id); setSelectedOrderIds(selectableOrderIds); }, [loading, orders]); const refetchBalance = useCallback(async () => { if (!isLinked) return; const balance = await fetchBalance(); if (balance) setPrepayBalance(balance); }, [fetchBalance, isLinked]); return (
{showOnboarding && ( )} {!loading && !addressValidation.valid && (
)} {isLinked && ( )} {loading && ( )} {error && ( {error} )} {!loading && !error && Array.isArray(orders) && (orders.length || total > 0 ? ( ) : (

{__( 'No orders ready to ship', 'parcel2go-shipping' )}

{__( 'When customers place orders with a "Processing" status, they\'ll appear here ready for you to book shipping.', 'parcel2go-shipping' )}

))} 0 ? 'space-between' : 'flex-start' } > {isLinked && selectedOrderIds.length > 0 && ( selectedOrderIds.includes(order.id) )} settings={settings} isLinked={isLinked} onComplete={() => { setSelectedOrderIds([]); fetchBalance(); handleRefresh(); }} /> )}
{!isLinked && (
window.location.assign(getShipmentsPageUrl()) } /> window.location.assign(getPluginSettingsPageUrl())} />
)}
setIsFeedbackModalOpen(false)} /> { window.open( 'https://www.parcel2go.com/help-centre/parcels/question/general/how-do-i-contact-parcel2go', '_blank' ); }} />
); }; export default LandingPage;