111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
import React, { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
import type { CheckoutPackage, CheckoutStepId, CheckoutWizardContextValue, CheckoutWizardState } from "./types";
|
|
|
|
interface CheckoutWizardProviderProps {
|
|
initialPackage: CheckoutPackage;
|
|
packageOptions: CheckoutPackage[];
|
|
initialStep?: CheckoutStepId;
|
|
initialAuthUser?: CheckoutWizardState['authUser'];
|
|
initialIsAuthenticated?: boolean;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const CheckoutWizardContext = createContext<CheckoutWizardContextValue | undefined>(undefined);
|
|
|
|
export const CheckoutWizardProvider: React.FC<CheckoutWizardProviderProps> = ({
|
|
initialPackage,
|
|
packageOptions,
|
|
initialStep = 'package',
|
|
initialAuthUser = null,
|
|
initialIsAuthenticated,
|
|
children,
|
|
}) => {
|
|
const [state, setState] = useState<CheckoutWizardState>(() => ({
|
|
currentStep: initialStep,
|
|
selectedPackage: initialPackage,
|
|
packageOptions,
|
|
isAuthenticated: Boolean(initialIsAuthenticated || initialAuthUser),
|
|
authUser: initialAuthUser ?? null,
|
|
paymentProvider: undefined,
|
|
isProcessing: false,
|
|
}));
|
|
|
|
const setStep = useCallback((step: CheckoutStepId) => {
|
|
setState((prev) => ({ ...prev, currentStep: step }));
|
|
}, []);
|
|
|
|
const nextStep = useCallback(() => {
|
|
setState((prev) => {
|
|
const order: CheckoutStepId[] = ['package', 'auth', 'payment', 'confirmation'];
|
|
const currentIndex = order.indexOf(prev.currentStep);
|
|
const nextIndex = currentIndex === -1 ? 0 : Math.min(order.length - 1, currentIndex + 1);
|
|
return { ...prev, currentStep: order[nextIndex] };
|
|
});
|
|
}, []);
|
|
|
|
const previousStep = useCallback(() => {
|
|
setState((prev) => {
|
|
const order: CheckoutStepId[] = ['package', 'auth', 'payment', 'confirmation'];
|
|
const currentIndex = order.indexOf(prev.currentStep);
|
|
const nextIndex = currentIndex <= 0 ? 0 : currentIndex - 1;
|
|
return { ...prev, currentStep: order[nextIndex] };
|
|
});
|
|
}, []);
|
|
|
|
const setSelectedPackage = useCallback((pkg: CheckoutPackage) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
selectedPackage: pkg,
|
|
paymentProvider: undefined,
|
|
}));
|
|
}, []);
|
|
|
|
const markAuthenticated = useCallback<CheckoutWizardContextValue['markAuthenticated']>((user) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
isAuthenticated: Boolean(user),
|
|
authUser: user ?? null,
|
|
}));
|
|
}, []);
|
|
|
|
const setPaymentProvider = useCallback<CheckoutWizardContextValue['setPaymentProvider']>((provider) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
paymentProvider: provider,
|
|
}));
|
|
}, []);
|
|
|
|
const resetPaymentState = useCallback(() => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
paymentProvider: undefined,
|
|
isProcessing: false,
|
|
}));
|
|
}, []);
|
|
|
|
const value = useMemo<CheckoutWizardContextValue>(() => ({
|
|
...state,
|
|
setStep,
|
|
nextStep,
|
|
previousStep,
|
|
setSelectedPackage,
|
|
markAuthenticated,
|
|
setPaymentProvider,
|
|
resetPaymentState,
|
|
}), [state, setStep, nextStep, previousStep, setSelectedPackage, markAuthenticated, setPaymentProvider, resetPaymentState]);
|
|
|
|
return (
|
|
<CheckoutWizardContext.Provider value={value}>
|
|
{children}
|
|
</CheckoutWizardContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useCheckoutWizard = () => {
|
|
const context = useContext(CheckoutWizardContext);
|
|
if (!context) {
|
|
throw new Error('useCheckoutWizard must be used within CheckoutWizardProvider');
|
|
}
|
|
return context;
|
|
};
|