frontend-refactor-1
This commit is contained in:
@@ -161,14 +161,8 @@ export default function App() {
|
||||
</Suspense>
|
||||
} />
|
||||
|
||||
{/* Planner Module */}
|
||||
<Route path="/planner" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="planner">
|
||||
<PlannerDashboard />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
{/* Planner Module - Redirect dashboard to keywords */}
|
||||
<Route path="/planner" element={<Navigate to="/planner/keywords" replace />} />
|
||||
<Route path="/planner/keywords" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="planner">
|
||||
@@ -191,14 +185,8 @@ export default function App() {
|
||||
</Suspense>
|
||||
} />
|
||||
|
||||
{/* Writer Module */}
|
||||
<Route path="/writer" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="writer">
|
||||
<WriterDashboard />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
{/* Writer Module - Redirect dashboard to content */}
|
||||
<Route path="/writer" element={<Navigate to="/writer/content" replace />} />
|
||||
<Route path="/writer/tasks" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="writer">
|
||||
@@ -238,14 +226,8 @@ export default function App() {
|
||||
</Suspense>
|
||||
} />
|
||||
|
||||
{/* Linker Module */}
|
||||
<Route path="/linker" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="linker">
|
||||
<LinkerDashboard />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
{/* Linker Module - Redirect dashboard to content */}
|
||||
<Route path="/linker" element={<Navigate to="/linker/content" replace />} />
|
||||
<Route path="/linker/content" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="linker">
|
||||
@@ -254,14 +236,8 @@ export default function App() {
|
||||
</Suspense>
|
||||
} />
|
||||
|
||||
{/* Optimizer Module */}
|
||||
<Route path="/optimizer" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="optimizer">
|
||||
<OptimizerDashboard />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
{/* Optimizer Module - Redirect dashboard to content */}
|
||||
<Route path="/optimizer" element={<Navigate to="/optimizer/content" replace />} />
|
||||
<Route path="/optimizer/content" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="optimizer">
|
||||
@@ -355,14 +331,8 @@ export default function App() {
|
||||
</Suspense>
|
||||
} />
|
||||
|
||||
{/* Other Pages */}
|
||||
<Route path="/automation" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="automation">
|
||||
<AutomationDashboard />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
{/* Automation Module - Redirect dashboard to rules */}
|
||||
<Route path="/automation" element={<Navigate to="/automation/rules" replace />} />
|
||||
<Route path="/automation/rules" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="automation">
|
||||
|
||||
85
frontend/src/components/dashboard/ModuleMetricsFooter.tsx
Normal file
85
frontend/src/components/dashboard/ModuleMetricsFooter.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* ModuleMetricsFooter - Compact metrics footer for table pages
|
||||
* Shows module-specific metrics at the bottom of table pages
|
||||
* Uses standard EnhancedMetricCard and ProgressBar components
|
||||
* Follows standard app design system and color scheme
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import EnhancedMetricCard, { MetricCardProps } from './EnhancedMetricCard';
|
||||
import { ProgressBar } from '../ui/progress';
|
||||
|
||||
export interface MetricItem {
|
||||
title: string;
|
||||
value: string | number;
|
||||
subtitle?: string;
|
||||
icon: React.ReactNode;
|
||||
accentColor: MetricCardProps['accentColor'];
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export interface ProgressMetric {
|
||||
label: string;
|
||||
value: number; // 0-100
|
||||
color?: 'primary' | 'success' | 'warning' | 'purple';
|
||||
}
|
||||
|
||||
interface ModuleMetricsFooterProps {
|
||||
metrics: MetricItem[];
|
||||
progress?: ProgressMetric;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function ModuleMetricsFooter({
|
||||
metrics,
|
||||
progress,
|
||||
className = ''
|
||||
}: ModuleMetricsFooterProps) {
|
||||
if (metrics.length === 0 && !progress) return null;
|
||||
|
||||
const progressColors = {
|
||||
primary: 'bg-[var(--color-primary)]',
|
||||
success: 'bg-[var(--color-success)]',
|
||||
warning: 'bg-[var(--color-warning)]',
|
||||
purple: 'bg-[var(--color-purple)]',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`mt-8 pt-6 border-t border-gray-200 dark:border-gray-800 ${className}`}>
|
||||
<div className="space-y-4">
|
||||
{/* Metrics Grid */}
|
||||
{metrics.length > 0 && (
|
||||
<div className={`grid grid-cols-1 sm:grid-cols-2 ${metrics.length > 2 ? 'lg:grid-cols-3' : 'lg:grid-cols-2'} ${metrics.length > 3 ? 'xl:grid-cols-4' : ''} gap-4`}>
|
||||
{metrics.map((metric, index) => (
|
||||
<EnhancedMetricCard
|
||||
key={index}
|
||||
title={metric.title}
|
||||
value={metric.value}
|
||||
subtitle={metric.subtitle}
|
||||
icon={metric.icon}
|
||||
accentColor={metric.accentColor}
|
||||
href={metric.href}
|
||||
onClick={metric.onClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Progress Bar */}
|
||||
{progress && (
|
||||
<div className="space-y-2">
|
||||
<ProgressBar
|
||||
value={progress.value}
|
||||
color={progress.color === 'success' ? 'success' : progress.color === 'warning' ? 'warning' : 'primary'}
|
||||
size="md"
|
||||
showLabel={true}
|
||||
label={progress.label}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
66
frontend/src/components/navigation/ModuleNavigationTabs.tsx
Normal file
66
frontend/src/components/navigation/ModuleNavigationTabs.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* ModuleNavigationTabs - Reusable in-page tab navigation component
|
||||
* Used across Planner, Writer, Linker, Optimizer, Thinker, Automation, Sites modules
|
||||
* Follows standard app design system and color scheme
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router';
|
||||
import { Tabs, TabList, Tab } from '../ui/tabs/Tabs';
|
||||
|
||||
export interface NavigationTab {
|
||||
label: string;
|
||||
path: string;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ModuleNavigationTabsProps {
|
||||
tabs: NavigationTab[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function ModuleNavigationTabs({ tabs, className = '' }: ModuleNavigationTabsProps) {
|
||||
const location = useLocation();
|
||||
|
||||
// Find active tab based on current path
|
||||
const activeTab = tabs.find(tab => {
|
||||
if (tab.path === location.pathname) return true;
|
||||
// Handle nested routes (e.g., /planner/keywords/123 should match /planner/keywords)
|
||||
return location.pathname.startsWith(tab.path + '/');
|
||||
});
|
||||
|
||||
const activeTabId = activeTab?.path || tabs[0]?.path || '';
|
||||
|
||||
return (
|
||||
<div className={`mb-6 ${className}`}>
|
||||
<Tabs defaultTab={activeTabId}>
|
||||
{(activeTabId, setActiveTab) => (
|
||||
<TabList className="bg-gray-100 dark:bg-gray-900">
|
||||
{tabs.map((tab) => {
|
||||
const isActive = activeTabId === tab.path || location.pathname.startsWith(tab.path + '/');
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={tab.path}
|
||||
to={tab.path}
|
||||
onClick={() => setActiveTab(tab.path)}
|
||||
className="flex-1"
|
||||
>
|
||||
<Tab
|
||||
tabId={tab.path}
|
||||
isActive={isActive}
|
||||
className="flex items-center justify-center gap-2"
|
||||
>
|
||||
{tab.icon && <span className="flex-shrink-0">{tab.icon}</span>}
|
||||
<span>{tab.label}</span>
|
||||
</Tab>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</TabList>
|
||||
)}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,132 +78,102 @@ const AppSidebar: React.FC = () => {
|
||||
|
||||
// Define menu sections with useMemo to prevent recreation on every render
|
||||
// Filter out disabled modules based on module enable settings
|
||||
// New structure: Dashboard (standalone) → SETUP → WORKFLOW → SETTINGS
|
||||
const menuSections: MenuSection[] = useMemo(() => {
|
||||
const workflowItems: NavItem[] = [
|
||||
// SETUP section items (single items, no dropdowns - submenus shown as in-page navigation)
|
||||
const setupItems: NavItem[] = [
|
||||
{
|
||||
icon: <PlugInIcon />,
|
||||
name: "Setup",
|
||||
subItems: [
|
||||
{ name: "Keywords Opportunities", path: "/planner/keyword-opportunities" },
|
||||
],
|
||||
icon: <DocsIcon />,
|
||||
name: "Industry, Sectors & Keywords",
|
||||
path: "/setup/industries-sectors-keywords", // Merged page
|
||||
},
|
||||
{
|
||||
icon: <GridIcon />,
|
||||
name: "Sites",
|
||||
path: "/sites", // Submenus shown as in-page navigation
|
||||
},
|
||||
];
|
||||
|
||||
// Add Automation if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('automation')) {
|
||||
setupItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Automation",
|
||||
path: "/automation/rules", // Default to rules, submenus shown as in-page navigation
|
||||
});
|
||||
}
|
||||
|
||||
// Add Thinker if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('thinker')) {
|
||||
setupItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Thinker",
|
||||
path: "/thinker/prompts", // Default to prompts, submenus shown as in-page navigation
|
||||
});
|
||||
}
|
||||
|
||||
// WORKFLOW section items (single items, no dropdowns - submenus shown as in-page navigation)
|
||||
const workflowItems: NavItem[] = [];
|
||||
|
||||
// Add Planner if enabled
|
||||
// Add Planner if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('planner')) {
|
||||
workflowItems.push({
|
||||
icon: <ListIcon />,
|
||||
name: "Planner",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/planner" },
|
||||
{ name: "Keywords", path: "/planner/keywords" },
|
||||
{ name: "Clusters", path: "/planner/clusters" },
|
||||
{ name: "Ideas", path: "/planner/ideas" },
|
||||
],
|
||||
path: "/planner/keywords", // Default to keywords, submenus shown as in-page navigation
|
||||
});
|
||||
}
|
||||
|
||||
// Add Writer if enabled
|
||||
// Add Writer if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('writer')) {
|
||||
workflowItems.push({
|
||||
icon: <TaskIcon />,
|
||||
name: "Writer",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/writer" },
|
||||
{ name: "Tasks", path: "/writer/tasks" },
|
||||
{ name: "Content", path: "/writer/content" },
|
||||
{ name: "Images", path: "/writer/images" },
|
||||
{ name: "Published", path: "/writer/published" },
|
||||
],
|
||||
path: "/writer/content", // Default to content, submenus shown as in-page navigation
|
||||
});
|
||||
}
|
||||
|
||||
// Add Thinker if enabled
|
||||
if (moduleEnabled('thinker')) {
|
||||
workflowItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Thinker",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/thinker" },
|
||||
{ name: "Prompts", path: "/thinker/prompts" },
|
||||
{ name: "Author Profiles", path: "/thinker/author-profiles" },
|
||||
{ name: "Strategies", path: "/thinker/strategies" },
|
||||
{ name: "Image Testing", path: "/thinker/image-testing" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Add Linker if enabled
|
||||
// Add Linker if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('linker')) {
|
||||
workflowItems.push({
|
||||
icon: <PlugInIcon />,
|
||||
name: "Linker",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/linker" },
|
||||
{ name: "Content", path: "/linker/content" },
|
||||
],
|
||||
path: "/linker/content",
|
||||
});
|
||||
}
|
||||
|
||||
// Add Optimizer if enabled
|
||||
// Add Optimizer if enabled (single item, no dropdown)
|
||||
if (moduleEnabled('optimizer')) {
|
||||
workflowItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Optimizer",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/optimizer" },
|
||||
{ name: "Content", path: "/optimizer/content" },
|
||||
],
|
||||
path: "/optimizer/content",
|
||||
});
|
||||
}
|
||||
|
||||
// Add Automation if enabled
|
||||
if (moduleEnabled('automation')) {
|
||||
workflowItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Automation",
|
||||
subItems: [
|
||||
{ name: "Dashboard", path: "/automation" },
|
||||
{ name: "Rules", path: "/automation/rules" },
|
||||
{ name: "Tasks", path: "/automation/tasks" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Add Sites menu (always available)
|
||||
workflowItems.push({
|
||||
icon: <GridIcon />,
|
||||
name: "Sites",
|
||||
subItems: [
|
||||
{ name: "All Sites", path: "/sites" },
|
||||
{ name: "Create Site", path: "/sites/builder" },
|
||||
{ name: "Blueprints", path: "/sites/blueprints" },
|
||||
],
|
||||
});
|
||||
|
||||
return [
|
||||
// Dashboard is standalone (no section header)
|
||||
{
|
||||
label: "OVERVIEW",
|
||||
label: "", // Empty label for standalone Dashboard
|
||||
items: [
|
||||
{
|
||||
icon: <GridIcon />,
|
||||
name: "Dashboard",
|
||||
path: "/",
|
||||
},
|
||||
{
|
||||
icon: <DocsIcon />,
|
||||
name: "Industry / Sectors",
|
||||
path: "/reference/industries",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "WORKFLOWS",
|
||||
label: "SETUP",
|
||||
items: setupItems,
|
||||
},
|
||||
{
|
||||
label: "WORKFLOW",
|
||||
items: workflowItems,
|
||||
},
|
||||
{
|
||||
label: "ACCOUNT & SETTINGS",
|
||||
items: [
|
||||
{
|
||||
label: "SETTINGS",
|
||||
items: [
|
||||
{
|
||||
icon: <PlugInIcon />,
|
||||
name: "Settings",
|
||||
@@ -215,28 +185,23 @@ const AppSidebar: React.FC = () => {
|
||||
{ name: "Import / Export", path: "/settings/import-export" },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: <DollarLineIcon />,
|
||||
name: "Billing",
|
||||
subItems: [
|
||||
{ name: "Credits", path: "/billing/credits" },
|
||||
{ name: "Transactions", path: "/billing/transactions" },
|
||||
{ name: "Usage", path: "/billing/usage" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "HELP",
|
||||
items: [
|
||||
{
|
||||
icon: <DocsIcon />,
|
||||
name: "Help & Documentation",
|
||||
path: "/help",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
{
|
||||
icon: <DollarLineIcon />,
|
||||
name: "Billing",
|
||||
subItems: [
|
||||
{ name: "Credits", path: "/billing/credits" },
|
||||
{ name: "Transactions", path: "/billing/transactions" },
|
||||
{ name: "Usage", path: "/billing/usage" },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: <DocsIcon />,
|
||||
name: "Help & Documentation",
|
||||
path: "/help",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}, [moduleEnabled]);
|
||||
|
||||
// Admin section - only shown for users in aws-admin account
|
||||
@@ -585,20 +550,22 @@ const AppSidebar: React.FC = () => {
|
||||
<nav className="mb-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
{allSections.map((section, sectionIndex) => (
|
||||
<div key={section.label}>
|
||||
<h2
|
||||
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
||||
!isExpanded && !isHovered
|
||||
? "lg:justify-center"
|
||||
: "justify-start"
|
||||
}`}
|
||||
>
|
||||
{isExpanded || isHovered || isMobileOpen ? (
|
||||
section.label
|
||||
) : (
|
||||
<HorizontaLDots className="size-6" />
|
||||
)}
|
||||
</h2>
|
||||
<div key={section.label || `section-${sectionIndex}`}>
|
||||
{section.label && (
|
||||
<h2
|
||||
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
||||
!isExpanded && !isHovered
|
||||
? "lg:justify-center"
|
||||
: "justify-start"
|
||||
}`}
|
||||
>
|
||||
{isExpanded || isHovered || isMobileOpen ? (
|
||||
section.label
|
||||
) : (
|
||||
<HorizontaLDots className="size-6" />
|
||||
)}
|
||||
</h2>
|
||||
)}
|
||||
{renderMenuItems(section.items, sectionIndex)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -5,9 +5,10 @@ import PageHeader from '../../components/common/PageHeader';
|
||||
import ComponentCard from '../../components/common/ComponentCard';
|
||||
import { automationApi, AutomationRule } from '../../api/automation.api';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { BoltIcon, PlusIcon, TrashBinIcon, PencilIcon, PaperPlaneIcon, CloseIcon } from '../../icons';
|
||||
import { BoltIcon, PlusIcon, TrashBinIcon, PencilIcon, PaperPlaneIcon, CloseIcon, TaskIcon, ClockIcon } from '../../icons';
|
||||
import { useSiteStore } from '../../store/siteStore';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function AutomationRules() {
|
||||
const navigate = useNavigate();
|
||||
@@ -109,6 +110,12 @@ export default function AutomationRules() {
|
||||
);
|
||||
};
|
||||
|
||||
// Automation navigation tabs
|
||||
const automationTabs = [
|
||||
{ label: 'Rules', path: '/automation/rules', icon: <BoltIcon /> },
|
||||
{ label: 'Tasks', path: '/automation/tasks', icon: <ClockIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageMeta title="Automation Rules" />
|
||||
|
||||
@@ -4,9 +4,10 @@ import PageHeader from '../../components/common/PageHeader';
|
||||
import ComponentCard from '../../components/common/ComponentCard';
|
||||
import { automationApi, ScheduledTask } from '../../api/automation.api';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { ClockIcon, CheckCircleIcon, XCircleIcon, ArrowRightIcon } from '../../icons';
|
||||
import { ClockIcon, CheckCircleIcon, XCircleIcon, ArrowRightIcon, BoltIcon } from '../../icons';
|
||||
import { useSiteStore } from '../../store/siteStore';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function AutomationTasks() {
|
||||
const toast = useToast();
|
||||
@@ -95,6 +96,12 @@ export default function AutomationTasks() {
|
||||
return true;
|
||||
});
|
||||
|
||||
// Automation navigation tabs
|
||||
const automationTabs = [
|
||||
{ label: 'Rules', path: '/automation/rules', icon: <BoltIcon /> },
|
||||
{ label: 'Tasks', path: '/automation/tasks', icon: <ClockIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageMeta title="Scheduled Tasks" />
|
||||
@@ -107,6 +114,7 @@ export default function AutomationTasks() {
|
||||
color: 'blue',
|
||||
}}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={automationTabs} />
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -21,12 +21,13 @@ import FormModal from '../../components/common/FormModal';
|
||||
import ProgressModal from '../../components/common/ProgressModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { GroupIcon, PlusIcon, DownloadIcon } from '../../icons';
|
||||
import { GroupIcon, PlusIcon, DownloadIcon, ListIcon, BoltIcon } from '../../icons';
|
||||
import { createClustersPageConfig } from '../../config/pages/clusters.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function Clusters() {
|
||||
const toast = useToast();
|
||||
@@ -383,12 +384,20 @@ export default function Clusters() {
|
||||
}
|
||||
};
|
||||
|
||||
// Planner navigation tabs
|
||||
const plannerTabs = [
|
||||
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
|
||||
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
|
||||
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Keyword Clusters"
|
||||
badge={{ icon: <GroupIcon />, color: 'purple' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={clusters}
|
||||
|
||||
@@ -23,11 +23,12 @@ import FormModal from '../../components/common/FormModal';
|
||||
import ProgressModal from '../../components/common/ProgressModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { BoltIcon, PlusIcon, DownloadIcon } from '../../icons';
|
||||
import { BoltIcon, PlusIcon, DownloadIcon, ListIcon, GroupIcon } from '../../icons';
|
||||
import { createIdeasPageConfig } from '../../config/pages/ideas.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function Ideas() {
|
||||
const toast = useToast();
|
||||
@@ -295,12 +296,20 @@ export default function Ideas() {
|
||||
}
|
||||
};
|
||||
|
||||
// Planner navigation tabs
|
||||
const plannerTabs = [
|
||||
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
|
||||
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
|
||||
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Content Ideas"
|
||||
badge={{ icon: <BoltIcon />, color: 'orange' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={ideas}
|
||||
|
||||
@@ -27,13 +27,14 @@ import { useSiteStore } from '../../store/siteStore';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
||||
import FormModal from '../../components/common/FormModal';
|
||||
import ProgressModal from '../../components/common/ProgressModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { ArrowUpIcon, PlusIcon, ListIcon, DownloadIcon } from '../../icons';
|
||||
import { ArrowUpIcon, PlusIcon, ListIcon, DownloadIcon, GroupIcon, BoltIcon } from '../../icons';
|
||||
import { useKeywordsImportExport } from '../../config/import-export.config';
|
||||
import { createKeywordsPageConfig } from '../../config/pages/keywords.config';
|
||||
|
||||
@@ -750,12 +751,20 @@ export default function Keywords() {
|
||||
setIsModalOpen(true);
|
||||
}, []);
|
||||
|
||||
// Planner navigation tabs
|
||||
const plannerTabs = [
|
||||
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
|
||||
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
|
||||
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Keywords"
|
||||
badge={{ icon: <ListIcon />, color: 'green' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={keywords}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { fetchAuthorProfiles, createAuthorProfile, updateAuthorProfile, deleteAuthorProfile, AuthorProfile } from '../../services/api';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import FormModal, { FormField } from '../../components/common/FormModal';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { PlusIcon } from '../../icons';
|
||||
import { PlusIcon, BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
|
||||
|
||||
export default function AuthorProfiles() {
|
||||
const toast = useToast();
|
||||
@@ -97,14 +99,23 @@ export default function AuthorProfiles() {
|
||||
{ name: 'is_active', label: 'Active', type: 'checkbox', required: false },
|
||||
];
|
||||
|
||||
// Thinker navigation tabs
|
||||
const thinkerTabs = [
|
||||
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
|
||||
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
|
||||
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
|
||||
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<PageMeta title="Author Profiles" />
|
||||
<PageHeader
|
||||
title="Author Profiles"
|
||||
badge={{ icon: <UserIcon />, color: 'blue' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={thinkerTabs} />
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Author Profiles</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">Manage writing style profiles</p>
|
||||
</div>
|
||||
<Button onClick={handleCreate} variant="primary">
|
||||
<PlusIcon className="w-4 h-4 mr-2" />
|
||||
Create Profile
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
import PageMeta from "../../components/common/PageMeta";
|
||||
import PageHeader from "../../components/common/PageHeader";
|
||||
import ModuleNavigationTabs from "../../components/navigation/ModuleNavigationTabs";
|
||||
import ComponentCard from "../../components/common/ComponentCard";
|
||||
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
|
||||
|
||||
export default function ImageTesting() {
|
||||
// Thinker navigation tabs
|
||||
const thinkerTabs = [
|
||||
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
|
||||
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
|
||||
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
|
||||
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageMeta title="Image Testing - IGNY8" description="AI image testing" />
|
||||
|
||||
<PageHeader
|
||||
title="Image Testing"
|
||||
badge={{ icon: <ImageIcon />, color: 'indigo' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={thinkerTabs} />
|
||||
<ComponentCard title="Coming Soon" desc="AI image testing">
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import PageMeta from '../../components/common/PageMeta';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import TextArea from '../../components/form/input/TextArea';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { BoltIcon } from '../../icons';
|
||||
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
|
||||
import { fetchAPI } from '../../services/api';
|
||||
|
||||
interface PromptData {
|
||||
@@ -197,23 +199,23 @@ export default function Prompts() {
|
||||
);
|
||||
}
|
||||
|
||||
// Thinker navigation tabs
|
||||
const thinkerTabs = [
|
||||
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
|
||||
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
|
||||
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
|
||||
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageMeta title="Prompts - IGNY8" description="AI prompts management" />
|
||||
|
||||
<PageHeader
|
||||
title="AI Prompts Management"
|
||||
badge={{ icon: <BoltIcon />, color: 'orange' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={thinkerTabs} />
|
||||
<div className="p-6">
|
||||
{/* Page Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<BoltIcon className="text-primary-500 size-6" />
|
||||
<h1 className="text-2xl font-semibold text-gray-800 dark:text-white">
|
||||
AI Prompts Management
|
||||
</h1>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Configure AI prompt templates for clustering, idea generation, content writing, and image generation
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Planner Prompts Section */}
|
||||
<div className="mb-8">
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
import PageMeta from "../../components/common/PageMeta";
|
||||
import PageHeader from "../../components/common/PageHeader";
|
||||
import ModuleNavigationTabs from "../../components/navigation/ModuleNavigationTabs";
|
||||
import ComponentCard from "../../components/common/ComponentCard";
|
||||
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
|
||||
|
||||
export default function Strategies() {
|
||||
// Thinker navigation tabs
|
||||
const thinkerTabs = [
|
||||
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
|
||||
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
|
||||
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
|
||||
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageMeta title="Strategies - IGNY8" description="Content strategies" />
|
||||
|
||||
<PageHeader
|
||||
title="Content Strategies"
|
||||
badge={{ icon: <ShootingStarIcon />, color: 'purple' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={thinkerTabs} />
|
||||
<ComponentCard title="Coming Soon" desc="Content strategies">
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
|
||||
@@ -14,13 +14,14 @@ import {
|
||||
import { optimizerApi } from '../../api/optimizer.api';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { FileIcon } from '../../icons';
|
||||
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||
import { createContentPageConfig } from '../../config/pages/content.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import ProgressModal from '../../components/common/ProgressModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function Content() {
|
||||
const toast = useToast();
|
||||
@@ -197,12 +198,21 @@ export default function Content() {
|
||||
}
|
||||
}, [toast, progressModal, loadContent, navigate]);
|
||||
|
||||
// Writer navigation tabs
|
||||
const writerTabs = [
|
||||
{ label: 'Tasks', path: '/writer/tasks', icon: <TaskIcon /> },
|
||||
{ label: 'Content', path: '/writer/content', icon: <FileIcon /> },
|
||||
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
|
||||
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Content"
|
||||
badge={{ icon: <FileIcon />, color: 'purple' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={writerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={content}
|
||||
|
||||
@@ -15,12 +15,13 @@ import {
|
||||
ContentImage,
|
||||
} from '../../services/api';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
|
||||
import { FileIcon, DownloadIcon, BoltIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||
import { createImagesPageConfig } from '../../config/pages/images.config';
|
||||
import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQueueModal';
|
||||
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
|
||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import { Modal } from '../../components/ui/modal';
|
||||
|
||||
export default function Images() {
|
||||
@@ -460,12 +461,21 @@ export default function Images() {
|
||||
}));
|
||||
}, [pageConfig?.headerMetrics, images, totalCount]);
|
||||
|
||||
// Writer navigation tabs
|
||||
const writerTabs = [
|
||||
{ label: 'Tasks', path: '/writer/tasks', icon: <TaskIcon /> },
|
||||
{ label: 'Content', path: '/writer/content', icon: <FileIcon /> },
|
||||
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
|
||||
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Content Images"
|
||||
badge={{ icon: <FileIcon />, color: 'orange' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={writerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={images}
|
||||
|
||||
@@ -25,11 +25,12 @@ import ProgressModal from '../../components/common/ProgressModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { TaskIcon, PlusIcon, DownloadIcon } from '../../icons';
|
||||
import { TaskIcon, PlusIcon, DownloadIcon, FileIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||
import { createTasksPageConfig } from '../../config/pages/tasks.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
|
||||
export default function Tasks() {
|
||||
const toast = useToast();
|
||||
@@ -559,12 +560,21 @@ export default function Tasks() {
|
||||
}
|
||||
};
|
||||
|
||||
// Writer navigation tabs
|
||||
const writerTabs = [
|
||||
{ label: 'Tasks', path: '/writer/tasks', icon: <TaskIcon /> },
|
||||
{ label: 'Content', path: '/writer/content', icon: <FileIcon /> },
|
||||
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
|
||||
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Tasks"
|
||||
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
||||
/>
|
||||
<ModuleNavigationTabs tabs={writerTabs} />
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
data={tasks}
|
||||
|
||||
Reference in New Issue
Block a user