From f1a3504b72c580816fda8f4b9c1d1159be2f2ebb Mon Sep 17 00:00:00 2001 From: alorig <220087330+alorig@users.noreply.github.com> Date: Tue, 18 Nov 2025 21:38:52 +0500 Subject: [PATCH] 12 --- frontend/src/pages/Sites/Builder/Wizard.tsx | 242 +++++++++++++++++++- frontend/src/store/builderStore.ts | 6 + 2 files changed, 247 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/Sites/Builder/Wizard.tsx b/frontend/src/pages/Sites/Builder/Wizard.tsx index 97f6c7bc..f16a7d47 100644 --- a/frontend/src/pages/Sites/Builder/Wizard.tsx +++ b/frontend/src/pages/Sites/Builder/Wizard.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState, useCallback, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { Card, @@ -22,6 +22,9 @@ import { BusinessDetailsStep } from "./steps/BusinessDetailsStep"; import { BriefStep } from "./steps/BriefStep"; import { ObjectivesStep } from "./steps/ObjectivesStep"; import { StyleStep } from "./steps/StyleStep"; +import { useProgressModal } from "../../../hooks/useProgressModal"; +import { useResourceDebug } from "../../../hooks/useResourceDebug"; +import ProgressModal from "../../../components/common/ProgressModal"; export default function SiteBuilderWizard() { const navigate = useNavigate(); @@ -52,6 +55,41 @@ export default function SiteBuilderWizard() { loadMetadata, } = useBuilderStore(); + // Progress modal for AI functions + const progressModal = useProgressModal(); + + // Resource Debug toggle - controls AI Function Logs + const resourceDebugEnabled = useResourceDebug(); + + // AI Function Logs state + const [aiLogs, setAiLogs] = useState>([]); + + // Track last logged step to avoid duplicates + const lastLoggedStepRef = useRef(null); + const lastLoggedPercentageRef = useRef(-1); + const hasReloadedRef = useRef(false); + + // Helper function to add log entry (only if Resource Debug is enabled) + const addAiLog = useCallback((log: { + timestamp: string; + type: 'request' | 'success' | 'error' | 'step'; + action: string; + data: any; + stepName?: string; + percentage?: number; + }) => { + if (resourceDebugEnabled) { + setAiLogs(prev => [...prev, log]); + } + }, [resourceDebugEnabled]); + useEffect(() => { syncContextFromStores(); }, [activeSite?.id, activeSite?.name]); @@ -60,6 +98,122 @@ export default function SiteBuilderWizard() { loadMetadata(); }, [loadMetadata]); + // Track structure generation task and open progress modal + const structureTaskId = useBuilderStore((state) => state.structureTaskId); + useEffect(() => { + if (structureTaskId) { + // Log initial request + addAiLog({ + timestamp: new Date().toISOString(), + type: 'request', + action: 'Generate Site Structure', + data: { + taskId: structureTaskId, + blueprintId: activeBlueprint?.id, + message: 'Structure generation task queued', + }, + }); + + progressModal.openModal(structureTaskId, 'Generating Site Structure', 'ai-generate-site-structure-01-desktop'); + } + }, [structureTaskId, progressModal, addAiLog, activeBlueprint?.id]); + + // Log AI function progress steps + useEffect(() => { + if (!progressModal.taskId || !progressModal.isOpen) { + return; + } + + const progress = progressModal.progress; + const currentStep = progress.details?.phase || ''; + const currentPercentage = progress.percentage; + const currentMessage = progress.message; + const currentStatus = progress.status; + + // Log step changes + if (currentStep && currentStep !== lastLoggedStepRef.current) { + const stepType = currentStatus === 'error' ? 'error' : + currentStatus === 'completed' ? 'success' : 'step'; + + addAiLog({ + timestamp: new Date().toISOString(), + type: stepType, + action: progressModal.title || 'Generate Site Structure', + stepName: currentStep, + percentage: currentPercentage, + data: { + step: currentStep, + message: currentMessage, + percentage: currentPercentage, + status: currentStatus, + details: progress.details, + }, + }); + + lastLoggedStepRef.current = currentStep; + lastLoggedPercentageRef.current = currentPercentage; + } + // Log percentage changes for same step (if significant change) + else if (currentStep && Math.abs(currentPercentage - lastLoggedPercentageRef.current) >= 10) { + const stepType = currentStatus === 'error' ? 'error' : + currentStatus === 'completed' ? 'success' : 'step'; + + addAiLog({ + timestamp: new Date().toISOString(), + type: stepType, + action: progressModal.title || 'Generate Site Structure', + stepName: currentStep, + percentage: currentPercentage, + data: { + step: currentStep, + message: currentMessage, + percentage: currentPercentage, + status: currentStatus, + details: progress.details, + }, + }); + + lastLoggedPercentageRef.current = currentPercentage; + } + // Log status changes (error, completed) + else if (currentStatus === 'error' || currentStatus === 'completed') { + // Only log if we haven't already logged this status for this step + if (currentStep !== lastLoggedStepRef.current || + (currentStatus === 'error' && lastLoggedStepRef.current !== 'error') || + (currentStatus === 'completed' && lastLoggedStepRef.current !== 'completed')) { + const stepType = currentStatus === 'error' ? 'error' : 'success'; + + addAiLog({ + timestamp: new Date().toISOString(), + type: stepType, + action: progressModal.title || 'Generate Site Structure', + stepName: currentStep || 'Final', + percentage: currentPercentage, + data: { + step: currentStep || 'Final', + message: currentMessage, + percentage: currentPercentage, + status: currentStatus, + details: progress.details, + }, + }); + + lastLoggedStepRef.current = currentStep || currentStatus; + } + } + }, [progressModal.progress, progressModal.taskId, progressModal.isOpen, progressModal.title, addAiLog]); + + // Reset step tracking when modal closes + useEffect(() => { + if (!progressModal.isOpen) { + lastLoggedStepRef.current = null; + lastLoggedPercentageRef.current = -1; + hasReloadedRef.current = false; + } else { + hasReloadedRef.current = false; + } + }, [progressModal.isOpen]); + const selectedSectors = useMemo( () => form.sectorIds.map((id) => ({ @@ -335,6 +489,92 @@ export default function SiteBuilderWizard() { )} + + { + progressModal.closeModal(); + // Reload pages when modal closes if task was completed + if (progressModal.progress.status === 'completed' && !hasReloadedRef.current && activeBlueprint) { + hasReloadedRef.current = true; + refreshPages(activeBlueprint.id); + } + // Clear structure task ID + useBuilderStore.setState({ structureTaskId: null }); + }} + /> + + {/* AI Function Logs - Display below content (only when Resource Debug is enabled) */} + {resourceDebugEnabled && aiLogs.length > 0 && ( +
+
+

+ AI Function Logs +

+ +
+
+ {aiLogs.slice().reverse().map((log, index) => ( +
+
+
+ + [{log.type.toUpperCase()}] + + + {log.action} + + {log.stepName && ( + + {log.stepName} + + )} + {log.percentage !== undefined && ( + + {log.percentage}% + + )} +
+ + {new Date(log.timestamp).toLocaleTimeString()} + +
+
+                  {JSON.stringify(log.data, null, 2)}
+                
+
+ ))} +
+
+ )} ); } diff --git a/frontend/src/store/builderStore.ts b/frontend/src/store/builderStore.ts index 9fa796e0..8bef9e1b 100644 --- a/frontend/src/store/builderStore.ts +++ b/frontend/src/store/builderStore.ts @@ -57,6 +57,7 @@ interface BuilderState { activeBlueprint?: SiteBlueprint; pages: PageBlueprint[]; selectedPageIds: number[]; + structureTaskId?: string | null; generationProgress?: { pagesQueued: number; taskIds: number[]; @@ -96,6 +97,7 @@ export const useBuilderStore = create((set, get) => ({ metadataError: undefined, pages: [], selectedPageIds: [], + structureTaskId: null, setField: (key, value) => set((state) => ({ @@ -284,6 +286,10 @@ export const useBuilderStore = create((set, get) => ({ }, ); + if (generation?.task_id) { + set({ structureTaskId: generation.task_id }); + } + if (generation?.structure) { lastStructure = generation.structure; }