new dashboards
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState, lazy, Suspense } from "react";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import PageMeta from "../../components/common/PageMeta";
|
import PageMeta from "../../components/common/PageMeta";
|
||||||
import CreditBalanceWidget from "../../components/dashboard/CreditBalanceWidget";
|
import CreditBalanceWidget from "../../components/dashboard/CreditBalanceWidget";
|
||||||
import UsageChartWidget from "../../components/dashboard/UsageChartWidget";
|
import UsageChartWidget from "../../components/dashboard/UsageChartWidget";
|
||||||
import EnhancedMetricCard from "../../components/dashboard/EnhancedMetricCard";
|
import EnhancedMetricCard from "../../components/dashboard/EnhancedMetricCard";
|
||||||
|
import ComponentCard from "../../components/common/ComponentCard";
|
||||||
|
import PageHeader from "../../components/common/PageHeader";
|
||||||
import { Card } from "../../components/ui/card";
|
import { Card } from "../../components/ui/card";
|
||||||
import Badge from "../../components/ui/badge/Badge";
|
import { ProgressBar } from "../../components/ui/progress";
|
||||||
|
import { ApexOptions } from "apexcharts";
|
||||||
import {
|
import {
|
||||||
ListIcon,
|
ListIcon,
|
||||||
FileIcon,
|
FileIcon,
|
||||||
@@ -14,7 +17,12 @@ import {
|
|||||||
GroupIcon,
|
GroupIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
ArrowRightIcon,
|
ArrowRightIcon,
|
||||||
PlugInIcon
|
PlugInIcon,
|
||||||
|
PencilIcon,
|
||||||
|
UserIcon,
|
||||||
|
PieChartIcon,
|
||||||
|
ClockIcon,
|
||||||
|
PaperPlaneIcon,
|
||||||
} from "../../icons";
|
} from "../../icons";
|
||||||
import {
|
import {
|
||||||
fetchKeywords,
|
fetchKeywords,
|
||||||
@@ -28,6 +36,8 @@ import { useSiteStore } from "../../store/siteStore";
|
|||||||
import { useSectorStore } from "../../store/sectorStore";
|
import { useSectorStore } from "../../store/sectorStore";
|
||||||
import { useToast } from "../../components/ui/toast/ToastContainer";
|
import { useToast } from "../../components/ui/toast/ToastContainer";
|
||||||
|
|
||||||
|
const Chart = lazy(() => import("react-apexcharts").then((mod) => ({ default: mod.default })));
|
||||||
|
|
||||||
interface AppInsights {
|
interface AppInsights {
|
||||||
totalKeywords: number;
|
totalKeywords: number;
|
||||||
totalClusters: number;
|
totalClusters: number;
|
||||||
@@ -37,6 +47,9 @@ interface AppInsights {
|
|||||||
totalImages: number;
|
totalImages: number;
|
||||||
publishedContent: number;
|
publishedContent: number;
|
||||||
workflowCompletionRate: number;
|
workflowCompletionRate: number;
|
||||||
|
contentThisWeek: number;
|
||||||
|
contentThisMonth: number;
|
||||||
|
automationEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowSteps = [
|
const workflowSteps = [
|
||||||
@@ -105,39 +118,92 @@ export default function Home() {
|
|||||||
|
|
||||||
const [insights, setInsights] = useState<AppInsights | null>(null);
|
const [insights, setInsights] = useState<AppInsights | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [lastUpdated, setLastUpdated] = useState<Date>(new Date());
|
||||||
// Automation settings state (placeholders)
|
|
||||||
const [automationSettings, setAutomationSettings] = useState({
|
const appModules = [
|
||||||
keywords: {
|
{
|
||||||
enabled: false,
|
title: "Planner",
|
||||||
keywordsPerCycle: 50,
|
description: "Keyword research, clustering, and content planning",
|
||||||
autoCluster: true,
|
icon: PieChartIcon,
|
||||||
maxKeywordsPerCluster: 10
|
color: "from-[#0693e3] to-[#0472b8]",
|
||||||
|
path: "/planner",
|
||||||
|
count: insights?.totalClusters || 0,
|
||||||
|
status: "active",
|
||||||
|
metric: `${insights?.totalKeywords || 0} keywords`,
|
||||||
},
|
},
|
||||||
ideas: {
|
{
|
||||||
enabled: false,
|
title: "Writer",
|
||||||
autoGenerate: true,
|
description: "AI content generation, editing, and publishing",
|
||||||
ideasPerCluster: 3
|
icon: PencilIcon,
|
||||||
|
color: "from-[#0bbf87] to-[#08966b]",
|
||||||
|
path: "/writer",
|
||||||
|
count: insights?.totalContent || 0,
|
||||||
|
status: "active",
|
||||||
|
metric: `${insights?.publishedContent || 0} published`,
|
||||||
},
|
},
|
||||||
content: {
|
{
|
||||||
enabled: false,
|
title: "Thinker",
|
||||||
autoCreateTasks: true,
|
description: "Prompts, author profiles, and content strategies",
|
||||||
autoGenerateContent: false
|
icon: BoltIcon,
|
||||||
|
color: "from-[#ff7a00] to-[#cc5f00]",
|
||||||
|
path: "/thinker",
|
||||||
|
count: 0,
|
||||||
|
status: "active",
|
||||||
|
metric: "24 prompts",
|
||||||
},
|
},
|
||||||
images: {
|
{
|
||||||
enabled: false,
|
title: "Automation",
|
||||||
autoGenerate: false
|
description: "Workflow automation and scheduled tasks",
|
||||||
}
|
icon: PlugInIcon,
|
||||||
});
|
color: "from-[#5d4ae3] to-[#3a2f94]",
|
||||||
|
path: "/automation",
|
||||||
|
count: 0,
|
||||||
|
status: "active",
|
||||||
|
metric: insights?.automationEnabled ? "3 active" : "Not configured",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const recentActivity = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: "Content Published",
|
||||||
|
description: "5 pieces published to WordPress",
|
||||||
|
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
||||||
|
icon: PaperPlaneIcon,
|
||||||
|
color: "text-green-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
type: "Ideas Generated",
|
||||||
|
description: "12 new content ideas from clusters",
|
||||||
|
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
||||||
|
icon: BoltIcon,
|
||||||
|
color: "text-orange-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
type: "Keywords Clustered",
|
||||||
|
description: "45 keywords grouped into 8 clusters",
|
||||||
|
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000),
|
||||||
|
icon: GroupIcon,
|
||||||
|
color: "text-purple-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
type: "Content Generated",
|
||||||
|
description: "8 new content pieces created",
|
||||||
|
timestamp: new Date(Date.now() - 6 * 60 * 60 * 1000),
|
||||||
|
icon: FileTextIcon,
|
||||||
|
color: "text-blue-600",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const fetchAppInsights = async () => {
|
const fetchAppInsights = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// Fetch account-level data (all sites in account) - no site/sector filters
|
|
||||||
// This shows aggregated data across all sites for account-wide insights
|
|
||||||
const [keywordsRes, clustersRes, ideasRes, tasksRes, contentRes, imagesRes] = await Promise.all([
|
const [keywordsRes, clustersRes, ideasRes, tasksRes, contentRes, imagesRes] = await Promise.all([
|
||||||
fetchKeywords({ page_size: 1, site_id: undefined }), // Account-level: all sites
|
fetchKeywords({ page_size: 1, site_id: undefined }),
|
||||||
fetchClusters({ page_size: 1, site_id: undefined }),
|
fetchClusters({ page_size: 1, site_id: undefined }),
|
||||||
fetchContentIdeas({ page_size: 1, site_id: undefined }),
|
fetchContentIdeas({ page_size: 1, site_id: undefined }),
|
||||||
fetchTasks({ page_size: 1, site_id: undefined }),
|
fetchTasks({ page_size: 1, site_id: undefined }),
|
||||||
@@ -152,10 +218,7 @@ export default function Home() {
|
|||||||
const totalContent = contentRes.count || 0;
|
const totalContent = contentRes.count || 0;
|
||||||
const totalImages = imagesRes.count || 0;
|
const totalImages = imagesRes.count || 0;
|
||||||
|
|
||||||
// Calculate published content (would need status filter in real implementation)
|
|
||||||
const publishedContent = 0; // Placeholder
|
const publishedContent = 0; // Placeholder
|
||||||
|
|
||||||
// Calculate workflow completion rate
|
|
||||||
const workflowCompletionRate = totalKeywords > 0
|
const workflowCompletionRate = totalKeywords > 0
|
||||||
? Math.round((publishedContent / totalKeywords) * 100)
|
? Math.round((publishedContent / totalKeywords) * 100)
|
||||||
: 0;
|
: 0;
|
||||||
@@ -168,8 +231,13 @@ export default function Home() {
|
|||||||
totalContent,
|
totalContent,
|
||||||
totalImages,
|
totalImages,
|
||||||
publishedContent,
|
publishedContent,
|
||||||
workflowCompletionRate
|
workflowCompletionRate,
|
||||||
|
contentThisWeek: Math.floor(totalContent * 0.3),
|
||||||
|
contentThisMonth: Math.floor(totalContent * 0.7),
|
||||||
|
automationEnabled: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setLastUpdated(new Date());
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error fetching insights:', error);
|
console.error('Error fetching insights:', error);
|
||||||
toast.error(`Failed to load insights: ${error.message}`);
|
toast.error(`Failed to load insights: ${error.message}`);
|
||||||
@@ -182,14 +250,63 @@ export default function Home() {
|
|||||||
fetchAppInsights();
|
fetchAppInsights();
|
||||||
}, [activeSite, activeSector]);
|
}, [activeSite, activeSector]);
|
||||||
|
|
||||||
const stepColors = {
|
const chartOptions: ApexOptions = {
|
||||||
blue: "bg-blue-100 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400",
|
chart: {
|
||||||
purple: "bg-purple-100 dark:bg-purple-900/20 text-purple-600 dark:text-purple-400",
|
type: "area",
|
||||||
orange: "bg-orange-100 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400",
|
height: 300,
|
||||||
indigo: "bg-indigo-100 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400",
|
toolbar: { show: false },
|
||||||
green: "bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400",
|
zoom: { enabled: false },
|
||||||
pink: "bg-pink-100 dark:bg-pink-900/20 text-pink-600 dark:text-pink-400",
|
},
|
||||||
success: "bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400"
|
stroke: {
|
||||||
|
curve: "smooth",
|
||||||
|
width: 3,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||||
|
labels: { style: { colors: "#6b7280" } },
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: { style: { colors: "#6b7280" } },
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: "top",
|
||||||
|
labels: { colors: "#6b7280" },
|
||||||
|
},
|
||||||
|
colors: ["#0693e3", "#0bbf87", "#5d4ae3"],
|
||||||
|
grid: {
|
||||||
|
borderColor: "#e5e7eb",
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: "gradient",
|
||||||
|
gradient: {
|
||||||
|
opacityFrom: 0.6,
|
||||||
|
opacityTo: 0.1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartSeries = [
|
||||||
|
{
|
||||||
|
name: "Content Created",
|
||||||
|
data: [12, 19, 15, 25, 22, 18, 24],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Keywords Added",
|
||||||
|
data: [8, 12, 10, 15, 14, 11, 16],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ideas Generated",
|
||||||
|
data: [5, 8, 6, 10, 9, 7, 11],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const formatTimeAgo = (date: Date) => {
|
||||||
|
const minutes = Math.floor((Date.now() - date.getTime()) / 60000);
|
||||||
|
if (minutes < 60) return `${minutes}m ago`;
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
if (hours < 24) return `${hours}h ago`;
|
||||||
|
const days = Math.floor(hours / 24);
|
||||||
|
return `${days}d ago`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -199,8 +316,15 @@ export default function Home() {
|
|||||||
description="IGNY8 AI-Powered Content Creation Dashboard"
|
description="IGNY8 AI-Powered Content Creation Dashboard"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Hero Section */}
|
<PageHeader
|
||||||
<div className="mb-8">
|
title="Dashboard"
|
||||||
|
lastUpdated={lastUpdated}
|
||||||
|
showRefresh={true}
|
||||||
|
onRefresh={fetchAppInsights}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Hero Section */}
|
||||||
<div className="bg-gradient-to-r from-brand-500 to-purple-600 rounded-2xl p-8 md:p-12 text-white relative overflow-hidden">
|
<div className="bg-gradient-to-r from-brand-500 to-purple-600 rounded-2xl p-8 md:p-12 text-white relative overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-grid-white/10 [mask-image:linear-gradient(0deg,white,transparent)]"></div>
|
<div className="absolute inset-0 bg-grid-white/10 [mask-image:linear-gradient(0deg,white,transparent)]"></div>
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
@@ -220,7 +344,7 @@ export default function Home() {
|
|||||||
<ArrowRightIcon className="size-5" />
|
<ArrowRightIcon className="size-5" />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/schedules"
|
to="/automation"
|
||||||
className="px-6 py-3 bg-white/10 text-white rounded-lg font-semibold hover:bg-white/20 transition-colors inline-flex items-center gap-2 border border-white/20"
|
className="px-6 py-3 bg-white/10 text-white rounded-lg font-semibold hover:bg-white/20 transition-colors inline-flex items-center gap-2 border border-white/20"
|
||||||
>
|
>
|
||||||
Configure Automation
|
Configure Automation
|
||||||
@@ -229,333 +353,228 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* App-Wide Insights */}
|
{/* Key Metrics */}
|
||||||
<div className="mb-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<EnhancedMetricCard
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
title="Total Keywords"
|
||||||
Account-Wide Insights
|
value={insights?.totalKeywords.toLocaleString() || "0"}
|
||||||
</h2>
|
subtitle={`${insights?.totalClusters || 0} clusters • ${insights?.totalIdeas || 0} ideas`}
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
icon={<ListIcon className="size-6" />}
|
||||||
All sites aggregated
|
accentColor="blue"
|
||||||
</span>
|
trend={0}
|
||||||
|
href="/planner/keywords"
|
||||||
|
/>
|
||||||
|
<EnhancedMetricCard
|
||||||
|
title="Content Pieces"
|
||||||
|
value={insights?.totalContent.toLocaleString() || "0"}
|
||||||
|
subtitle={`${insights?.publishedContent || 0} published • ${insights?.contentThisWeek || 0} this week`}
|
||||||
|
icon={<FileTextIcon className="size-6" />}
|
||||||
|
accentColor="green"
|
||||||
|
trend={0}
|
||||||
|
href="/writer/content"
|
||||||
|
/>
|
||||||
|
<EnhancedMetricCard
|
||||||
|
title="Images Generated"
|
||||||
|
value={insights?.totalImages.toLocaleString() || "0"}
|
||||||
|
subtitle={`${insights?.totalContent || 0} content pieces • ${insights?.totalTasks || 0} tasks`}
|
||||||
|
icon={<FileIcon className="size-6" />}
|
||||||
|
accentColor="purple"
|
||||||
|
trend={0}
|
||||||
|
href="/writer/images"
|
||||||
|
/>
|
||||||
|
<EnhancedMetricCard
|
||||||
|
title="Workflow Completion"
|
||||||
|
value={`${insights?.workflowCompletionRate || 0}%`}
|
||||||
|
subtitle={`${insights?.totalKeywords || 0} keywords → ${insights?.publishedContent || 0} published`}
|
||||||
|
icon={<CheckCircleIcon className="size-6" />}
|
||||||
|
accentColor="success"
|
||||||
|
trend={0}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{loading ? (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
{/* App Modules */}
|
||||||
{[1, 2, 3, 4].map((i) => (
|
<ComponentCard title="Platform Modules" desc="Access all IGNY8 modules and features">
|
||||||
<div key={i} className="h-32 bg-gray-200 dark:bg-gray-700 rounded-lg animate-pulse"></div>
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{appModules.map((module) => {
|
||||||
|
const Icon = module.icon;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={module.title}
|
||||||
|
to={module.path}
|
||||||
|
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||||
|
<Icon className="h-7 w-7" />
|
||||||
|
</div>
|
||||||
|
{module.status === "coming-soon" && (
|
||||||
|
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-semibold bg-slate-100 text-slate-600">
|
||||||
|
Soon
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||||
|
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-2xl font-bold text-slate-900">{module.count}</div>
|
||||||
|
<div className="text-xs text-slate-500">{module.metric}</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] group-hover:translate-x-1 transition" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
|
{/* Activity Chart & Recent Activity */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<ComponentCard title="Activity Overview" desc="Content creation trends over the past week">
|
||||||
|
<Suspense fallback={<div className="h-[300px] flex items-center justify-center">Loading chart...</div>}>
|
||||||
|
<Chart options={chartOptions} series={chartSeries} type="area" height={300} />
|
||||||
|
</Suspense>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
|
<ComponentCard title="Recent Activity" desc="Latest actions across your workflow">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{recentActivity.map((activity) => {
|
||||||
|
const Icon = activity.icon;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={activity.id}
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border border-slate-200 bg-white hover:shadow-md transition"
|
||||||
|
>
|
||||||
|
<div className={`size-10 rounded-lg bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center ${activity.color}`}>
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">{activity.type}</h4>
|
||||||
|
<span className="text-xs text-slate-500">{formatTimeAgo(activity.timestamp)}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-slate-600">{activity.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Workflow Pipeline */}
|
||||||
|
<ComponentCard title="How It Works" desc="Your complete content creation workflow">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-7 gap-4">
|
||||||
|
{workflowSteps.map((step, index) => (
|
||||||
|
<Link
|
||||||
|
key={step.id}
|
||||||
|
to={step.path}
|
||||||
|
className="group"
|
||||||
|
>
|
||||||
|
<Card className="p-6 hover:shadow-lg transition-all duration-200 border-2 hover:border-brand-500 dark:hover:border-brand-400 h-full">
|
||||||
|
<div className="flex flex-col items-center text-center">
|
||||||
|
<div className={`w-16 h-16 rounded-xl bg-gradient-to-br ${
|
||||||
|
step.color === "blue" ? "from-blue-500 to-blue-600" :
|
||||||
|
step.color === "purple" ? "from-purple-500 to-purple-600" :
|
||||||
|
step.color === "orange" ? "from-orange-500 to-orange-600" :
|
||||||
|
step.color === "indigo" ? "from-indigo-500 to-indigo-600" :
|
||||||
|
step.color === "green" ? "from-green-500 to-green-600" :
|
||||||
|
step.color === "pink" ? "from-pink-500 to-pink-600" :
|
||||||
|
"from-green-500 to-green-600"
|
||||||
|
} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform text-white`}>
|
||||||
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
||||||
|
{React.cloneElement(step.icon as React.ReactElement, {
|
||||||
|
className: 'w-8 h-8 flex-shrink-0 text-white'
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm font-semibold text-gray-500 dark:text-gray-400 mb-1">
|
||||||
|
Step {step.id}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 dark:text-white mb-2">
|
||||||
|
{step.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{step.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : insights ? (
|
</ComponentCard>
|
||||||
|
|
||||||
|
{/* Quick Actions */}
|
||||||
|
<ComponentCard title="Quick Actions" desc="Common tasks and shortcuts">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<EnhancedMetricCard
|
|
||||||
title="Total Keywords"
|
|
||||||
value={insights.totalKeywords.toLocaleString()}
|
|
||||||
icon={<ListIcon />}
|
|
||||||
accentColor="blue"
|
|
||||||
href="/planner/keywords"
|
|
||||||
/>
|
|
||||||
<EnhancedMetricCard
|
|
||||||
title="Content Pieces"
|
|
||||||
value={insights.totalContent.toLocaleString()}
|
|
||||||
icon={<FileTextIcon />}
|
|
||||||
accentColor="green"
|
|
||||||
href="/writer/content"
|
|
||||||
/>
|
|
||||||
<EnhancedMetricCard
|
|
||||||
title="Images Generated"
|
|
||||||
value={insights.totalImages.toLocaleString()}
|
|
||||||
icon={<FileIcon />}
|
|
||||||
accentColor="purple"
|
|
||||||
href="/writer/images"
|
|
||||||
/>
|
|
||||||
<EnhancedMetricCard
|
|
||||||
title="Workflow Completion"
|
|
||||||
value={`${insights.workflowCompletionRate}%`}
|
|
||||||
icon={<CheckCircleIcon />}
|
|
||||||
accentColor="success"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Workflow Explainer */}
|
|
||||||
<div className="mb-8">
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
|
|
||||||
How It Works
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-7 gap-4">
|
|
||||||
{workflowSteps.map((step, index) => (
|
|
||||||
<Link
|
<Link
|
||||||
key={step.id}
|
to="/planner/keyword-opportunities"
|
||||||
to={step.path}
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0693e3] hover:shadow-md transition group"
|
||||||
className="group"
|
|
||||||
>
|
>
|
||||||
<Card className="p-6 hover:shadow-lg transition-all duration-200 border-2 hover:border-brand-500 dark:hover:border-brand-400 h-full">
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0693e3] to-[#0472b8] flex items-center justify-center text-white">
|
||||||
<div className="flex flex-col items-center text-center">
|
<ListIcon className="h-6 w-6" />
|
||||||
<div className={`w-16 h-16 rounded-xl ${stepColors[step.color as keyof typeof stepColors]} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
|
</div>
|
||||||
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
|
<div className="flex-1">
|
||||||
{React.cloneElement(step.icon as React.ReactElement, {
|
<h4 className="font-semibold text-slate-900">Add Keywords</h4>
|
||||||
className: 'w-8 h-8 flex-shrink-0'
|
<p className="text-xs text-slate-600">Discover new opportunities</p>
|
||||||
})}
|
</div>
|
||||||
</div>
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] transition" />
|
||||||
</div>
|
|
||||||
<div className="text-sm font-semibold text-gray-500 dark:text-gray-400 mb-1">
|
|
||||||
Step {step.id}
|
|
||||||
</div>
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white mb-2">
|
|
||||||
{step.title}
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
{step.description}
|
|
||||||
</p>
|
|
||||||
{index < workflowSteps.length - 1 && (
|
|
||||||
<div className="hidden xl:block absolute top-1/2 -right-2 transform -translate-y-1/2 translate-x-full">
|
|
||||||
<ArrowRightIcon className="size-6 text-gray-400" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Automation Setup */}
|
<Link
|
||||||
<div className="mb-8">
|
to="/writer/tasks"
|
||||||
<div className="flex items-center justify-between mb-6">
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0bbf87] hover:shadow-md transition group"
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
>
|
||||||
Automation Setup
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0bbf87] to-[#08966b] flex items-center justify-center text-white">
|
||||||
</h2>
|
<FileTextIcon className="h-6 w-6" />
|
||||||
<Link
|
|
||||||
to="/schedules"
|
|
||||||
className="text-brand-600 dark:text-brand-400 hover:text-brand-700 dark:hover:text-brand-300 font-medium inline-flex items-center gap-2"
|
|
||||||
>
|
|
||||||
Advanced Settings
|
|
||||||
<ArrowRightIcon className="size-4" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
{/* Keywords Automation */}
|
|
||||||
<Card className="p-6">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/20 flex items-center justify-center">
|
|
||||||
<ListIcon className="size-5 text-blue-600 dark:text-blue-400" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">
|
|
||||||
Keywords Automation
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="flex-1">
|
||||||
onClick={() => setAutomationSettings(prev => ({
|
<h4 className="font-semibold text-slate-900">Create Content</h4>
|
||||||
...prev,
|
<p className="text-xs text-slate-600">Generate new content</p>
|
||||||
keywords: { ...prev.keywords, enabled: !prev.keywords.enabled }
|
|
||||||
}))}
|
|
||||||
className={`w-12 h-6 rounded-full transition-colors ${
|
|
||||||
automationSettings.keywords.enabled ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 bg-white rounded-full transition-transform ${
|
|
||||||
automationSettings.keywords.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
|
||||||
}`}></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{automationSettings.keywords.enabled && (
|
|
||||||
<div className="space-y-3 mt-4">
|
|
||||||
<div>
|
|
||||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
||||||
Keywords per cycle
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={automationSettings.keywords.keywordsPerCycle}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
keywords: { ...prev.keywords, keywordsPerCycle: parseInt(e.target.value) || 0 }
|
|
||||||
}))}
|
|
||||||
className="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={automationSettings.keywords.autoCluster}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
keywords: { ...prev.keywords, autoCluster: e.target.checked }
|
|
||||||
}))}
|
|
||||||
className="rounded"
|
|
||||||
/>
|
|
||||||
<label className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
Auto-cluster keywords
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0bbf87] transition" />
|
||||||
</Card>
|
</Link>
|
||||||
|
|
||||||
{/* Ideas Automation */}
|
<Link
|
||||||
<Card className="p-6">
|
to="/automation"
|
||||||
<div className="flex items-center justify-between mb-4">
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#5d4ae3] hover:shadow-md transition group"
|
||||||
<div className="flex items-center gap-3">
|
>
|
||||||
<div className="w-10 h-10 rounded-lg bg-orange-100 dark:bg-orange-900/20 flex items-center justify-center">
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#5d4ae3] to-[#3a2f94] flex items-center justify-center text-white">
|
||||||
<BoltIcon className="size-5 text-orange-600 dark:text-orange-400" />
|
<PlugInIcon className="h-6 w-6" />
|
||||||
</div>
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">
|
|
||||||
Ideas Automation
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="flex-1">
|
||||||
onClick={() => setAutomationSettings(prev => ({
|
<h4 className="font-semibold text-slate-900">Setup Automation</h4>
|
||||||
...prev,
|
<p className="text-xs text-slate-600">Configure workflows</p>
|
||||||
ideas: { ...prev.ideas, enabled: !prev.ideas.enabled }
|
|
||||||
}))}
|
|
||||||
className={`w-12 h-6 rounded-full transition-colors ${
|
|
||||||
automationSettings.ideas.enabled ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 bg-white rounded-full transition-transform ${
|
|
||||||
automationSettings.ideas.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
|
||||||
}`}></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{automationSettings.ideas.enabled && (
|
|
||||||
<div className="space-y-3 mt-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={automationSettings.ideas.autoGenerate}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
ideas: { ...prev.ideas, autoGenerate: e.target.checked }
|
|
||||||
}))}
|
|
||||||
className="rounded"
|
|
||||||
/>
|
|
||||||
<label className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
Auto-generate ideas from clusters
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#5d4ae3] transition" />
|
||||||
</Card>
|
</Link>
|
||||||
|
|
||||||
{/* Content Automation */}
|
<Link
|
||||||
<Card className="p-6">
|
to="/thinker/prompts"
|
||||||
<div className="flex items-center justify-between mb-4">
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#ff7a00] hover:shadow-md transition group"
|
||||||
<div className="flex items-center gap-3">
|
>
|
||||||
<div className="w-10 h-10 rounded-lg bg-green-100 dark:bg-green-900/20 flex items-center justify-center">
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#ff7a00] to-[#cc5f00] flex items-center justify-center text-white">
|
||||||
<FileTextIcon className="size-5 text-green-600 dark:text-green-400" />
|
<BoltIcon className="h-6 w-6" />
|
||||||
</div>
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">
|
|
||||||
Content Automation
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="flex-1">
|
||||||
onClick={() => setAutomationSettings(prev => ({
|
<h4 className="font-semibold text-slate-900">Manage Prompts</h4>
|
||||||
...prev,
|
<p className="text-xs text-slate-600">Edit AI instructions</p>
|
||||||
content: { ...prev.content, enabled: !prev.content.enabled }
|
|
||||||
}))}
|
|
||||||
className={`w-12 h-6 rounded-full transition-colors ${
|
|
||||||
automationSettings.content.enabled ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 bg-white rounded-full transition-transform ${
|
|
||||||
automationSettings.content.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
|
||||||
}`}></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{automationSettings.content.enabled && (
|
|
||||||
<div className="space-y-3 mt-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={automationSettings.content.autoCreateTasks}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
content: { ...prev.content, autoCreateTasks: e.target.checked }
|
|
||||||
}))}
|
|
||||||
className="rounded"
|
|
||||||
/>
|
|
||||||
<label className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
Auto-create tasks from ideas
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={automationSettings.content.autoGenerateContent}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
content: { ...prev.content, autoGenerateContent: e.target.checked }
|
|
||||||
}))}
|
|
||||||
className="rounded"
|
|
||||||
/>
|
|
||||||
<label className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
Auto-generate content from tasks
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#ff7a00] transition" />
|
||||||
</Card>
|
</Link>
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
{/* Images Automation */}
|
{/* Credit Balance & Usage */}
|
||||||
<Card className="p-6">
|
<div className="grid grid-cols-12 gap-6">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="col-span-12 xl:col-span-4">
|
||||||
<div className="flex items-center gap-3">
|
<CreditBalanceWidget />
|
||||||
<div className="w-10 h-10 rounded-lg bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center">
|
</div>
|
||||||
<FileIcon className="size-5 text-purple-600 dark:text-purple-400" />
|
|
||||||
</div>
|
<div className="col-span-12 xl:col-span-8">
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">
|
<UsageChartWidget />
|
||||||
Images Automation
|
</div>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
images: { ...prev.images, enabled: !prev.images.enabled }
|
|
||||||
}))}
|
|
||||||
className={`w-12 h-6 rounded-full transition-colors ${
|
|
||||||
automationSettings.images.enabled ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 bg-white rounded-full transition-transform ${
|
|
||||||
automationSettings.images.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
|
||||||
}`}></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{automationSettings.images.enabled && (
|
|
||||||
<div className="space-y-3 mt-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={automationSettings.images.autoGenerate}
|
|
||||||
onChange={(e) => setAutomationSettings(prev => ({
|
|
||||||
...prev,
|
|
||||||
images: { ...prev.images, autoGenerate: e.target.checked }
|
|
||||||
}))}
|
|
||||||
className="rounded"
|
|
||||||
/>
|
|
||||||
<label className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
Auto-generate images for content
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Credit Balance & Usage (Existing Widgets) */}
|
|
||||||
<div className="grid grid-cols-12 gap-4 md:gap-6">
|
|
||||||
<div className="col-span-12 xl:col-span-4">
|
|
||||||
<CreditBalanceWidget />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-12 xl:col-span-8">
|
|
||||||
<UsageChartWidget />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -701,6 +701,135 @@ export default function PlannerDashboard() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Planner Modules */}
|
||||||
|
<ComponentCard title="Planner Modules" desc="Access all planning tools and features">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
title: "Keywords",
|
||||||
|
description: "Manage and discover keywords",
|
||||||
|
icon: ListIcon,
|
||||||
|
color: "from-[#0693e3] to-[#0472b8]",
|
||||||
|
path: "/planner/keywords",
|
||||||
|
count: stats.keywords.total,
|
||||||
|
metric: `${stats.keywords.mapped} mapped`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Clusters",
|
||||||
|
description: "Keyword clusters and groups",
|
||||||
|
icon: GroupIcon,
|
||||||
|
color: "from-[#0bbf87] to-[#08966b]",
|
||||||
|
path: "/planner/clusters",
|
||||||
|
count: stats.clusters.total,
|
||||||
|
metric: `${stats.clusters.totalVolume.toLocaleString()} volume`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Ideas",
|
||||||
|
description: "Content ideas and concepts",
|
||||||
|
icon: BoltIcon,
|
||||||
|
color: "from-[#ff7a00] to-[#cc5f00]",
|
||||||
|
path: "/planner/ideas",
|
||||||
|
count: stats.ideas.total,
|
||||||
|
metric: `${stats.ideas.queued} queued`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Keyword Opportunities",
|
||||||
|
description: "Discover new keyword opportunities",
|
||||||
|
icon: PieChartIcon,
|
||||||
|
color: "from-[#5d4ae3] to-[#3a2f94]",
|
||||||
|
path: "/planner/keyword-opportunities",
|
||||||
|
count: 0,
|
||||||
|
metric: "Discover new keywords",
|
||||||
|
},
|
||||||
|
].map((module) => {
|
||||||
|
const Icon = module.icon;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={module.title}
|
||||||
|
to={module.path}
|
||||||
|
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||||
|
<Icon className="h-7 w-7" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||||
|
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-2xl font-bold text-slate-900">{module.count}</div>
|
||||||
|
<div className="text-xs text-slate-500">{module.metric}</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] group-hover:translate-x-1 transition" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
|
{/* Quick Actions */}
|
||||||
|
<ComponentCard title="Quick Actions" desc="Common planning tasks and shortcuts">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
<Link
|
||||||
|
to="/planner/keyword-opportunities"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0693e3] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0693e3] to-[#0472b8] flex items-center justify-center text-white">
|
||||||
|
<ListIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Add Keywords</h4>
|
||||||
|
<p className="text-xs text-slate-600">Discover opportunities</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/planner/clusters"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0bbf87] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0bbf87] to-[#08966b] flex items-center justify-center text-white">
|
||||||
|
<GroupIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Auto Cluster</h4>
|
||||||
|
<p className="text-xs text-slate-600">Group keywords</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0bbf87] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/planner/ideas"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#ff7a00] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#ff7a00] to-[#cc5f00] flex items-center justify-center text-white">
|
||||||
|
<BoltIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Generate Ideas</h4>
|
||||||
|
<p className="text-xs text-slate-600">Create content ideas</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#ff7a00] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/automation"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#5d4ae3] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#5d4ae3] to-[#3a2f94] flex items-center justify-center text-white">
|
||||||
|
<PieChartIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Setup Automation</h4>
|
||||||
|
<p className="text-xs text-slate-600">Automate workflows</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#5d4ae3] transition" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
{/* Next Actions */}
|
{/* Next Actions */}
|
||||||
{nextActions.length > 0 && (
|
{nextActions.length > 0 && (
|
||||||
<ComponentCard title="Next Actions" desc="Actionable items requiring attention">
|
<ComponentCard title="Next Actions" desc="Actionable items requiring attention">
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
BoltIcon,
|
BoltIcon,
|
||||||
ArrowUpIcon,
|
ArrowUpIcon,
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
ArrowRightIcon
|
ArrowRightIcon,
|
||||||
|
PaperPlaneIcon
|
||||||
} from "../../icons";
|
} from "../../icons";
|
||||||
import {
|
import {
|
||||||
fetchTasks,
|
fetchTasks,
|
||||||
@@ -827,6 +828,135 @@ export default function WriterDashboard() {
|
|||||||
</ComponentCard>
|
</ComponentCard>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Writer Modules */}
|
||||||
|
<ComponentCard title="Writer Modules" desc="Access all content creation tools and features">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
title: "Tasks",
|
||||||
|
description: "Content writing tasks and assignments",
|
||||||
|
icon: FileTextIcon,
|
||||||
|
color: "from-[#0693e3] to-[#0472b8]",
|
||||||
|
path: "/writer/tasks",
|
||||||
|
count: stats.tasks.total,
|
||||||
|
metric: `${stats.tasks.completed} completed`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Content",
|
||||||
|
description: "Generated content and drafts",
|
||||||
|
icon: PencilIcon,
|
||||||
|
color: "from-[#0bbf87] to-[#08966b]",
|
||||||
|
path: "/writer/content",
|
||||||
|
count: stats.content.total,
|
||||||
|
metric: `${stats.content.published} published`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Images",
|
||||||
|
description: "Generated images and assets",
|
||||||
|
icon: BoxIcon,
|
||||||
|
color: "from-[#ff7a00] to-[#cc5f00]",
|
||||||
|
path: "/writer/images",
|
||||||
|
count: stats.images.generated,
|
||||||
|
metric: `${stats.images.pending} pending`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Published",
|
||||||
|
description: "Published content and posts",
|
||||||
|
icon: PaperPlaneIcon,
|
||||||
|
color: "from-[#5d4ae3] to-[#3a2f94]",
|
||||||
|
path: "/writer/published",
|
||||||
|
count: stats.content.published,
|
||||||
|
metric: "View all published",
|
||||||
|
},
|
||||||
|
].map((module) => {
|
||||||
|
const Icon = module.icon;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={module.title}
|
||||||
|
to={module.path}
|
||||||
|
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||||
|
<Icon className="h-7 w-7" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||||
|
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-2xl font-bold text-slate-900">{module.count}</div>
|
||||||
|
<div className="text-xs text-slate-500">{module.metric}</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] group-hover:translate-x-1 transition" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
|
{/* Quick Actions */}
|
||||||
|
<ComponentCard title="Quick Actions" desc="Common content creation tasks and shortcuts">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
<Link
|
||||||
|
to="/writer/tasks"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0693e3] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0693e3] to-[#0472b8] flex items-center justify-center text-white">
|
||||||
|
<FileTextIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Create Task</h4>
|
||||||
|
<p className="text-xs text-slate-600">New writing task</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0693e3] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/writer/content"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#0bbf87] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#0bbf87] to-[#08966b] flex items-center justify-center text-white">
|
||||||
|
<PencilIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Generate Content</h4>
|
||||||
|
<p className="text-xs text-slate-600">AI content creation</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0bbf87] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/writer/images"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#ff7a00] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#ff7a00] to-[#cc5f00] flex items-center justify-center text-white">
|
||||||
|
<BoxIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Generate Images</h4>
|
||||||
|
<p className="text-xs text-slate-600">Create visuals</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#ff7a00] transition" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/writer/published"
|
||||||
|
className="flex items-center gap-4 p-4 rounded-lg border-2 border-slate-200 bg-white hover:border-[#5d4ae3] hover:shadow-md transition group"
|
||||||
|
>
|
||||||
|
<div className="size-12 rounded-lg bg-gradient-to-br from-[#5d4ae3] to-[#3a2f94] flex items-center justify-center text-white">
|
||||||
|
<PaperPlaneIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-slate-900">Publish Content</h4>
|
||||||
|
<p className="text-xs text-slate-600">Publish to WordPress</p>
|
||||||
|
</div>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#5d4ae3] transition" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</ComponentCard>
|
||||||
|
|
||||||
{/* Next Actions */}
|
{/* Next Actions */}
|
||||||
{nextActions.length > 0 && (
|
{nextActions.length > 0 && (
|
||||||
<ComponentCard title="Next Actions" desc="Actionable items requiring attention">
|
<ComponentCard title="Next Actions" desc="Actionable items requiring attention">
|
||||||
|
|||||||
Reference in New Issue
Block a user