Refactor site building workflow and context handling; update API response structure for improved clarity and consistency. Adjust frontend components to align with new data structure, including error handling and loading states.

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-20 21:50:16 +00:00
parent 781052c719
commit 1b4cd59e5b
24 changed files with 386 additions and 164 deletions

View File

@@ -235,8 +235,23 @@ export const useAuthStore = create<AuthState>()(
set({ user: refreshedUser, isAuthenticated: true });
} catch (error: any) {
console.warn('Failed to refresh user data:', error);
set({ user: null, token: null, refreshToken: null, isAuthenticated: false });
// Only logout on authentication/authorization errors, not on network errors
// Network errors (500, timeout, etc.) should not log the user out
const isAuthError = error?.code === 'ACCOUNT_REQUIRED' ||
error?.code === 'PLAN_REQUIRED' ||
error?.status === 401 ||
error?.status === 403 ||
(error?.message && error.message.includes('Not authenticated'));
if (isAuthError) {
// Real authentication error - logout user
console.warn('Authentication error during refresh, logging out:', error);
set({ user: null, token: null, refreshToken: null, isAuthenticated: false });
} else {
// Network/server error - don't logout, just throw the error
// The caller (AppLayout) will handle it gracefully
console.debug('Non-auth error during refresh (will retry):', error);
}
throw error;
}
},

View File

@@ -70,21 +70,38 @@ export const useBuilderWorkflowStore = create<BuilderWorkflowState>()(
set({ blueprintId, loading: true, error: null });
try {
const context = await fetchWizardContext(blueprintId);
const workflow = context.workflow;
const workflow = context?.workflow;
// If workflow is null, initialize with defaults
if (!workflow) {
set({
blueprintId,
currentStep: DEFAULT_STEP,
completedSteps: new Set<WizardStep>(),
blockingIssues: [],
workflowState: null,
context,
loading: false,
error: null,
});
return;
}
// Determine completed steps from workflow state
// Backend returns 'steps' as an array, not 'step_status' as an object
const completedSteps = new Set<WizardStep>();
Object.entries(workflow.step_status || {}).forEach(([step, status]) => {
if (status.status === 'ready' || status.status === 'complete') {
completedSteps.add(step as WizardStep);
const steps = workflow.steps || [];
steps.forEach((stepData: any) => {
if (stepData?.status === 'ready' || stepData?.status === 'complete') {
completedSteps.add(stepData.step as WizardStep);
}
});
// Extract blocking issues
const blockingIssues: Array<{ step: WizardStep; message: string }> = [];
Object.entries(workflow.step_status || {}).forEach(([step, status]) => {
if (status.status === 'blocked' && status.message) {
blockingIssues.push({ step: step as WizardStep, message: status.message });
steps.forEach((stepData: any) => {
if (stepData?.status === 'blocked' && stepData?.message) {
blockingIssues.push({ step: stepData.step as WizardStep, message: stepData.message });
}
});
@@ -133,11 +150,17 @@ export const useBuilderWorkflowStore = create<BuilderWorkflowState>()(
},
completeStep: async (step: WizardStep, metadata?: Record<string, any>) => {
const { blueprintId } = get();
const { blueprintId, workflowState } = get();
if (!blueprintId) {
throw new Error('No blueprint initialized');
}
// Ensure workflow is initialized before updating
if (!workflowState) {
// Try to initialize first
await get().initialize(blueprintId);
}
set({ loading: true, error: null });
try {
const updatedState = await updateWorkflowStep(blueprintId, step, 'ready', metadata);
@@ -161,8 +184,13 @@ export const useBuilderWorkflowStore = create<BuilderWorkflowState>()(
// Emit telemetry
get().flushTelemetry();
} catch (error: any) {
// Extract more detailed error message if available
const errorMessage = error?.response?.error ||
error?.response?.message ||
error?.message ||
`Failed to complete step: ${step}`;
set({
error: error.message || `Failed to complete step: ${step}`,
error: errorMessage,
loading: false,
});
throw error;