From 5d97ab6e49d402a72ba2d50a9a5658f52305a0ab Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Tue, 18 Nov 2025 10:52:24 +0000 Subject: [PATCH] Refactor Site Builder Integration and Update Component Structure - Removed the separate `igny8_sites` service from Docker Compose, integrating its functionality into the main app. - Updated the Site Builder components to enhance user experience, including improved loading states and error handling. - Refactored routing and navigation in the Site Builder Wizard and Preview components for better clarity and usability. - Adjusted test files to reflect changes in import paths and ensure compatibility with the new structure. --- SITE_BUILDER_INTEGRATION_PLAN.md | 6 +- backend/celerybeat-schedule | Bin 16384 -> 16384 bytes docker-compose.app.yml | 22 --- .../__tests__/sites/BulkGeneration.test.tsx | 4 +- .../src/pages/Sites/Builder/Blueprints.tsx | 172 +++++++++------- frontend/src/pages/Sites/Builder/Preview.tsx | 183 +++++++++++++++--- frontend/src/pages/Sites/Builder/Wizard.tsx | 33 +++- frontend/src/store/builderStore.ts | 38 ++++ 8 files changed, 325 insertions(+), 133 deletions(-) diff --git a/SITE_BUILDER_INTEGRATION_PLAN.md b/SITE_BUILDER_INTEGRATION_PLAN.md index 913741c1..27754b21 100644 --- a/SITE_BUILDER_INTEGRATION_PLAN.md +++ b/SITE_BUILDER_INTEGRATION_PLAN.md @@ -125,21 +125,21 @@ Integrate the Site Builder wizard directly into the main frontend app (`frontend 1. Copy `siteDefinitionStore.ts` from `sites/src/builder/state/` 2. Use for preview functionality (if needed) -### Phase 7: Update Routing & Navigation ⏳ +### Phase 7: Update Routing & Navigation ✅ **Location**: `frontend/src/App.tsx` **Tasks**: 1. Ensure `/sites/builder` route points to new `Wizard.tsx` 2. Update navigation to show wizard in Sites section -### Phase 8: Fix Test File ⏳ +### Phase 8: Fix Test File ✅ **Location**: `frontend/src/__tests__/sites/BulkGeneration.test.tsx` **Tasks**: 1. Update import path from `site-builder/src/api/builder.api` to `services/siteBuilder.api` 2. Update mock path accordingly -### Phase 9: Testing ⏳ +### Phase 9: Testing ⏳ *(blocked by vitest not installed in dev env)* **Tasks**: 1. Test wizard flow: - Site selection diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index a81e7af1df96a3d52813ce30596bea99b22586b0..b7fd699287992e734c5194f6f18f2716c00d509b 100644 GIT binary patch delta 29 lcmZo@U~Fh$+@NH_r^~>=@M%hhXxo&aDc;vM8< { beforeEach(() => { diff --git a/frontend/src/pages/Sites/Builder/Blueprints.tsx b/frontend/src/pages/Sites/Builder/Blueprints.tsx index 7aecc76d..4eaf6d3d 100644 --- a/frontend/src/pages/Sites/Builder/Blueprints.tsx +++ b/frontend/src/pages/Sites/Builder/Blueprints.tsx @@ -1,102 +1,144 @@ -/** - * Site Builder Blueprints - * Moved from site-builder container to main app - * TODO: Migrate full implementation from site-builder/src/pages/dashboard/ - */ -import React, { useState, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import PageMeta from '../../../components/common/PageMeta'; -import { Card } from '../../../components/ui/card'; -import Button from '../../../components/ui/button/Button'; -import { useToast } from '../../../components/ui/toast/ToastContainer'; -import { fetchAPI } from '../../../services/api'; -import { FileText, Plus } from 'lucide-react'; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import PageMeta from "../../../components/common/PageMeta"; +import { Card } from "../../../components/ui/card"; +import Button from "../../../components/ui/button/Button"; +import { useToast } from "../../../components/ui/toast/ToastContainer"; +import { FileText, Loader2, Plus } from "lucide-react"; +import { useSiteStore } from "../../../store/siteStore"; +import { useBuilderStore } from "../../../store/builderStore"; +import { siteBuilderApi } from "../../../services/siteBuilder.api"; +import type { SiteBlueprint } from "../../../types/siteBuilder"; export default function SiteBuilderBlueprints() { const navigate = useNavigate(); const toast = useToast(); - const [blueprints, setBlueprints] = useState([]); - const [loading, setLoading] = useState(true); + const { activeSite } = useSiteStore(); + const { loadBlueprint, isLoadingBlueprint, activeBlueprint } = + useBuilderStore(); + const [blueprints, setBlueprints] = useState([]); + const [loading, setLoading] = useState(false); useEffect(() => { - loadBlueprints(); - }, []); + if (activeSite?.id) { + loadBlueprints(activeSite.id); + } else { + setBlueprints([]); + } + }, [activeSite?.id]); - const loadBlueprints = async () => { + const loadBlueprints = async (siteId: number) => { try { setLoading(true); - const data = await fetchAPI('/v1/site-builder/blueprints/'); - if (data?.results) { - setBlueprints(data.results); - } else if (Array.isArray(data)) { - setBlueprints(data); - } + const results = await siteBuilderApi.listBlueprints(siteId); + setBlueprints(results); } catch (error: any) { - toast.error(`Failed to load blueprints: ${error.message}`); + toast.error(error?.message || "Failed to load blueprints"); } finally { setLoading(false); } }; + const handleOpenPreview = async (blueprintId: number) => { + try { + await loadBlueprint(blueprintId); + toast.success("Loaded blueprint preview"); + navigate("/sites/builder/preview"); + } catch (error: any) { + toast.error(error?.message || "Unable to open blueprint"); + } + }; + return ( -
+
-
+
-

+

+ Sites / Blueprints +

+

Blueprints

-

- View and manage all site blueprints +

+ Review and preview structures generated for your active site.

-
- {loading ? ( -
-
Loading blueprints...
+ {!activeSite ? ( + +

+ Select a site using the header switcher to view its blueprints. +

+
+ ) : loading ? ( +
+ + Loading blueprints…
) : blueprints.length === 0 ? ( - -

- No blueprints created yet + +

+ No blueprints created yet for {activeSite.name}.

-
) : ( -
+
{blueprints.map((blueprint) => ( - -
-
-

- {blueprint.name} -

- {blueprint.description && ( -

- {blueprint.description} -

+ +
+

+ Blueprint #{blueprint.id} +

+

+ {blueprint.name} +

+ {blueprint.description && ( +

+ {blueprint.description} +

+ )} +
+
+ Status + {blueprint.status} +
+
+
-
-
- Status: {blueprint.status} -
- -
+ +
))} diff --git a/frontend/src/pages/Sites/Builder/Preview.tsx b/frontend/src/pages/Sites/Builder/Preview.tsx index a05d51da..38c10537 100644 --- a/frontend/src/pages/Sites/Builder/Preview.tsx +++ b/frontend/src/pages/Sites/Builder/Preview.tsx @@ -1,43 +1,162 @@ -/** - * Site Builder Preview - * Moved from site-builder container to main app - * TODO: Migrate full implementation from site-builder/src/pages/preview/ - */ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; -import PageMeta from '../../../components/common/PageMeta'; -import { Card } from '../../../components/ui/card'; -import Button from '../../../components/ui/button/Button'; -import { Eye } from 'lucide-react'; +import { useMemo } from "react"; +import { useNavigate } from "react-router-dom"; +import PageMeta from "../../../components/common/PageMeta"; +import { + Card, + CardDescription, + CardTitle, +} from "../../../components/ui/card"; +import Button from "../../../components/ui/button/Button"; +import Alert from "../../../components/ui/alert/Alert"; +import { useBuilderStore } from "../../../store/builderStore"; +import { useSiteDefinitionStore } from "../../../store/siteDefinitionStore"; +import { Eye } from "lucide-react"; export default function SiteBuilderPreview() { const navigate = useNavigate(); + const { activeBlueprint, pages } = useBuilderStore(); + const { structure, selectedSlug, selectPage } = useSiteDefinitionStore(); + + const selectedPageDefinition = useMemo(() => { + return structure?.pages?.find((page) => page.slug === selectedSlug); + }, [structure, selectedSlug]); + + if (!activeBlueprint) { + return ( +
+ + + +

+ Run the Site Builder wizard or open a blueprint to preview it. +

+ +
+
+ ); + } return ( -
+
-
-

- Site Preview -

-

- Preview your site during building -

+
+
+

+ Sites / Preview +

+

+ {activeBlueprint.name} +

+

+ Inspect the generated structure before publishing or deploying. +

+
+
- - -

- Site Preview -

-

- The Site Builder preview is being integrated into the main app. - Full implementation coming soon. -

- -
+ + +
+ + Pages + Choose a page to inspect its blocks. +
+ {pages.length === 0 && ( +

+ No pages yet. Run the wizard to generate structure. +

+ )} + {pages.map((page) => ( + + ))} +
+
+ + + {selectedPageDefinition ? ( +
+
+

+ Page +

+

+ {selectedPageDefinition.title} +

+

+ {selectedPageDefinition.objective || + "No objective provided"} +

+
+
+ {selectedPageDefinition.blocks?.map((block, index) => ( +
+

+ Block {index + 1} +

+

+ {block.type.replace(/_/g, " ")} +

+ {Array.isArray(block.content) ? ( +
    + {block.content.map((line, idx) => ( +
  • {line}
  • + ))} +
+ ) : block.content ? ( +
+                        {JSON.stringify(block.content, null, 2)}
+                      
+ ) : ( +

+ No content provided +

+ )} +
+ ))} + {!selectedPageDefinition.blocks?.length && ( +

+ This page has no block definitions yet. +

+ )} +
+
+ ) : ( +
+ Select a page to see its details. +
+ )} +
+
); } diff --git a/frontend/src/pages/Sites/Builder/Wizard.tsx b/frontend/src/pages/Sites/Builder/Wizard.tsx index 4cf68534..c882e9e3 100644 --- a/frontend/src/pages/Sites/Builder/Wizard.tsx +++ b/frontend/src/pages/Sites/Builder/Wizard.tsx @@ -214,15 +214,30 @@ export default function SiteBuilderWizard() { Pages generated {pages.length}
- +
+ + +
) : (
diff --git a/frontend/src/store/builderStore.ts b/frontend/src/store/builderStore.ts index 129e8278..e69dfd52 100644 --- a/frontend/src/store/builderStore.ts +++ b/frontend/src/store/builderStore.ts @@ -40,6 +40,7 @@ interface BuilderState { currentStep: number; isSubmitting: boolean; isGenerating: boolean; + isLoadingBlueprint: boolean; error?: string; activeBlueprint?: SiteBlueprint; pages: PageBlueprint[]; @@ -67,6 +68,7 @@ interface BuilderState { togglePageSelection: (pageId: number) => void; selectAllPages: () => void; clearPageSelection: () => void; + loadBlueprint: (blueprintId: number) => Promise; generateAllPages: (blueprintId: number, force?: boolean) => Promise; } @@ -75,6 +77,7 @@ export const useBuilderStore = create((set, get) => ({ currentStep: 0, isSubmitting: false, isGenerating: false, + isLoadingBlueprint: false, pages: [], selectedPageIds: [], @@ -221,6 +224,41 @@ export const useBuilderStore = create((set, get) => ({ clearPageSelection: () => set({ selectedPageIds: [] }), + loadBlueprint: async (blueprintId: number) => { + set({ isLoadingBlueprint: true, error: undefined }); + try { + const [blueprint, pages] = await Promise.all([ + siteBuilderApi.getBlueprint(blueprintId), + siteBuilderApi.listPages(blueprintId), + ]); + set({ + activeBlueprint: blueprint, + pages, + selectedPageIds: [], + }); + if (blueprint.structure_json) { + useSiteDefinitionStore.getState().setStructure(blueprint.structure_json); + } else { + useSiteDefinitionStore.getState().setStructure({ + site: undefined, + pages: pages.map((page) => ({ + slug: page.slug, + title: page.title, + type: page.type, + blocks: page.blocks_json, + })), + }); + } + useSiteDefinitionStore.getState().setPages(pages); + } catch (error: any) { + set({ + error: error?.message || "Unable to load blueprint", + }); + } finally { + set({ isLoadingBlueprint: false }); + } + }, + generateAllPages: async (blueprintId: number, force = false) => { const { selectedPageIds } = get(); set({ isGenerating: true, error: undefined, generationProgress: undefined });