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.
This commit is contained in:
@@ -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<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { activeSite } = useSiteStore();
|
||||
const { loadBlueprint, isLoadingBlueprint, activeBlueprint } =
|
||||
useBuilderStore();
|
||||
const [blueprints, setBlueprints] = useState<SiteBlueprint[]>([]);
|
||||
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 (
|
||||
<div className="p-6">
|
||||
<div className="space-y-6 p-6">
|
||||
<PageMeta title="Blueprints - IGNY8" />
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
<p className="text-xs uppercase tracking-wider text-gray-500 dark:text-white/50">
|
||||
Sites / Blueprints
|
||||
</p>
|
||||
<h1 className="text-3xl font-semibold text-gray-900 dark:text-white">
|
||||
Blueprints
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
View and manage all site blueprints
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Review and preview structures generated for your active site.
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="primary">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create Blueprint
|
||||
<Button
|
||||
onClick={() => navigate("/sites/builder")}
|
||||
variant="solid"
|
||||
tone="brand"
|
||||
startIcon={<Plus className="h-4 w-4" />}
|
||||
>
|
||||
Create blueprint
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-gray-500">Loading blueprints...</div>
|
||||
{!activeSite ? (
|
||||
<Card className="p-8 text-center">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Select a site using the header switcher to view its blueprints.
|
||||
</p>
|
||||
</Card>
|
||||
) : loading ? (
|
||||
<div className="flex h-64 items-center justify-center text-gray-500 dark:text-gray-400">
|
||||
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
|
||||
Loading blueprints…
|
||||
</div>
|
||||
) : blueprints.length === 0 ? (
|
||||
<Card className="p-12 text-center">
|
||||
<FileText className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||
No blueprints created yet
|
||||
<FileText className="mx-auto mb-4 h-16 w-16 text-gray-400" />
|
||||
<p className="mb-4 text-gray-600 dark:text-gray-400">
|
||||
No blueprints created yet for {activeSite.name}.
|
||||
</p>
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="primary">
|
||||
Create Your First Blueprint
|
||||
<Button onClick={() => navigate("/sites/builder")} variant="solid" tone="brand">
|
||||
Launch Site Builder
|
||||
</Button>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{blueprints.map((blueprint) => (
|
||||
<Card key={blueprint.id} className="p-4 hover:shadow-lg transition-shadow">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{blueprint.name}
|
||||
</h3>
|
||||
{blueprint.description && (
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{blueprint.description}
|
||||
</p>
|
||||
<Card key={blueprint.id} className="space-y-4 p-5">
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-wider text-gray-500 dark:text-white/50">
|
||||
Blueprint #{blueprint.id}
|
||||
</p>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{blueprint.name}
|
||||
</h3>
|
||||
{blueprint.description && (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{blueprint.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-2xl bg-gray-50 px-4 py-3 text-sm font-semibold text-gray-700 dark:bg-white/[0.04] dark:text-white/80">
|
||||
<span>Status</span>
|
||||
<span className="capitalize">{blueprint.status}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
tone="brand"
|
||||
disabled={isLoadingBlueprint}
|
||||
onClick={() => handleOpenPreview(blueprint.id)}
|
||||
>
|
||||
{isLoadingBlueprint && activeBlueprint?.id === blueprint.id ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Loading…
|
||||
</>
|
||||
) : (
|
||||
"Open preview"
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between pt-3 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400">
|
||||
Status: {blueprint.status}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate(`/sites/${blueprint.site}/editor`)}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
tone="neutral"
|
||||
onClick={() => navigate(`/sites/${blueprint.site}/editor`)}
|
||||
>
|
||||
Open in editor
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user