fix: Resolve merge conflicts in workflowStore.ts by combining normalization and fallback logic (PR #387)
This commit is contained in:
@@ -14,7 +14,8 @@ import {
|
|||||||
Pause,
|
Pause,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Schedule,
|
Schedule,
|
||||||
TrendingUp
|
TrendingUp,
|
||||||
|
CloudOff
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useWorkflowStore } from '../../../stores/workflowStore';
|
import { useWorkflowStore } from '../../../stores/workflowStore';
|
||||||
|
|
||||||
@@ -42,7 +43,9 @@ const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
|
|||||||
startWorkflow,
|
startWorkflow,
|
||||||
isWorkflowComplete,
|
isWorkflowComplete,
|
||||||
getCompletionPercentage,
|
getCompletionPercentage,
|
||||||
generateDailyWorkflow
|
generateDailyWorkflow,
|
||||||
|
isDegradedMode,
|
||||||
|
degradedModeReason
|
||||||
} = useWorkflowStore();
|
} = useWorkflowStore();
|
||||||
|
|
||||||
const completionPercentage = getCompletionPercentage();
|
const completionPercentage = getCompletionPercentage();
|
||||||
@@ -169,6 +172,30 @@ const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
|
{isDegradedMode && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
mb: 2,
|
||||||
|
p: 1.5,
|
||||||
|
borderRadius: 1,
|
||||||
|
border: `1px solid ${theme.palette.warning.main}55`,
|
||||||
|
bgcolor: `${theme.palette.warning.main}18`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloudOff sx={{ color: theme.palette.warning.light, fontSize: 18 }} />
|
||||||
|
<Typography variant="body2" sx={{ color: theme.palette.warning.light, fontWeight: 600 }}>
|
||||||
|
Degraded mode
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.75)' }}>
|
||||||
|
{degradedModeReason || 'Server workflow is unavailable; local fallback is active.'}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<Box sx={{ mb: 2 }}>
|
<Box sx={{ mb: 2 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
|
||||||
|
|||||||
@@ -311,18 +311,15 @@ class TaskWorkflowOrchestrator {
|
|||||||
date: string,
|
date: string,
|
||||||
context?: TaskGenerationContext
|
context?: TaskGenerationContext
|
||||||
): Promise<TodayTask[]> {
|
): Promise<TodayTask[]> {
|
||||||
// This is a placeholder implementation
|
|
||||||
// In Phase 3, this will be replaced with AI-powered task generation
|
|
||||||
|
|
||||||
const defaultTasks: TodayTask[] = [
|
const defaultTasks: TodayTask[] = [
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-plan-1`,
|
id: `${userId}-${date}-plan`,
|
||||||
pillarId: 'plan',
|
pillarId: 'plan',
|
||||||
title: 'Review content strategy',
|
title: 'Review today\'s plan',
|
||||||
description: 'Check and update your content strategy for the week',
|
description: 'Confirm priorities and schedule for today\'s content work.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'high',
|
priority: 'high',
|
||||||
estimatedTime: 15,
|
estimatedTime: 10,
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/content-planning-dashboard',
|
actionUrl: '/content-planning-dashboard',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -330,29 +327,14 @@ class TaskWorkflowOrchestrator {
|
|||||||
color: '#4CAF50'
|
color: '#4CAF50'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-plan-2`,
|
id: `${userId}-${date}-generate`,
|
||||||
pillarId: 'plan',
|
pillarId: 'generate',
|
||||||
title: 'Update content calendar',
|
title: 'Generate a draft',
|
||||||
description: 'Review and update your content calendar',
|
description: 'Create one content draft using the content writer.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'medium',
|
priority: 'medium',
|
||||||
estimatedTime: 10,
|
estimatedTime: 20,
|
||||||
dependencies: [`${userId}-${date}-plan-1`],
|
dependencies: [`${userId}-${date}-plan`],
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/content-planning-dashboard',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'CalendarMonth',
|
|
||||||
color: '#4CAF50'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-generate-1`,
|
|
||||||
pillarId: 'generate',
|
|
||||||
title: 'Create social media content',
|
|
||||||
description: 'Generate content for your social media platforms',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'high',
|
|
||||||
estimatedTime: 30,
|
|
||||||
dependencies: [`${userId}-${date}-plan-1`, `${userId}-${date}-plan-2`],
|
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/facebook-writer',
|
actionUrl: '/facebook-writer',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -360,29 +342,14 @@ class TaskWorkflowOrchestrator {
|
|||||||
color: '#2196F3'
|
color: '#2196F3'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-generate-2`,
|
id: `${userId}-${date}-publish`,
|
||||||
pillarId: 'generate',
|
|
||||||
title: 'Create blog content',
|
|
||||||
description: 'Write blog posts for your website',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'medium',
|
|
||||||
estimatedTime: 45,
|
|
||||||
dependencies: [`${userId}-${date}-plan-1`],
|
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/blog-writer',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'Article',
|
|
||||||
color: '#2196F3'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-publish-1`,
|
|
||||||
pillarId: 'publish',
|
pillarId: 'publish',
|
||||||
title: 'Publish social media content',
|
title: 'Publish approved content',
|
||||||
description: 'Publish your created content to social media',
|
description: 'Open publishing tools and publish today\'s approved draft.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'medium',
|
priority: 'high',
|
||||||
estimatedTime: 10,
|
estimatedTime: 10,
|
||||||
dependencies: [`${userId}-${date}-generate-1`],
|
dependencies: [`${userId}-${date}-generate`],
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/facebook-writer',
|
actionUrl: '/facebook-writer',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -390,29 +357,14 @@ class TaskWorkflowOrchestrator {
|
|||||||
color: '#FF9800'
|
color: '#FF9800'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-publish-2`,
|
id: `${userId}-${date}-analyze`,
|
||||||
pillarId: 'publish',
|
pillarId: 'analyze',
|
||||||
title: 'Publish blog content',
|
title: 'Check performance snapshot',
|
||||||
description: 'Publish blog posts to your website',
|
description: 'Review key analytics to assess today\'s published content.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'medium',
|
priority: 'medium',
|
||||||
estimatedTime: 15,
|
estimatedTime: 10,
|
||||||
dependencies: [`${userId}-${date}-generate-2`],
|
dependencies: [`${userId}-${date}-publish`],
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/blog-writer',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'Publish',
|
|
||||||
color: '#FF9800'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-analyze-1`,
|
|
||||||
pillarId: 'analyze',
|
|
||||||
title: 'Review content performance',
|
|
||||||
description: 'Analyze performance of published content',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'low',
|
|
||||||
estimatedTime: 20,
|
|
||||||
dependencies: [`${userId}-${date}-publish-1`, `${userId}-${date}-publish-2`],
|
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/analytics-dashboard',
|
actionUrl: '/analytics-dashboard',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -420,95 +372,50 @@ class TaskWorkflowOrchestrator {
|
|||||||
color: '#9C27B0'
|
color: '#9C27B0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-engage-1`,
|
id: `${userId}-${date}-engage`,
|
||||||
pillarId: 'engage',
|
pillarId: 'engage',
|
||||||
title: 'Respond to comments',
|
title: 'Respond to audience activity',
|
||||||
description: 'Engage with comments on your content',
|
description: 'Reply to new comments or mentions from today\'s posts.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'low',
|
priority: 'low',
|
||||||
estimatedTime: 15,
|
estimatedTime: 10,
|
||||||
dependencies: [`${userId}-${date}-publish-1`],
|
dependencies: [`${userId}-${date}-publish`],
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/engagement-dashboard',
|
actionUrl: '/engagement-dashboard',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
icon: 'ChatBubbleOutline',
|
icon: 'ChatBubbleOutline',
|
||||||
color: '#E91E63'
|
color: '#E91E63'
|
||||||
},
|
},
|
||||||
// Engage pillar tasks
|
|
||||||
{
|
{
|
||||||
id: `${userId}-${date}-engage-1`,
|
id: `${userId}-${date}-remarket`,
|
||||||
pillarId: 'engage',
|
|
||||||
title: 'Reply to blog comment',
|
|
||||||
description: 'Respond to comments on your latest blog post',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'high',
|
|
||||||
estimatedTime: 10,
|
|
||||||
dependencies: [`${userId}-${date}-analyze-1`],
|
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/engagement-dashboard',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'Comment',
|
|
||||||
color: '#E91E63'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-engage-2`,
|
|
||||||
pillarId: 'engage',
|
|
||||||
title: 'Respond to Twitter mention',
|
|
||||||
description: 'Reply to Twitter mentions and engage with followers',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'medium',
|
|
||||||
estimatedTime: 5,
|
|
||||||
dependencies: [`${userId}-${date}-engage-1`],
|
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/engagement-dashboard',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'Twitter',
|
|
||||||
color: '#1DA1F2'
|
|
||||||
},
|
|
||||||
// Remarket pillar tasks
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-remarket-1`,
|
|
||||||
pillarId: 'remarket',
|
pillarId: 'remarket',
|
||||||
title: 'Launch Retargeting Campaign',
|
title: 'Prepare remarketing audience',
|
||||||
description: 'Create and launch targeted remarketing campaigns',
|
description: 'Open remarketing tools to refresh your retargeting audience.',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
priority: 'high',
|
priority: 'low',
|
||||||
estimatedTime: 35,
|
estimatedTime: 15,
|
||||||
dependencies: [`${userId}-${date}-engage-2`],
|
dependencies: [`${userId}-${date}-analyze`],
|
||||||
actionType: 'navigate',
|
actionType: 'navigate',
|
||||||
actionUrl: '/remarketing-dashboard',
|
actionUrl: '/remarketing-dashboard',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
icon: 'Psychology',
|
icon: 'Psychology',
|
||||||
color: '#00695C'
|
color: '#00695C'
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${userId}-${date}-remarket-2`,
|
|
||||||
pillarId: 'remarket',
|
|
||||||
title: 'Lead Nurturing Sequence',
|
|
||||||
description: 'Set up automated lead nurturing workflows',
|
|
||||||
status: 'pending',
|
|
||||||
priority: 'medium',
|
|
||||||
estimatedTime: 30,
|
|
||||||
dependencies: [`${userId}-${date}-remarket-1`],
|
|
||||||
actionType: 'navigate',
|
|
||||||
actionUrl: '/lead-nurturing',
|
|
||||||
enabled: true,
|
|
||||||
icon: 'Refresh',
|
|
||||||
color: '#4CAF50'
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const uniqueTasks = this.ensureUniqueTaskIds(defaultTasks);
|
||||||
|
|
||||||
// Validate dependencies and get optimal execution order
|
// Validate dependencies and get optimal execution order
|
||||||
const tempWorkflow: DailyWorkflow = {
|
const tempWorkflow: DailyWorkflow = {
|
||||||
id: `${userId}-${date}`,
|
id: `${userId}-${date}`,
|
||||||
date,
|
date,
|
||||||
userId,
|
userId,
|
||||||
tasks: defaultTasks,
|
tasks: uniqueTasks,
|
||||||
currentTaskIndex: 0,
|
currentTaskIndex: 0,
|
||||||
completedTasks: 0,
|
completedTasks: 0,
|
||||||
totalTasks: defaultTasks.length,
|
totalTasks: uniqueTasks.length,
|
||||||
workflowStatus: 'not_started',
|
workflowStatus: 'not_started',
|
||||||
totalEstimatedTime: defaultTasks.reduce((sum, task) => sum + task.estimatedTime, 0),
|
totalEstimatedTime: uniqueTasks.reduce((sum, task) => sum + task.estimatedTime, 0),
|
||||||
actualTimeSpent: 0
|
actualTimeSpent: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -517,13 +424,46 @@ class TaskWorkflowOrchestrator {
|
|||||||
if (!validation.isValid) {
|
if (!validation.isValid) {
|
||||||
console.warn('Dependency validation failed:', validation.errors);
|
console.warn('Dependency validation failed:', validation.errors);
|
||||||
// Return tasks without dependencies if validation fails
|
// Return tasks without dependencies if validation fails
|
||||||
return defaultTasks.map(task => ({ ...task, dependencies: [] }));
|
return uniqueTasks.map(task => ({ ...task, dependencies: [] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get optimal execution order
|
// Get optimal execution order
|
||||||
const orderedTasks = taskDependencyManager.getOptimalExecutionOrder(tempWorkflow);
|
const orderedTasks = taskDependencyManager.getOptimalExecutionOrder(tempWorkflow);
|
||||||
|
|
||||||
return orderedTasks;
|
return this.ensureUniqueTaskIds(orderedTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ensureUniqueTaskIds(tasks: TodayTask[]): TodayTask[] {
|
||||||
|
const idOccurrences = new Map<string, number>();
|
||||||
|
const oldToNew = new Map<string, string>();
|
||||||
|
|
||||||
|
const withUniqueIds = tasks.map(task => {
|
||||||
|
const count = idOccurrences.get(task.id) ?? 0;
|
||||||
|
idOccurrences.set(task.id, count + 1);
|
||||||
|
|
||||||
|
if (count === 0) {
|
||||||
|
oldToNew.set(task.id, task.id);
|
||||||
|
return { ...task };
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueId = `${task.id}-${count + 1}`;
|
||||||
|
oldToNew.set(`${task.id}#${count + 1}`, uniqueId);
|
||||||
|
return { ...task, id: uniqueId };
|
||||||
|
});
|
||||||
|
|
||||||
|
const allTaskIds = new Set(withUniqueIds.map(task => task.id));
|
||||||
|
|
||||||
|
return withUniqueIds.map(task => {
|
||||||
|
const dependencies = (task.dependencies ?? [])
|
||||||
|
.map(dep => oldToNew.get(dep) || dep)
|
||||||
|
.filter((dep, index, arr) => arr.indexOf(dep) === index)
|
||||||
|
.filter(dep => allTaskIds.has(dep));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...task,
|
||||||
|
dependencies: dependencies.length > 0 ? dependencies : undefined
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
WorkflowError
|
WorkflowError
|
||||||
} from '../types/workflow';
|
} from '../types/workflow';
|
||||||
import { taskWorkflowOrchestrator } from '../services/TaskWorkflowOrchestrator';
|
import { taskWorkflowOrchestrator } from '../services/TaskWorkflowOrchestrator';
|
||||||
import { apiClient } from '../api/client';
|
import { apiClient, ConnectionError, NetworkError } from '../api/client';
|
||||||
|
|
||||||
const isServerWorkflowId = (workflowId: string) => workflowId.startsWith('daily-');
|
const isServerWorkflowId = (workflowId: string) => workflowId.startsWith('daily-');
|
||||||
|
|
||||||
@@ -47,6 +47,21 @@ const normalizeServerWorkflow = (workflow: DailyWorkflow): DailyWorkflow => ({
|
|||||||
: [],
|
: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isServerUnavailableError = (error: unknown): boolean =>
|
||||||
|
error instanceof ConnectionError || error instanceof NetworkError;
|
||||||
|
|
||||||
|
const toWorkflowError = (error: unknown, fallbackMessage: string): WorkflowError => {
|
||||||
|
if (error instanceof WorkflowError) return error;
|
||||||
|
|
||||||
|
const message = error instanceof Error ? error.message : fallbackMessage;
|
||||||
|
return {
|
||||||
|
code: 'WORKFLOW_ERROR',
|
||||||
|
message,
|
||||||
|
timestamp: new Date(),
|
||||||
|
recoverable: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const computeProgressAndNavigation = (workflow: DailyWorkflow): { progress: WorkflowProgress; navigation: NavigationState } => {
|
const computeProgressAndNavigation = (workflow: DailyWorkflow): { progress: WorkflowProgress; navigation: NavigationState } => {
|
||||||
const tasks = Array.isArray(workflow.tasks) ? workflow.tasks : [];
|
const tasks = Array.isArray(workflow.tasks) ? workflow.tasks : [];
|
||||||
const totalTasks = tasks.length;
|
const totalTasks = tasks.length;
|
||||||
@@ -102,6 +117,8 @@ interface WorkflowState {
|
|||||||
isWorkflowModalOpen: boolean;
|
isWorkflowModalOpen: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
error: WorkflowError | null;
|
error: WorkflowError | null;
|
||||||
|
isDegradedMode: boolean;
|
||||||
|
degradedModeReason: string | null;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
generateDailyWorkflow: (userId: string, date?: string) => Promise<void>;
|
generateDailyWorkflow: (userId: string, date?: string) => Promise<void>;
|
||||||
@@ -141,37 +158,67 @@ export const useWorkflowStore = create<WorkflowState>()(
|
|||||||
isWorkflowModalOpen: false,
|
isWorkflowModalOpen: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
isDegradedMode: false,
|
||||||
|
degradedModeReason: null,
|
||||||
|
|
||||||
// Generate daily workflow
|
// Generate daily workflow
|
||||||
generateDailyWorkflow: async (userId: string, date?: string) => {
|
generateDailyWorkflow: async (userId: string, date?: string) => {
|
||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
const resp = await apiClient.get('/api/today-workflow', { params: date ? { date } : {} });
|
|
||||||
const serverWorkflow = resp?.data?.data?.workflow as DailyWorkflow | undefined;
|
|
||||||
if (serverWorkflow && Array.isArray(serverWorkflow.tasks)) {
|
|
||||||
const normalizedWorkflow = normalizeServerWorkflow(serverWorkflow);
|
|
||||||
const derived = computeProgressAndNavigation(normalizedWorkflow);
|
|
||||||
set({
|
|
||||||
currentWorkflow: normalizedWorkflow,
|
|
||||||
workflowProgress: derived.progress,
|
|
||||||
navigationState: derived.navigation,
|
|
||||||
isLoading: false
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await apiClient.get('/api/today-workflow', { params: date ? { date } : {} });
|
||||||
|
const serverWorkflow = resp?.data?.data?.workflow as DailyWorkflow | undefined;
|
||||||
|
|
||||||
|
if (!serverWorkflow || !Array.isArray(serverWorkflow.tasks)) {
|
||||||
|
throw new WorkflowError({
|
||||||
|
code: 'WORKFLOW_SCHEMA_INVALID',
|
||||||
|
message: 'Server workflow response is missing a valid tasks array.',
|
||||||
|
timestamp: new Date(),
|
||||||
|
recoverable: false,
|
||||||
|
suggestedAction: 'Refresh and try again. If this persists, contact support.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedWorkflow = normalizeServerWorkflow(serverWorkflow);
|
||||||
|
const derived = computeProgressAndNavigation(normalizedWorkflow);
|
||||||
|
set({
|
||||||
|
currentWorkflow: normalizedWorkflow,
|
||||||
|
workflowProgress: derived.progress,
|
||||||
|
navigationState: derived.navigation,
|
||||||
|
isLoading: false,
|
||||||
|
isDegradedMode: false,
|
||||||
|
degradedModeReason: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
if (!isServerUnavailableError(error)) {
|
||||||
|
set({
|
||||||
|
error: toWorkflowError(error, 'Failed to load workflow from server.'),
|
||||||
|
isLoading: false,
|
||||||
|
isDegradedMode: false,
|
||||||
|
degradedModeReason: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const workflow = await taskWorkflowOrchestrator.generateDailyWorkflow(userId, date);
|
const workflow = await taskWorkflowOrchestrator.generateDailyWorkflow(userId, date);
|
||||||
const progress = taskWorkflowOrchestrator.getWorkflowProgress(workflow.id);
|
const progress = taskWorkflowOrchestrator.getWorkflowProgress(workflow.id);
|
||||||
const navigation = taskWorkflowOrchestrator.getNavigationState(workflow.id);
|
const navigation = taskWorkflowOrchestrator.getNavigationState(workflow.id);
|
||||||
set({ currentWorkflow: workflow, workflowProgress: progress, navigationState: navigation, isLoading: false });
|
set({
|
||||||
|
currentWorkflow: workflow,
|
||||||
|
workflowProgress: progress,
|
||||||
|
navigationState: navigation,
|
||||||
|
isLoading: false,
|
||||||
|
isDegradedMode: true,
|
||||||
|
degradedModeReason: 'Server workflow unavailable. Using local fallback workflow.',
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const workflowError = error as WorkflowError;
|
set({
|
||||||
set({
|
error: toWorkflowError(error, 'Failed to generate local fallback workflow.'),
|
||||||
error: workflowError,
|
isLoading: false,
|
||||||
isLoading: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user