Files
ALwrity/frontend/src/contexts/StrategyCalendarContext.tsx
2025-10-10 13:08:09 +05:30

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
};
};