130 lines
3.2 KiB
TypeScript
130 lines
3.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Data persistence and validation for onboarding
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { useState, useCallback } from 'react';
|
||
|
|
import { getStepById } from './steps';
|
||
|
|
import type { OnboardingData, OnboardingError, StepValidator } from './types';
|
||
|
|
|
||
|
|
interface OnboardingDataState {
|
||
|
|
data: OnboardingData;
|
||
|
|
error: OnboardingError | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface OnboardingDataActions {
|
||
|
|
updateStepData: (stepId: string, stepData: Partial<OnboardingData>) => void;
|
||
|
|
validateStep: (stepId: string) => string | null;
|
||
|
|
clearError: () => void;
|
||
|
|
resetData: () => void;
|
||
|
|
getStepData: (stepId: string) => any;
|
||
|
|
setAllStepData: (allData: { [stepId: string]: any }) => void;
|
||
|
|
getAllStepData: () => { [stepId: string]: any };
|
||
|
|
}
|
||
|
|
|
||
|
|
export const useOnboardingData = () => {
|
||
|
|
const [state, setState] = useState<OnboardingDataState>({
|
||
|
|
data: {
|
||
|
|
allStepData: {},
|
||
|
|
},
|
||
|
|
error: null,
|
||
|
|
});
|
||
|
|
|
||
|
|
const updateStepData = useCallback((stepId: string, stepData: Partial<OnboardingData>) => {
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
data: {
|
||
|
|
...prev.data,
|
||
|
|
...stepData,
|
||
|
|
// Also store in allStepData for cross-step access
|
||
|
|
allStepData: {
|
||
|
|
...prev.data.allStepData,
|
||
|
|
[stepId]: {
|
||
|
|
...prev.data.allStepData?.[stepId],
|
||
|
|
...stepData,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
error: null, // Clear error when data is updated successfully
|
||
|
|
}));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const validateStep = useCallback((stepId: string): string | null => {
|
||
|
|
const step = getStepById(stepId);
|
||
|
|
if (step?.validation) {
|
||
|
|
try {
|
||
|
|
const validationResult = step.validation(state.data);
|
||
|
|
if (validationResult) {
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
error: {
|
||
|
|
step: stepId,
|
||
|
|
message: validationResult,
|
||
|
|
},
|
||
|
|
}));
|
||
|
|
return validationResult;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
const errorMessage = error instanceof Error ? error.message : 'Validation error';
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
error: {
|
||
|
|
step: stepId,
|
||
|
|
message: errorMessage,
|
||
|
|
details: error,
|
||
|
|
},
|
||
|
|
}));
|
||
|
|
return errorMessage;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}, [state.data]);
|
||
|
|
|
||
|
|
const clearError = useCallback(() => {
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
error: null,
|
||
|
|
}));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const resetData = useCallback(() => {
|
||
|
|
setState({
|
||
|
|
data: {
|
||
|
|
allStepData: {},
|
||
|
|
},
|
||
|
|
error: null,
|
||
|
|
});
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const getStepData = useCallback((stepId: string): any => {
|
||
|
|
return state.data.allStepData?.[stepId] || {};
|
||
|
|
}, [state.data.allStepData]);
|
||
|
|
|
||
|
|
const setAllStepData = useCallback((allData: { [stepId: string]: any }) => {
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
data: {
|
||
|
|
...prev.data,
|
||
|
|
allStepData: allData,
|
||
|
|
},
|
||
|
|
}));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const getAllStepData = useCallback((): { [stepId: string]: any } => {
|
||
|
|
return state.data.allStepData || {};
|
||
|
|
}, [state.data.allStepData]);
|
||
|
|
|
||
|
|
return {
|
||
|
|
// State
|
||
|
|
data: state.data,
|
||
|
|
error: state.error,
|
||
|
|
|
||
|
|
// Actions
|
||
|
|
updateStepData,
|
||
|
|
validateStep,
|
||
|
|
clearError,
|
||
|
|
resetData,
|
||
|
|
getStepData,
|
||
|
|
setAllStepData,
|
||
|
|
getAllStepData,
|
||
|
|
} satisfies OnboardingDataState & OnboardingDataActions;
|
||
|
|
};
|