354 lines
10 KiB
TypeScript
354 lines
10 KiB
TypeScript
import React, { createContext, useContext, useReducer, useEffect, useCallback, ReactNode } from 'react';
|
|
import { StrategyContext, CalendarContext, WorkflowProgress } from '../services/navigationOrchestrator';
|
|
|
|
// Context state interface
|
|
interface StrategyCalendarState {
|
|
activeStrategy: any | null;
|
|
strategyContext: StrategyContext | null;
|
|
calendarContext: CalendarContext | null;
|
|
workflowProgress: WorkflowProgress;
|
|
isContextValid: boolean;
|
|
lastUpdated: string | null;
|
|
error: string | null;
|
|
}
|
|
|
|
// Action types
|
|
type StrategyCalendarAction =
|
|
| { type: 'SET_ACTIVE_STRATEGY'; payload: any }
|
|
| { type: 'SET_STRATEGY_CONTEXT'; payload: StrategyContext }
|
|
| { type: 'SET_CALENDAR_CONTEXT'; payload: CalendarContext }
|
|
| { type: 'UPDATE_WORKFLOW_PROGRESS'; payload: Partial<WorkflowProgress> }
|
|
| { type: 'CLEAR_CONTEXT' }
|
|
| { type: 'SET_ERROR'; payload: string }
|
|
| { type: 'CLEAR_ERROR' }
|
|
| { type: 'VALIDATE_CONTEXT' };
|
|
|
|
// Initial state
|
|
const initialState: StrategyCalendarState = {
|
|
activeStrategy: null,
|
|
strategyContext: null,
|
|
calendarContext: null,
|
|
workflowProgress: {
|
|
currentStep: 'strategy',
|
|
completedSteps: [],
|
|
totalSteps: 4,
|
|
progressPercentage: 0
|
|
},
|
|
isContextValid: false,
|
|
lastUpdated: null,
|
|
error: null
|
|
};
|
|
|
|
// Reducer function
|
|
function strategyCalendarReducer(state: StrategyCalendarState, action: StrategyCalendarAction): StrategyCalendarState {
|
|
switch (action.type) {
|
|
case 'SET_ACTIVE_STRATEGY':
|
|
return {
|
|
...state,
|
|
activeStrategy: action.payload,
|
|
lastUpdated: new Date().toISOString(),
|
|
error: null
|
|
};
|
|
|
|
case 'SET_STRATEGY_CONTEXT':
|
|
return {
|
|
...state,
|
|
strategyContext: action.payload,
|
|
lastUpdated: new Date().toISOString(),
|
|
error: null
|
|
};
|
|
|
|
case 'SET_CALENDAR_CONTEXT':
|
|
return {
|
|
...state,
|
|
calendarContext: action.payload,
|
|
lastUpdated: new Date().toISOString(),
|
|
error: null
|
|
};
|
|
|
|
case 'UPDATE_WORKFLOW_PROGRESS':
|
|
return {
|
|
...state,
|
|
workflowProgress: {
|
|
...state.workflowProgress,
|
|
...action.payload
|
|
},
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
|
|
case 'CLEAR_CONTEXT':
|
|
return {
|
|
...initialState,
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
|
|
case 'SET_ERROR':
|
|
return {
|
|
...state,
|
|
error: action.payload,
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
|
|
case 'CLEAR_ERROR':
|
|
return {
|
|
...state,
|
|
error: null
|
|
};
|
|
|
|
case 'VALIDATE_CONTEXT':
|
|
const isValid = validateContextIntegrity(state);
|
|
return {
|
|
...state,
|
|
isContextValid: isValid,
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
|
|
default:
|
|
return state;
|
|
}
|
|
}
|
|
|
|
// Context validation function
|
|
function validateContextIntegrity(state: StrategyCalendarState): boolean {
|
|
try {
|
|
// Check if strategy context exists and has required fields
|
|
if (state.strategyContext) {
|
|
const { strategyId, strategyData, activationStatus } = state.strategyContext;
|
|
if (!strategyId || !strategyData || !activationStatus) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if calendar context is valid when it exists
|
|
if (state.calendarContext) {
|
|
const { strategyContext } = state.calendarContext;
|
|
if (strategyContext && !validateContextIntegrity({ ...state, strategyContext })) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check workflow progress validity
|
|
const { currentStep, totalSteps, progressPercentage } = state.workflowProgress;
|
|
if (!currentStep || totalSteps <= 0 || progressPercentage < 0 || progressPercentage > 100) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Context validation error:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Context interface
|
|
interface StrategyCalendarContextType {
|
|
state: StrategyCalendarState;
|
|
dispatch: React.Dispatch<StrategyCalendarAction>;
|
|
|
|
// Convenience methods
|
|
setActiveStrategy: (strategy: any) => void;
|
|
setStrategyContext: (context: StrategyContext) => void;
|
|
setCalendarContext: (context: CalendarContext) => void;
|
|
updateWorkflowProgress: (progress: Partial<WorkflowProgress>) => void;
|
|
clearContext: () => void;
|
|
setError: (error: string) => void;
|
|
clearError: () => void;
|
|
validateContext: () => void;
|
|
|
|
// Utility methods
|
|
getStrategyData: () => any | null;
|
|
getCalendarData: () => any | null;
|
|
isStrategyActive: () => boolean;
|
|
isCalendarReady: () => boolean;
|
|
getWorkflowStep: () => string;
|
|
getProgressPercentage: () => number;
|
|
isFromStrategyActivation: () => boolean;
|
|
}
|
|
|
|
// Create context
|
|
const StrategyCalendarContext = createContext<StrategyCalendarContextType | undefined>(undefined);
|
|
|
|
// Provider component
|
|
interface StrategyCalendarProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export const StrategyCalendarProvider: React.FC<StrategyCalendarProviderProps> = ({ children }) => {
|
|
const [state, dispatch] = useReducer(strategyCalendarReducer, initialState);
|
|
|
|
// Validate context on state changes
|
|
useEffect(() => {
|
|
dispatch({ type: 'VALIDATE_CONTEXT' });
|
|
}, [state.strategyContext, state.calendarContext, state.workflowProgress]);
|
|
|
|
// Persist context to session storage
|
|
useEffect(() => {
|
|
if (state.lastUpdated) {
|
|
try {
|
|
sessionStorage.setItem('strategyCalendarContext', JSON.stringify({
|
|
strategyContext: state.strategyContext,
|
|
calendarContext: state.calendarContext,
|
|
workflowProgress: state.workflowProgress,
|
|
lastUpdated: state.lastUpdated
|
|
}));
|
|
} catch (error) {
|
|
console.error('Failed to persist context to session storage:', error);
|
|
}
|
|
}
|
|
}, [state.strategyContext, state.calendarContext, state.workflowProgress, state.lastUpdated]);
|
|
|
|
// Restore context from session storage on mount
|
|
useEffect(() => {
|
|
try {
|
|
const persisted = sessionStorage.getItem('strategyCalendarContext');
|
|
if (persisted) {
|
|
const data = JSON.parse(persisted);
|
|
const lastUpdated = new Date(data.lastUpdated);
|
|
const now = new Date();
|
|
|
|
// Check if context is still valid (not older than 30 minutes)
|
|
if (now.getTime() - lastUpdated.getTime() < 30 * 60 * 1000) {
|
|
if (data.strategyContext) {
|
|
dispatch({ type: 'SET_STRATEGY_CONTEXT', payload: data.strategyContext });
|
|
}
|
|
if (data.calendarContext) {
|
|
dispatch({ type: 'SET_CALENDAR_CONTEXT', payload: data.calendarContext });
|
|
}
|
|
if (data.workflowProgress) {
|
|
dispatch({ type: 'UPDATE_WORKFLOW_PROGRESS', payload: data.workflowProgress });
|
|
}
|
|
} else {
|
|
// Clear expired context
|
|
sessionStorage.removeItem('strategyCalendarContext');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to restore context from session storage:', error);
|
|
}
|
|
}, []);
|
|
|
|
// Convenience methods
|
|
const setActiveStrategy = (strategy: any) => {
|
|
dispatch({ type: 'SET_ACTIVE_STRATEGY', payload: strategy });
|
|
};
|
|
|
|
const setStrategyContext = (context: StrategyContext) => {
|
|
dispatch({ type: 'SET_STRATEGY_CONTEXT', payload: context });
|
|
};
|
|
|
|
const setCalendarContext = (context: CalendarContext) => {
|
|
dispatch({ type: 'SET_CALENDAR_CONTEXT', payload: context });
|
|
};
|
|
|
|
const updateWorkflowProgress = (progress: Partial<WorkflowProgress>) => {
|
|
dispatch({ type: 'UPDATE_WORKFLOW_PROGRESS', payload: progress });
|
|
};
|
|
|
|
const clearContext = () => {
|
|
dispatch({ type: 'CLEAR_CONTEXT' });
|
|
sessionStorage.removeItem('strategyCalendarContext');
|
|
};
|
|
|
|
const setError = (error: string) => {
|
|
dispatch({ type: 'SET_ERROR', payload: error });
|
|
};
|
|
|
|
const clearError = () => {
|
|
dispatch({ type: 'CLEAR_ERROR' });
|
|
};
|
|
|
|
const validateContext = () => {
|
|
dispatch({ type: 'VALIDATE_CONTEXT' });
|
|
};
|
|
|
|
// Utility methods
|
|
const getStrategyData = () => state.strategyContext?.strategyData || null;
|
|
|
|
const getCalendarData = () => state.calendarContext?.autoPopulatedData || null;
|
|
|
|
const isStrategyActive = () => state.strategyContext?.activationStatus === 'active';
|
|
|
|
const isCalendarReady = () => {
|
|
return state.calendarContext !== null &&
|
|
state.calendarContext.autoPopulatedData !== null &&
|
|
state.isContextValid;
|
|
};
|
|
|
|
const getWorkflowStep = () => state.workflowProgress.currentStep;
|
|
|
|
const getProgressPercentage = () => state.workflowProgress.progressPercentage;
|
|
|
|
const isFromStrategyActivation = useCallback(() => {
|
|
// Check if we have a preserved strategy context from navigation
|
|
const result = state.strategyContext?.activationStatus === 'active' &&
|
|
state.strategyContext?.activationTimestamp !== null;
|
|
return result;
|
|
}, [state.strategyContext?.activationStatus, state.strategyContext?.activationTimestamp]);
|
|
|
|
const contextValue: StrategyCalendarContextType = {
|
|
state,
|
|
dispatch,
|
|
setActiveStrategy,
|
|
setStrategyContext,
|
|
setCalendarContext,
|
|
updateWorkflowProgress,
|
|
clearContext,
|
|
setError,
|
|
clearError,
|
|
validateContext,
|
|
getStrategyData,
|
|
getCalendarData,
|
|
isStrategyActive,
|
|
isCalendarReady,
|
|
getWorkflowStep,
|
|
getProgressPercentage,
|
|
isFromStrategyActivation
|
|
};
|
|
|
|
return (
|
|
<StrategyCalendarContext.Provider value={contextValue}>
|
|
{children}
|
|
</StrategyCalendarContext.Provider>
|
|
);
|
|
};
|
|
|
|
// Custom hook to use the context
|
|
export const useStrategyCalendarContext = (): StrategyCalendarContextType => {
|
|
const context = useContext(StrategyCalendarContext);
|
|
if (context === undefined) {
|
|
throw new Error('useStrategyCalendarContext must be used within a StrategyCalendarProvider');
|
|
}
|
|
return context;
|
|
};
|
|
|
|
// Hook for components that only need read access
|
|
export const useStrategyCalendarState = () => {
|
|
const { state } = useStrategyCalendarContext();
|
|
return state;
|
|
};
|
|
|
|
// Hook for components that need to dispatch actions
|
|
export const useStrategyCalendarActions = () => {
|
|
const {
|
|
setActiveStrategy,
|
|
setStrategyContext,
|
|
setCalendarContext,
|
|
updateWorkflowProgress,
|
|
clearContext,
|
|
setError,
|
|
clearError,
|
|
validateContext
|
|
} = useStrategyCalendarContext();
|
|
|
|
return {
|
|
setActiveStrategy,
|
|
setStrategyContext,
|
|
setCalendarContext,
|
|
updateWorkflowProgress,
|
|
clearContext,
|
|
setError,
|
|
clearError,
|
|
validateContext
|
|
};
|
|
};
|