refactor-upto-phase 6
This commit is contained in:
@@ -8,13 +8,14 @@ import {
|
||||
import Button from "../../../components/ui/button/Button";
|
||||
import PageMeta from "../../../components/common/PageMeta";
|
||||
import SiteAndSectorSelector from "../../../components/common/SiteAndSectorSelector";
|
||||
import PageHeader from "../../../components/common/PageHeader";
|
||||
import Alert from "../../../components/ui/alert/Alert";
|
||||
import {
|
||||
Loader2,
|
||||
PlayCircle,
|
||||
RefreshCw,
|
||||
Wand2,
|
||||
} from "lucide-react";
|
||||
GridIcon,
|
||||
ArrowLeftIcon,
|
||||
ArrowRightIcon,
|
||||
BoltIcon,
|
||||
} from "../../../icons";
|
||||
import { useSiteStore } from "../../../store/siteStore";
|
||||
import { useSectorStore } from "../../../store/sectorStore";
|
||||
import { useBuilderStore } from "../../../store/builderStore";
|
||||
@@ -289,29 +290,41 @@ export default function SiteBuilderWizard() {
|
||||
}
|
||||
};
|
||||
|
||||
const renderPrimaryIcon = () => {
|
||||
if (isSubmitting) {
|
||||
return (
|
||||
<span className="inline-flex h-4 w-4 items-center justify-center">
|
||||
<span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/70 border-t-transparent" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (isLastStep) {
|
||||
return <ArrowRightIcon className="size-4" />;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
<PageMeta title="Create Site - IGNY8" />
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-wider text-gray-500 dark:text-white/50">
|
||||
Sites / Create Site
|
||||
</p>
|
||||
<h1 className="text-3xl font-semibold text-gray-900 dark:text-white">
|
||||
Site Builder
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Create a new site using IGNY8’s AI-powered wizard. Align the estate,
|
||||
strategy, and tone before publishing.
|
||||
<PageHeader
|
||||
title="Site Builder"
|
||||
badge={{ icon: <GridIcon className="text-white size-5" />, color: "purple" }}
|
||||
hideSiteSector
|
||||
/>
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 max-w-2xl">
|
||||
Use the AI-powered wizard to capture business context, brand direction, and tone before generating blueprints.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate("/sites")}
|
||||
startIcon={<ArrowLeftIcon className="size-4" />}
|
||||
>
|
||||
Back to Sites
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate("/sites")}
|
||||
startIcon={<Wand2 size={16} />}
|
||||
>
|
||||
Back to sites
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<SiteAndSectorSelector hideSectorSelector />
|
||||
@@ -390,13 +403,7 @@ export default function SiteBuilderWizard() {
|
||||
tone="brand"
|
||||
disabled={missingContext || isSubmitting}
|
||||
onClick={handlePrimary}
|
||||
startIcon={
|
||||
isSubmitting ? (
|
||||
<Loader2 className="animate-spin" size={16} />
|
||||
) : isLastStep ? (
|
||||
<PlayCircle size={16} />
|
||||
) : undefined
|
||||
}
|
||||
startIcon={renderPrimaryIcon()}
|
||||
>
|
||||
{isLastStep ? "Generate structure" : "Next"}
|
||||
</Button>
|
||||
@@ -421,28 +428,29 @@ export default function SiteBuilderWizard() {
|
||||
<span>{pages.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row">
|
||||
<Button
|
||||
variant="outline"
|
||||
tone="brand"
|
||||
fullWidth
|
||||
startIcon={<RefreshCw size={16} />}
|
||||
onClick={() => refreshPages(activeBlueprint.id)}
|
||||
>
|
||||
Sync pages
|
||||
</Button>
|
||||
<Button
|
||||
variant="soft"
|
||||
tone="brand"
|
||||
fullWidth
|
||||
disabled={isGenerating}
|
||||
onClick={() =>
|
||||
loadBlueprint(activeBlueprint.id).then(() =>
|
||||
navigate("/sites/builder/preview"),
|
||||
)
|
||||
}
|
||||
>
|
||||
Open preview
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
tone="brand"
|
||||
fullWidth
|
||||
startIcon={<BoltIcon className="size-4" />}
|
||||
onClick={() => refreshPages(activeBlueprint.id)}
|
||||
>
|
||||
Sync pages
|
||||
</Button>
|
||||
<Button
|
||||
variant="soft"
|
||||
tone="brand"
|
||||
fullWidth
|
||||
disabled={isGenerating}
|
||||
endIcon={<ArrowRightIcon className="size-4" />}
|
||||
onClick={() =>
|
||||
loadBlueprint(activeBlueprint.id).then(() =>
|
||||
navigate("/sites/builder/preview"),
|
||||
)
|
||||
}
|
||||
>
|
||||
Open preview
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -10,10 +10,10 @@ import { useState, useEffect } from 'react';
|
||||
import { useBuilderWorkflowStore } from '../../../../store/builderWorkflowStore';
|
||||
import { fetchSiteBlueprintById, updateSiteBlueprint, SiteBlueprint } from '../../../../services/api';
|
||||
import { Card, CardDescription, CardTitle } from '../../../../components/ui/card';
|
||||
import ButtonWithTooltip from '../../../../components/ui/button/ButtonWithTooltip';
|
||||
import Button from '../../../../components/ui/button/Button';
|
||||
import Input from '../../../../components/form/input/InputField';
|
||||
import Alert from '../../../../components/ui/alert/Alert';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { BoltIcon, GridIcon } from '../../../../icons';
|
||||
import type { BuilderFormData, SiteBuilderMetadata } from '../../../../types/siteBuilder';
|
||||
|
||||
// Stage 1 Wizard props
|
||||
@@ -115,11 +115,13 @@ function BusinessDetailsStepStage2({ blueprintId }: { blueprintId: number }) {
|
||||
const canProceed = formData.name.trim().length > 0;
|
||||
|
||||
return (
|
||||
<Card className="p-6">
|
||||
<CardTitle>Business Details</CardTitle>
|
||||
<CardDescription>
|
||||
Tell us about your business and site type to get started.
|
||||
</CardDescription>
|
||||
<Card variant="surface" padding="lg" className="space-y-6">
|
||||
<div>
|
||||
<CardTitle>Business details</CardTitle>
|
||||
<CardDescription>
|
||||
Tell us about your business and hosting preference to keep blueprints organized.
|
||||
</CardDescription>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="error" className="mt-4">
|
||||
@@ -128,62 +130,58 @@ function BusinessDetailsStepStage2({ blueprintId }: { blueprintId: number }) {
|
||||
)}
|
||||
|
||||
<div className="mt-6 space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Site Name *</label>
|
||||
<Input
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder="My Awesome Site"
|
||||
required
|
||||
/>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Site name *
|
||||
</label>
|
||||
<Input
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder="Acme Robotics"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Hosting type
|
||||
</label>
|
||||
<select
|
||||
value={formData.hosting_type}
|
||||
onChange={(e) => setFormData({ ...formData, hosting_type: e.target.value as any })}
|
||||
className="w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 shadow-theme-xs dark:border-white/10 dark:bg-white/[0.03] dark:text-white"
|
||||
>
|
||||
<option value="igny8_sites">IGNY8 Sites</option>
|
||||
<option value="wordpress">WordPress</option>
|
||||
<option value="shopify">Shopify</option>
|
||||
<option value="multi">Multiple destinations</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Description</label>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Business description
|
||||
</label>
|
||||
<textarea
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
className="w-full px-3 py-2 border rounded-md"
|
||||
className="w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 shadow-theme-xs dark:border-white/10 dark:bg-white/[0.03] dark:text-white"
|
||||
rows={3}
|
||||
placeholder="Brief description of your site..."
|
||||
placeholder="Brief description of your business and what the site should cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Hosting Type</label>
|
||||
<select
|
||||
value={formData.hosting_type}
|
||||
onChange={(e) => setFormData({ ...formData, hosting_type: e.target.value as any })}
|
||||
className="w-full px-3 py-2 border rounded-md"
|
||||
>
|
||||
<option value="igny8_sites">IGNY8 Sites</option>
|
||||
<option value="wordpress">WordPress</option>
|
||||
<option value="shopify">Shopify</option>
|
||||
<option value="multi">Multiple Destinations</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-end">
|
||||
<ButtonWithTooltip
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={!canProceed || saving || loading}
|
||||
variant="primary"
|
||||
tooltip={
|
||||
!canProceed ? 'Please provide a site name to continue' :
|
||||
saving ? 'Saving...' :
|
||||
loading ? 'Loading...' : undefined
|
||||
}
|
||||
startIcon={<GridIcon className="size-4" />}
|
||||
>
|
||||
{saving ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
'Save & Continue'
|
||||
)}
|
||||
</ButtonWithTooltip>
|
||||
{saving ? 'Saving…' : 'Save & continue'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{!canProceed && (
|
||||
@@ -208,69 +206,88 @@ function BusinessDetailsStepStage1({
|
||||
selectedSectors?: Array<{ id: number; name: string }>;
|
||||
}) {
|
||||
return (
|
||||
<Card variant="surface" padding="lg">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-wider text-gray-500 dark:text-white/50">
|
||||
Business context
|
||||
</p>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Business details
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
These details help the AI understand what kind of site we are building.
|
||||
</p>
|
||||
<Card variant="surface" padding="lg" className="space-y-6">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white">
|
||||
<BoltIcon className="size-3.5" />
|
||||
Business context
|
||||
</div>
|
||||
<h3 className="mt-3 text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Business details
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 max-w-2xl">
|
||||
These inputs help the AI understand what we’re building. You can refine them later in the builder or site settings.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Site name</label>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<div className="space-y-4 rounded-2xl border border-gray-100 bg-white p-4 shadow-theme-sm dark:border-white/5 dark:bg-white/[0.02]">
|
||||
<label className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Site name
|
||||
</label>
|
||||
<Input
|
||||
value={data.siteName}
|
||||
onChange={(e) => onChange('siteName', e.target.value)}
|
||||
placeholder="Acme Robotics"
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Appears in dashboards, blueprints, and deployment metadata.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Business type</label>
|
||||
<Input
|
||||
value={data.businessType}
|
||||
onChange={(e) => onChange('businessType', e.target.value)}
|
||||
placeholder="B2B SaaS platform"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Industry</label>
|
||||
<Input
|
||||
value={data.industry}
|
||||
onChange={(e) => onChange('industry', e.target.value)}
|
||||
placeholder="Supply chain automation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Target audience</label>
|
||||
<div className="space-y-4 rounded-2xl border border-gray-100 bg-white p-4 shadow-theme-sm dark:border-white/5 dark:bg-white/[0.02]">
|
||||
<label className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Target audience
|
||||
</label>
|
||||
<Input
|
||||
value={data.targetAudience}
|
||||
onChange={(e) => onChange('targetAudience', e.target.value)}
|
||||
placeholder="Operations leaders at fast-scaling eCommerce brands"
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Helps the AI craft messaging, examples, and tone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Hosting preference</label>
|
||||
<div className="grid gap-6 lg:grid-cols-3">
|
||||
<div className="space-y-3 rounded-2xl border border-gray-100 bg-white p-4 shadow-theme-sm dark:border-white/5 dark:bg-white/[0.02]">
|
||||
<label className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Business type
|
||||
</label>
|
||||
<Input
|
||||
value={data.businessType}
|
||||
onChange={(e) => onChange('businessType', e.target.value)}
|
||||
placeholder="B2B SaaS platform"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3 rounded-2xl border border-gray-100 bg-white p-4 shadow-theme-sm dark:border-white/5 dark:bg-white/[0.02]">
|
||||
<label className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Industry
|
||||
</label>
|
||||
<Input
|
||||
value={data.industry}
|
||||
onChange={(e) => onChange('industry', e.target.value)}
|
||||
placeholder="Supply chain automation"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3 rounded-2xl border border-gray-100 bg-white p-4 shadow-theme-sm dark:border-white/5 dark:bg-white/[0.02]">
|
||||
<label className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
Hosting preference
|
||||
</label>
|
||||
<select
|
||||
value={data.hostingType}
|
||||
onChange={(e) => onChange('hostingType', e.target.value as BuilderFormData['hostingType'])}
|
||||
className="w-full px-3 py-2 border rounded-md"
|
||||
className="w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 shadow-theme-xs dark:border-white/10 dark:bg-white/[0.03] dark:text-white"
|
||||
>
|
||||
<option value="igny8_sites">IGNY8 Sites</option>
|
||||
<option value="wordpress">WordPress</option>
|
||||
<option value="shopify">Shopify</option>
|
||||
<option value="multi">Multiple destinations</option>
|
||||
</select>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Determines deployment targets and integration requirements.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -5,12 +5,21 @@
|
||||
*/
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { SearchIcon, FilterIcon, EditIcon, EyeIcon, TrashIcon, PlusIcon } from 'lucide-react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
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 {
|
||||
SearchIcon,
|
||||
PencilIcon,
|
||||
EyeIcon,
|
||||
TrashBinIcon,
|
||||
PlusIcon,
|
||||
FileIcon,
|
||||
GridIcon
|
||||
} from '../../icons';
|
||||
|
||||
interface ContentItem {
|
||||
id: number;
|
||||
@@ -118,15 +127,12 @@ export default function SiteContentManager() {
|
||||
<div className="p-6">
|
||||
<PageMeta title="Site Content Manager - IGNY8" />
|
||||
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Content Manager
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Manage and organize your site content ({totalCount} items)
|
||||
</p>
|
||||
</div>
|
||||
<PageHeader
|
||||
title={`Content Manager (${totalCount} items)`}
|
||||
badge={{ icon: <FileIcon />, color: 'blue' }}
|
||||
hideSiteSector
|
||||
/>
|
||||
<div className="mb-6 flex justify-end">
|
||||
<Button onClick={() => navigate(`/sites/${siteId}/posts/new`)} variant="primary">
|
||||
<PlusIcon className="w-4 h-4 mr-2" />
|
||||
New Post
|
||||
@@ -146,7 +152,7 @@ export default function SiteContentManager() {
|
||||
setSearchTerm(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md dark:bg-gray-800 dark:text-white"
|
||||
className="w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg dark:bg-gray-800 dark:text-white"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -261,7 +267,7 @@ export default function SiteContentManager() {
|
||||
onClick={() => navigate(`/sites/${siteId}/posts/${item.id}/edit`)}
|
||||
title="Edit"
|
||||
>
|
||||
<EditIcon className="w-4 h-4" />
|
||||
<PencilIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -269,7 +275,7 @@ export default function SiteContentManager() {
|
||||
onClick={() => handleDelete(item.id)}
|
||||
title="Delete"
|
||||
>
|
||||
<TrashIcon className="w-4 h-4" />
|
||||
<TrashBinIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,24 +6,23 @@
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
XCircleIcon,
|
||||
AlertCircleIcon,
|
||||
RocketIcon,
|
||||
RotateCcwIcon,
|
||||
RefreshCwIcon,
|
||||
FileTextIcon,
|
||||
TagIcon,
|
||||
LinkIcon,
|
||||
CheckSquareIcon,
|
||||
XSquareIcon,
|
||||
} from 'lucide-react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ErrorIcon,
|
||||
AlertIcon,
|
||||
BoltIcon,
|
||||
ArrowRightIcon,
|
||||
FileIcon,
|
||||
BoxIcon,
|
||||
CheckLineIcon,
|
||||
GridIcon
|
||||
} from '../../icons';
|
||||
import {
|
||||
fetchDeploymentReadiness,
|
||||
fetchSiteBlueprints,
|
||||
@@ -111,7 +110,7 @@ export default function DeploymentPanel() {
|
||||
return passed ? (
|
||||
<CheckCircleIcon className="w-5 h-5 text-green-600 dark:text-green-400" />
|
||||
) : (
|
||||
<XCircleIcon className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
<ErrorIcon className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -140,7 +139,7 @@ export default function DeploymentPanel() {
|
||||
<PageMeta title="Deployment Panel" />
|
||||
<Card className="p-6">
|
||||
<div className="text-center py-8">
|
||||
<AlertCircleIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<AlertIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-2">No blueprints found for this site</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -160,38 +159,34 @@ export default function DeploymentPanel() {
|
||||
<div className="p-6">
|
||||
<PageMeta title="Deployment Panel" />
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Deployment Panel</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Check readiness and deploy your site
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate(`/sites/${siteId}`)}
|
||||
>
|
||||
Back to Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleRollback}
|
||||
disabled={!selectedBlueprintId}
|
||||
>
|
||||
<RotateCcwIcon className="w-4 h-4 mr-2" />
|
||||
Rollback
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleDeploy}
|
||||
disabled={deploying || !readiness?.ready || !selectedBlueprintId}
|
||||
>
|
||||
<RocketIcon className={`w-4 h-4 mr-2 ${deploying ? 'animate-pulse' : ''}`} />
|
||||
{deploying ? 'Deploying...' : 'Deploy'}
|
||||
</Button>
|
||||
</div>
|
||||
<PageHeader
|
||||
title="Deployment Panel"
|
||||
badge={{ icon: <BoltIcon />, color: 'orange' }}
|
||||
hideSiteSector
|
||||
/>
|
||||
<div className="mb-6 flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate(`/sites/${siteId}`)}
|
||||
>
|
||||
Back to Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleRollback}
|
||||
disabled={!selectedBlueprintId}
|
||||
>
|
||||
<ArrowRightIcon className="w-4 h-4 mr-2 rotate-180" />
|
||||
Rollback
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleDeploy}
|
||||
disabled={deploying || !readiness?.ready || !selectedBlueprintId}
|
||||
>
|
||||
<BoltIcon className={`w-4 h-4 mr-2 ${deploying ? 'animate-pulse' : ''}`} />
|
||||
{deploying ? 'Deploying...' : 'Deploy'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Blueprint Selector */}
|
||||
@@ -286,7 +281,7 @@ export default function DeploymentPanel() {
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<GridIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">
|
||||
Cluster Coverage
|
||||
</h3>
|
||||
@@ -313,7 +308,7 @@ export default function DeploymentPanel() {
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckSquareIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<CheckLineIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">
|
||||
Content Validation
|
||||
</h3>
|
||||
@@ -340,7 +335,7 @@ export default function DeploymentPanel() {
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<TagIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<BoxIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">
|
||||
Taxonomy Completeness
|
||||
</h3>
|
||||
@@ -366,7 +361,7 @@ export default function DeploymentPanel() {
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<RefreshCwIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<BoltIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">Sync Status</h3>
|
||||
</div>
|
||||
{getCheckBadge(readiness.checks.sync_status)}
|
||||
@@ -395,7 +390,7 @@ export default function DeploymentPanel() {
|
||||
variant="outline"
|
||||
onClick={() => loadReadiness(selectedBlueprintId!)}
|
||||
>
|
||||
<RefreshCwIcon className="w-4 h-4 mr-2" />
|
||||
<BoltIcon className="w-4 h-4 mr-2" />
|
||||
Refresh Checks
|
||||
</Button>
|
||||
<Button
|
||||
@@ -403,7 +398,7 @@ export default function DeploymentPanel() {
|
||||
onClick={handleDeploy}
|
||||
disabled={deploying || !readiness.ready}
|
||||
>
|
||||
<RocketIcon className={`w-4 h-4 mr-2 ${deploying ? 'animate-pulse' : ''}`} />
|
||||
<BoltIcon className={`w-4 h-4 mr-2 ${deploying ? 'animate-pulse' : ''}`} />
|
||||
{deploying ? 'Deploying...' : 'Deploy Now'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -7,12 +7,20 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { PlusIcon, EditIcon, TrashIcon, GripVerticalIcon, CheckSquareIcon, SquareIcon } from 'lucide-react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
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 {
|
||||
PlusIcon,
|
||||
PencilIcon,
|
||||
TrashBinIcon,
|
||||
HorizontaLDots,
|
||||
CheckLineIcon,
|
||||
PageIcon
|
||||
} from '../../icons';
|
||||
|
||||
interface Page {
|
||||
id: number;
|
||||
@@ -71,12 +79,12 @@ const DraggablePageItem: React.FC<{
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{isSelected ? (
|
||||
<CheckSquareIcon className="w-5 h-5 text-brand-600 dark:text-brand-400" />
|
||||
<CheckLineIcon className="w-5 h-5 text-brand-600 dark:text-brand-400" />
|
||||
) : (
|
||||
<SquareIcon className="w-5 h-5 text-gray-400" />
|
||||
<div className="w-5 h-5 border-2 border-gray-400 rounded" />
|
||||
)}
|
||||
</button>
|
||||
<GripVerticalIcon className="w-5 h-5 text-gray-400 cursor-move" />
|
||||
<HorizontaLDots className="w-5 h-5 text-gray-400 cursor-move" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">{page.title}</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -86,11 +94,11 @@ const DraggablePageItem: React.FC<{
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={() => onEdit(page.id)}>
|
||||
<EditIcon className="w-4 h-4 mr-1" />
|
||||
<PencilIcon className="w-4 h-4 mr-1" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => onDelete(page.id)}>
|
||||
<TrashIcon className="w-4 h-4" />
|
||||
<TrashBinIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -272,15 +280,12 @@ export default function PageManager() {
|
||||
<div className="p-6">
|
||||
<PageMeta title="Page Manager - IGNY8" />
|
||||
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Page Manager
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Manage pages for your site
|
||||
</p>
|
||||
</div>
|
||||
<PageHeader
|
||||
title="Page Manager"
|
||||
badge={{ icon: <PageIcon />, color: 'blue' }}
|
||||
hideSiteSector
|
||||
/>
|
||||
<div className="mb-6 flex justify-end">
|
||||
<Button onClick={handleAddPage} variant="primary">
|
||||
<PlusIcon className="w-4 h-4 mr-2" />
|
||||
Add Page
|
||||
@@ -326,7 +331,7 @@ export default function PageManager() {
|
||||
onClick={handleBulkDelete}
|
||||
className="text-red-600 hover:text-red-700"
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 mr-1" />
|
||||
<TrashBinIcon className="w-4 h-4 mr-1" />
|
||||
Delete Selected
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => setSelectedPages(new Set())}>
|
||||
@@ -367,9 +372,9 @@ export default function PageManager() {
|
||||
className="flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white"
|
||||
>
|
||||
{selectedPages.size === pages.length ? (
|
||||
<CheckSquareIcon className="w-5 h-5 text-brand-600 dark:text-brand-400" />
|
||||
<CheckLineIcon className="w-5 h-5 text-brand-600 dark:text-brand-400" />
|
||||
) : (
|
||||
<SquareIcon className="w-5 h-5 text-gray-400" />
|
||||
<div className="w-5 h-5 border-2 border-gray-400 rounded" />
|
||||
)}
|
||||
<span>Select All</span>
|
||||
</button>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { SettingsIcon, SearchIcon, Share2Icon, CodeIcon, PlugIcon } from 'lucide-react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import Label from '../../components/form/Label';
|
||||
@@ -18,6 +18,7 @@ import { fetchAPI } from '../../services/api';
|
||||
import WordPressIntegrationCard from '../../components/sites/WordPressIntegrationCard';
|
||||
import WordPressIntegrationModal, { WordPressIntegrationFormData } from '../../components/sites/WordPressIntegrationModal';
|
||||
import { integrationApi, SiteIntegration } from '../../services/integration.api';
|
||||
import { GridIcon, PlugInIcon, PaperPlaneIcon, DocsIcon, BoltIcon } from '../../icons';
|
||||
|
||||
export default function SiteSettings() {
|
||||
const { id: siteId } = useParams<{ id: string }>();
|
||||
@@ -211,14 +212,11 @@ export default function SiteSettings() {
|
||||
<div className="p-6">
|
||||
<PageMeta title="Site Settings - IGNY8" />
|
||||
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Site Settings
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Configure site type, hosting, and other settings
|
||||
</p>
|
||||
</div>
|
||||
<PageHeader
|
||||
title="Site Settings"
|
||||
badge={{ icon: <GridIcon />, color: 'blue' }}
|
||||
hideSiteSector
|
||||
/>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="mb-6 border-b border-gray-200 dark:border-gray-700">
|
||||
@@ -235,7 +233,7 @@ export default function SiteSettings() {
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
<SettingsIcon className="w-4 h-4 inline mr-2" />
|
||||
<GridIcon className="w-4 h-4 inline mr-2" />
|
||||
General
|
||||
</button>
|
||||
<button
|
||||
@@ -250,7 +248,7 @@ export default function SiteSettings() {
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
<SearchIcon className="w-4 h-4 inline mr-2" />
|
||||
<DocsIcon className="w-4 h-4 inline mr-2" />
|
||||
SEO Meta Tags
|
||||
</button>
|
||||
<button
|
||||
@@ -265,7 +263,7 @@ export default function SiteSettings() {
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
<Share2Icon className="w-4 h-4 inline mr-2" />
|
||||
<PaperPlaneIcon className="w-4 h-4 inline mr-2" />
|
||||
Open Graph
|
||||
</button>
|
||||
<button
|
||||
@@ -280,7 +278,7 @@ export default function SiteSettings() {
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
<CodeIcon className="w-4 h-4 inline mr-2" />
|
||||
<BoltIcon className="w-4 h-4 inline mr-2" />
|
||||
Schema.org
|
||||
</button>
|
||||
<button
|
||||
@@ -295,7 +293,7 @@ export default function SiteSettings() {
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
<PlugIcon className="w-4 h-4 inline mr-2" />
|
||||
<PlugInIcon className="w-4 h-4 inline mr-2" />
|
||||
Integrations
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -6,24 +6,25 @@
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
XCircleIcon,
|
||||
AlertCircleIcon,
|
||||
RefreshCwIcon,
|
||||
ClockIcon,
|
||||
FileTextIcon,
|
||||
TagIcon,
|
||||
PackageIcon,
|
||||
ArrowRightIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
} from 'lucide-react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ErrorIcon,
|
||||
AlertIcon,
|
||||
BoltIcon,
|
||||
TimeIcon,
|
||||
FileIcon,
|
||||
BoxIcon,
|
||||
ArrowRightIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
PlugInIcon
|
||||
} from '../../icons';
|
||||
import {
|
||||
fetchSyncStatus,
|
||||
runSync,
|
||||
@@ -106,12 +107,12 @@ export default function SyncDashboard() {
|
||||
case 'success':
|
||||
return <CheckCircleIcon className="w-5 h-5 text-green-600 dark:text-green-400" />;
|
||||
case 'warning':
|
||||
return <AlertCircleIcon className="w-5 h-5 text-yellow-600 dark:text-yellow-400" />;
|
||||
return <AlertIcon className="w-5 h-5 text-yellow-600 dark:text-yellow-400" />;
|
||||
case 'error':
|
||||
case 'failed':
|
||||
return <XCircleIcon className="w-5 h-5 text-red-600 dark:text-red-400" />;
|
||||
return <ErrorIcon className="w-5 h-5 text-red-600 dark:text-red-400" />;
|
||||
default:
|
||||
return <ClockIcon className="w-5 h-5 text-gray-400" />;
|
||||
return <TimeIcon className="w-5 h-5 text-gray-400" />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,7 +133,7 @@ export default function SyncDashboard() {
|
||||
<PageMeta title="Sync Dashboard" />
|
||||
<Card className="p-6">
|
||||
<div className="text-center py-8">
|
||||
<AlertCircleIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<AlertIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<p className="text-gray-600 dark:text-gray-400">No sync data available</p>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -153,30 +154,26 @@ export default function SyncDashboard() {
|
||||
<div className="p-6">
|
||||
<PageMeta title="Sync Dashboard" />
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Sync Dashboard</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Monitor and manage WordPress sync status
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate(`/sites/${siteId}`)}
|
||||
>
|
||||
Back to Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => handleSync('both')}
|
||||
disabled={syncing || !hasIntegrations}
|
||||
>
|
||||
<RefreshCwIcon className={`w-4 h-4 mr-2 ${syncing ? 'animate-spin' : ''}`} />
|
||||
{syncing ? 'Syncing...' : 'Sync All'}
|
||||
</Button>
|
||||
</div>
|
||||
<PageHeader
|
||||
title="Sync Dashboard"
|
||||
badge={{ icon: <PlugInIcon />, color: 'blue' }}
|
||||
hideSiteSector
|
||||
/>
|
||||
<div className="mb-6 flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate(`/sites/${siteId}`)}
|
||||
>
|
||||
Back to Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => handleSync('both')}
|
||||
disabled={syncing || !hasIntegrations}
|
||||
>
|
||||
<BoltIcon className={`w-4 h-4 mr-2 ${syncing ? 'animate-spin' : ''}`} />
|
||||
{syncing ? 'Syncing...' : 'Sync All'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Overall Status */}
|
||||
@@ -275,7 +272,7 @@ export default function SyncDashboard() {
|
||||
) : (
|
||||
<Card className="p-6 mb-6">
|
||||
<div className="text-center py-8">
|
||||
<AlertCircleIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<AlertIcon className="w-12 h-12 text-gray-400 mx-auto mb-3" />
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-2">No active integrations</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -311,7 +308,7 @@ export default function SyncDashboard() {
|
||||
mismatches.taxonomies.missing_in_igny8.length > 0) && (
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2 flex items-center gap-2">
|
||||
<TagIcon className="w-4 h-4" />
|
||||
<BoxIcon className="w-4 h-4" />
|
||||
Taxonomy Mismatches
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
@@ -348,7 +345,7 @@ export default function SyncDashboard() {
|
||||
mismatches.products.missing_in_igny8.length > 0) && (
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2 flex items-center gap-2">
|
||||
<PackageIcon className="w-4 h-4" />
|
||||
<BoxIcon className="w-4 h-4" />
|
||||
Product Mismatches
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
@@ -375,7 +372,7 @@ export default function SyncDashboard() {
|
||||
mismatches.posts.missing_in_igny8.length > 0) && (
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2 flex items-center gap-2">
|
||||
<FileTextIcon className="w-4 h-4" />
|
||||
<FileIcon className="w-4 h-4" />
|
||||
Post Mismatches
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
@@ -404,7 +401,7 @@ export default function SyncDashboard() {
|
||||
onClick={() => handleSync('both')}
|
||||
disabled={syncing}
|
||||
>
|
||||
<RefreshCwIcon className="w-4 h-4 mr-2" />
|
||||
<BoltIcon className="w-4 h-4 mr-2" />
|
||||
Retry Sync to Resolve
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user