From 3ea519483d22c2035defa2ad36f69d0e3eb8990c Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Tue, 18 Nov 2025 10:35:30 +0000 Subject: [PATCH] Refactor Site Builder Integration and Update Docker Configuration - Merged the site builder functionality into the main app, enhancing the SiteBuilderWizard component with new steps and improved UI. - Updated the Docker Compose configuration by removing the separate site builder service and integrating its functionality into the igny8_sites service. - Enhanced Vite configuration to support code-splitting for builder routes, optimizing loading times. - Updated package dependencies to include new libraries for state management and form handling. --- SITE_BUILDER_INTEGRATION_PLAN.md | 264 ++++++++++++++++ backend/celerybeat-schedule | Bin 16384 -> 16384 bytes docker-compose.app.yml | 23 +- frontend/src/pages/Sites/Builder/Wizard.tsx | 296 ++++++++++++++++-- .../pages/Sites/Builder/steps/BriefStep.tsx | 48 +++ .../Builder/steps/BusinessDetailsStep.tsx | 125 ++++++++ .../Sites/Builder/steps/ObjectivesStep.tsx | 90 ++++++ .../pages/Sites/Builder/steps/StyleStep.tsx | 106 +++++++ frontend/src/services/siteBuilder.api.ts | 125 ++++++++ frontend/src/store/builderStore.ts | 251 +++++++++++++++ frontend/src/store/siteDefinitionStore.ts | 30 ++ frontend/src/types/siteBuilder.ts | 88 ++++++ sites/MIGRATION_SUMMARY.md | 116 +++++++ sites/package.json | 14 +- sites/src/App.tsx | 50 ++- sites/src/index.css | 31 +- sites/src/main.tsx | 11 +- sites/src/shared/ProtectedRoute.tsx | 44 +++ sites/vite.config.ts | 16 +- 19 files changed, 1637 insertions(+), 91 deletions(-) create mode 100644 SITE_BUILDER_INTEGRATION_PLAN.md create mode 100644 frontend/src/pages/Sites/Builder/steps/BriefStep.tsx create mode 100644 frontend/src/pages/Sites/Builder/steps/BusinessDetailsStep.tsx create mode 100644 frontend/src/pages/Sites/Builder/steps/ObjectivesStep.tsx create mode 100644 frontend/src/pages/Sites/Builder/steps/StyleStep.tsx create mode 100644 frontend/src/services/siteBuilder.api.ts create mode 100644 frontend/src/store/builderStore.ts create mode 100644 frontend/src/store/siteDefinitionStore.ts create mode 100644 frontend/src/types/siteBuilder.ts create mode 100644 sites/MIGRATION_SUMMARY.md create mode 100644 sites/src/shared/ProtectedRoute.tsx diff --git a/SITE_BUILDER_INTEGRATION_PLAN.md b/SITE_BUILDER_INTEGRATION_PLAN.md new file mode 100644 index 00000000..913741c1 --- /dev/null +++ b/SITE_BUILDER_INTEGRATION_PLAN.md @@ -0,0 +1,264 @@ +# Site Builder Wizard Integration Plan + +## Overview +Integrate the Site Builder wizard directly into the main frontend app (`frontend/src/pages/Sites/Builder/`), using the same UI kit, state stores, and API helpers as the rest of the dashboard. The legacy `sites/src/builder` + `sites/src/renderer` code has been removed, so the only viable implementation path is the unified Sites module. + +## Current State + +### ✅ What's Done +- Legacy builder/renderer folders removed from Sites container (no more parallel UI) +- Type definitions created in `frontend/src/types/siteBuilder.ts` +- API helper created in `frontend/src/services/siteBuilder.api.ts` + +### ⚠️ What's Missing +- Builder store not yet created in the main app +- Wizard steps/page still placeholder in `frontend/src/pages/Sites/Builder/` +- No Tailwind/CX styling hooked into shared UI kit +- Routes/menu point to placeholder +- Tests/docs still reference old structure +- Sites container still contains stale references (needs cleanup after integration) + +## Integration Plan + +### Phase 1: Create API Service Layer ✅ +**Location**: `frontend/src/services/siteBuilder.api.ts` + +**Tasks**: +1. Create `siteBuilderApi` using `fetchAPI` pattern (not axios) +2. Functions needed: + - `listBlueprints()` + - `createBlueprint(payload)` + - `generateStructure(blueprintId, payload)` + - `listPages(blueprintId)` + - `generateAllPages(blueprintId, options)` + - `createTasksForPages(blueprintId, pageIds)` + +**API Endpoints** (already exist in backend): +- `GET /api/v1/site-builder/blueprints/` +- `POST /api/v1/site-builder/blueprints/` +- `POST /api/v1/site-builder/blueprints/{id}/generate_structure/` +- `GET /api/v1/site-builder/pages/?site_blueprint={id}` +- `POST /api/v1/site-builder/blueprints/{id}/generate_all_pages/` +- `POST /api/v1/site-builder/blueprints/{id}/create_tasks/` + +### Phase 2: Create Zustand Store ⏳ +**Location**: `frontend/src/store/builderStore.ts` + +**Tasks**: +1. Copy `builderStore.ts` from `sites/src/builder/state/` +2. Adapt to use `siteBuilderApi` instead of `builderApi` +3. Integrate with `useSiteStore` and `useSectorStore`: + - Auto-populate `siteId` from `useSiteStore().activeSite` + - Auto-populate `sectorId` from `useSectorStore().activeSector` + - Show site/sector selector if not set + +**Store State**: +- `form: BuilderFormData` - Wizard form data +- `currentStep: number` - Current wizard step (0-3) +- `isSubmitting: boolean` - Generation in progress +- `activeBlueprint: SiteBlueprint | null` - Latest blueprint +- `pages: PageBlueprint[]` - Generated pages +- `error: string | null` - Error message + +### Phase 3: Create Type Definitions ✅ +**Location**: `frontend/src/types/siteBuilder.ts` + +**Tasks**: +1. Copy types from `sites/src/builder/types/siteBuilder.ts` +2. Ensure compatibility with frontend's existing types + +**Types Needed**: +- `HostingType` +- `StylePreferences` +- `BuilderFormData` +- `SiteBlueprint` +- `PageBlueprint` +- `PageBlock` +- `SiteStructure` + +### Phase 4: Create Wizard Step Components ⏳ +**Location**: `frontend/src/pages/Sites/Builder/steps/` + +**Tasks**: +1. Copy step components from `sites/src/builder/pages/wizard/steps/` +2. Adapt to use frontend's UI components: + - Replace `Card` with `frontend/src/components/ui/card/Card` + - Replace custom inputs with Tailwind-styled inputs + - Use frontend's `Button` component +3. Adapt styles to Tailwind CSS: + - Remove `.sb-field`, `.sb-grid`, `.sb-pill` classes + - Use Tailwind utility classes instead + +**Step Components**: +- `BusinessDetailsStep.tsx` - Site/sector selection, business info +- `BriefStep.tsx` - Business brief textarea +- `ObjectivesStep.tsx` - Objectives list with add/remove +- `StyleStep.tsx` - Style preferences (palette, typography, personality) + +### Phase 5: Create Main Wizard Page ⏳ +**Location**: `frontend/src/pages/Sites/Builder/Wizard.tsx` + +**Tasks**: +1. Copy `WizardPage.tsx` from `sites/src/builder/pages/wizard/` +2. Adapt to frontend patterns: + - Use `PageMeta` component + - Use frontend's `Card` component + - Use frontend's `Button` component + - Use Tailwind CSS for styling +3. Integrate with stores: + - Auto-load active site/sector + - Show site/sector selector if needed + - Navigate to sites list on completion + +**Features**: +- 4-step wizard with progress indicators +- Step navigation (Back/Next buttons) +- Form validation +- Blueprint generation on submit +- Error handling +- Loading states + +### Phase 6: Create Site Definition Store (Optional) ⏳ +**Location**: `frontend/src/store/siteDefinitionStore.ts` + +**Tasks**: +1. Copy `siteDefinitionStore.ts` from `sites/src/builder/state/` +2. Use for preview functionality (if needed) + +### 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 ⏳ +**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 ⏳ +**Tasks**: +1. Test wizard flow: + - Site selection + - Sector selection + - All 4 wizard steps + - Blueprint generation + - Error handling +2. Test integration: + - Site/sector auto-population + - Navigation + - API calls + +### Phase 10: Cleanup ⏳ +**Tasks**: +1. Stop `igny8_site_builder` container +2. Remove Docker image +3. Remove `/site-builder` folder +4. Update documentation + +## File Structure After Integration + +``` +frontend/src/ +├── pages/Sites/Builder/ +│ ├── Wizard.tsx # Main wizard page (UPDATED) +│ ├── Preview.tsx # Preview page (keep placeholder for now) +│ ├── Blueprints.tsx # Blueprints list (already exists) +│ └── steps/ # NEW +│ ├── BusinessDetailsStep.tsx +│ ├── BriefStep.tsx +│ ├── ObjectivesStep.tsx +│ └── StyleStep.tsx +├── services/ +│ └── siteBuilder.api.ts # NEW - API service +├── store/ +│ ├── builderStore.ts # NEW - Builder state +│ └── siteDefinitionStore.ts # NEW - Site definition state (optional) +└── types/ + └── siteBuilder.ts # NEW - Type definitions +``` + +## Key Adaptations Needed + +### 1. API Client Pattern +**From** (sites container): +```typescript +import axios from 'axios'; +const client = axios.create({ baseURL: BASE_PATH }); +``` + +**To** (frontend): +```typescript +import { fetchAPI } from '../services/api'; +// Use fetchAPI directly, no axios +``` + +### 2. Component Library +**From** (sites container): +```typescript +import { Card } from '../../components/common/Card'; +``` + +**To** (frontend): +```typescript +import { Card } from '../../../components/ui/card/Card'; +``` + +### 3. Styling +**From** (sites container): +```css +.sb-field { ... } +.sb-grid { ... } +``` + +**To** (frontend): +```tsx +className="flex flex-col gap-2" +className="grid grid-cols-2 gap-4" +``` + +### 4. Store Integration +**From** (sites container): +```typescript +// Manual siteId/sectorId input +``` + +**To** (frontend): +```typescript +import { useSiteStore } from '../../../store/siteStore'; +import { useSectorStore } from '../../../store/sectorStore'; +// Auto-populate from stores +``` + +## Implementation Order + +1. ✅ Create types (`types/siteBuilder.ts`) +2. ✅ Create API service (`services/siteBuilder.api.ts`) +3. ⏳ Create builder store (`store/builderStore.ts`) +4. ⏳ Create step components (`pages/Sites/Builder/steps/`) +5. ⏳ Create main wizard page (`pages/Sites/Builder/Wizard.tsx`) +6. ⏳ Fix test file(s) +7. ⏳ Test integration +8. ⏳ Cleanup site-builder container/image/docs + +## Success Criteria + +- ✅ Wizard loads in main app at `/sites/builder` +- ✅ Site/sector auto-populated from stores +- ✅ All 4 steps work correctly +- ✅ Blueprint generation works +- ✅ Error handling works +- ✅ Navigation works +- ✅ No references to `site-builder/` folder in code +- ✅ Test file updated +- ✅ Sites container removed or marked deprecated in compose + +## Notes + +- Sites container will be deprecated once the wizard lives entirely inside the main app. +- Only integrate wizard into main frontend app (no parallel codepaths). +- Use frontend's existing patterns/components/stores for absolute consistency. + diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index 741a7355faf57a04a8125055db2c4bfa9a6cf1a5..a81e7af1df96a3d52813ce30596bea99b22586b0 100644 GIT binary patch delta 30 mcmZo@U~Fh$+@NT}&n3sez~D6{L$qy5&=l{>n+;4Za038`!U?JX delta 30 mcmZo@U~Fh$+@NT}&mqUaz~DY5L$qy5&=l|8n+;4Za038_-3g5V diff --git a/docker-compose.app.yml b/docker-compose.app.yml index 4074d315..5c59a627 100644 --- a/docker-compose.app.yml +++ b/docker-compose.app.yml @@ -101,33 +101,14 @@ services: - "com.docker.compose.project=igny8-app" - "com.docker.compose.service=igny8_marketing_dev" - igny8_site_builder: - image: igny8-site-builder-dev:latest - container_name: igny8_site_builder - restart: always - ports: - - "0.0.0.0:8025:5175" - environment: - VITE_API_URL: "https://api.igny8.com/api" - volumes: - - /data/app/igny8/site-builder:/app:rw - - /data/app/igny8/frontend:/frontend:ro - depends_on: - igny8_backend: - condition: service_healthy - networks: [igny8_net] - labels: - - "com.docker.compose.project=igny8-app" - - "com.docker.compose.service=igny8_site_builder" - igny8_sites: - # Sites renderer for hosting public sites + # Sites container: Public site renderer + Site Builder (merged) # Build separately: docker build -t igny8-sites-dev:latest -f Dockerfile.dev . image: igny8-sites-dev:latest container_name: igny8_sites restart: always ports: - - "0.0.0.0:8024:5176" # Sites renderer dev server port + - "0.0.0.0:8024:5176" # Sites renderer + Builder dev server port environment: VITE_API_URL: "https://api.igny8.com/api" SITES_DATA_PATH: "/sites" diff --git a/frontend/src/pages/Sites/Builder/Wizard.tsx b/frontend/src/pages/Sites/Builder/Wizard.tsx index 1ceef3c5..4cf68534 100644 --- a/frontend/src/pages/Sites/Builder/Wizard.tsx +++ b/frontend/src/pages/Sites/Builder/Wizard.tsx @@ -1,43 +1,273 @@ -/** - * Site Builder Wizard - * Moved from site-builder container to main app - * TODO: Migrate full implementation from site-builder/src/pages/wizard/ - */ -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 { Wand2 } from 'lucide-react'; +import { useEffect, useMemo } from "react"; +import { useNavigate } from "react-router-dom"; +import { + Card, + CardDescription, + CardTitle, +} from "../../../components/ui/card"; +import Button from "../../../components/ui/button/Button"; +import PageMeta from "../../../components/common/PageMeta"; +import SiteAndSectorSelector from "../../../components/common/SiteAndSectorSelector"; +import Alert from "../../../components/ui/alert/Alert"; +import { + Loader2, + PlayCircle, + RefreshCw, + Wand2, +} from "lucide-react"; +import { useSiteStore } from "../../../store/siteStore"; +import { useSectorStore } from "../../../store/sectorStore"; +import { useBuilderStore } from "../../../store/builderStore"; +import { BusinessDetailsStep } from "./steps/BusinessDetailsStep"; +import { BriefStep } from "./steps/BriefStep"; +import { ObjectivesStep } from "./steps/ObjectivesStep"; +import { StyleStep } from "./steps/StyleStep"; export default function SiteBuilderWizard() { const navigate = useNavigate(); + const { activeSite } = useSiteStore(); + const { activeSector } = useSectorStore(); + const { + form, + currentStep, + setStep, + setField, + updateStyle, + addObjective, + removeObjective, + nextStep, + previousStep, + submitWizard, + isSubmitting, + error, + activeBlueprint, + refreshPages, + pages, + generationProgress, + isGenerating, + syncContextFromStores, + } = useBuilderStore(); + + useEffect(() => { + syncContextFromStores(); + }, [activeSite?.id, activeSite?.name, activeSector?.id]); + + const steps = useMemo( + () => [ + { + title: "Business context", + component: ( + + ), + }, + { + title: "Brand brief", + component: , + }, + { + title: "Objectives", + component: ( + + ), + }, + { + title: "Look & feel", + component: ( + + ), + }, + ], + [form, setField, updateStyle, addObjective, removeObjective], + ); + + const isLastStep = currentStep === steps.length - 1; + const missingContext = !activeSite || !activeSector; + + const handlePrimary = async () => { + if (isLastStep) { + await submitWizard(); + } else { + nextStep(); + } + }; return ( -
- -
-

- Site Builder -

-

- Create a new site using AI-powered wizard -

+
+ +
+
+

+ Sites / Create Site +

+

+ Site Builder +

+

+ Create a new site using IGNY8’s AI-powered wizard. Align the estate, + strategy, and tone before publishing. +

+
+
- - -

- Site Builder Wizard -

-

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

- -
+ + + {missingContext && ( + + )} + +
+
+ +
+ {steps.map((step, index) => ( + + ))} +
+
+ + {steps[currentStep].component} + + {error && ( + + )} + +
+
+ Step {currentStep + 1} of {steps.length} +
+
+ + +
+
+
+ +
+ + Latest blueprint + + Once the wizard finishes, the most recent blueprint appears here. + + {activeBlueprint ? ( +
+
+ Status + {activeBlueprint.status} +
+
+ Pages generated + {pages.length} +
+ +
+ ) : ( +
+ Run the wizard to create your first blueprint. +
+ )} +
+ + {generationProgress && ( + + Generation progress + + Tracking background tasks queued for this blueprint. + +
+
+ Pages queued + {generationProgress.pagesQueued} +
+
+

+ Task IDs +

+

+ {generationProgress.taskIds.join(", ")} +

+
+
+ {generationProgress.celeryTaskId && ( +

+ Celery task ID: {generationProgress.celeryTaskId} +

+ )} +
+ )} + + {isGenerating && ( + + )} +
+
); } diff --git a/frontend/src/pages/Sites/Builder/steps/BriefStep.tsx b/frontend/src/pages/Sites/Builder/steps/BriefStep.tsx new file mode 100644 index 00000000..1226f261 --- /dev/null +++ b/frontend/src/pages/Sites/Builder/steps/BriefStep.tsx @@ -0,0 +1,48 @@ +import type { BuilderFormData } from "../../../../types/siteBuilder"; +import { Card } from "../../../../components/ui/card"; + +const labelClass = + "text-sm font-semibold text-gray-700 dark:text-white/80 mb-2 inline-block"; +const textareaClass = + "w-full rounded-2xl border border-gray-200 bg-white px-4 py-3 text-sm font-medium text-gray-900 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-white/10 dark:bg-white/[0.03] dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800"; + +interface Props { + data: BuilderFormData; + onChange: ( + key: K, + value: BuilderFormData[K], + ) => void; +} + +export function BriefStep({ data, onChange }: Props) { + return ( + +
+
+

+ Brand narrative +

+

+ Business brief +

+

+ Describe the brand, the offer, and what makes it unique. The more + context we provide, the more precise the structure. +

+
+ +
+ +