frontend-refactor-1
This commit is contained in:
@@ -161,14 +161,8 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Planner Module */}
|
{/* Planner Module - Redirect dashboard to keywords */}
|
||||||
<Route path="/planner" element={
|
<Route path="/planner" element={<Navigate to="/planner/keywords" replace />} />
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ModuleGuard module="planner">
|
|
||||||
<PlannerDashboard />
|
|
||||||
</ModuleGuard>
|
|
||||||
</Suspense>
|
|
||||||
} />
|
|
||||||
<Route path="/planner/keywords" element={
|
<Route path="/planner/keywords" element={
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ModuleGuard module="planner">
|
<ModuleGuard module="planner">
|
||||||
@@ -191,14 +185,8 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Writer Module */}
|
{/* Writer Module - Redirect dashboard to content */}
|
||||||
<Route path="/writer" element={
|
<Route path="/writer" element={<Navigate to="/writer/content" replace />} />
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ModuleGuard module="writer">
|
|
||||||
<WriterDashboard />
|
|
||||||
</ModuleGuard>
|
|
||||||
</Suspense>
|
|
||||||
} />
|
|
||||||
<Route path="/writer/tasks" element={
|
<Route path="/writer/tasks" element={
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ModuleGuard module="writer">
|
<ModuleGuard module="writer">
|
||||||
@@ -238,14 +226,8 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Linker Module */}
|
{/* Linker Module - Redirect dashboard to content */}
|
||||||
<Route path="/linker" element={
|
<Route path="/linker" element={<Navigate to="/linker/content" replace />} />
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ModuleGuard module="linker">
|
|
||||||
<LinkerDashboard />
|
|
||||||
</ModuleGuard>
|
|
||||||
</Suspense>
|
|
||||||
} />
|
|
||||||
<Route path="/linker/content" element={
|
<Route path="/linker/content" element={
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ModuleGuard module="linker">
|
<ModuleGuard module="linker">
|
||||||
@@ -254,14 +236,8 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Optimizer Module */}
|
{/* Optimizer Module - Redirect dashboard to content */}
|
||||||
<Route path="/optimizer" element={
|
<Route path="/optimizer" element={<Navigate to="/optimizer/content" replace />} />
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ModuleGuard module="optimizer">
|
|
||||||
<OptimizerDashboard />
|
|
||||||
</ModuleGuard>
|
|
||||||
</Suspense>
|
|
||||||
} />
|
|
||||||
<Route path="/optimizer/content" element={
|
<Route path="/optimizer/content" element={
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ModuleGuard module="optimizer">
|
<ModuleGuard module="optimizer">
|
||||||
@@ -355,14 +331,8 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Other Pages */}
|
{/* Automation Module - Redirect dashboard to rules */}
|
||||||
<Route path="/automation" element={
|
<Route path="/automation" element={<Navigate to="/automation/rules" replace />} />
|
||||||
<Suspense fallback={null}>
|
|
||||||
<ModuleGuard module="automation">
|
|
||||||
<AutomationDashboard />
|
|
||||||
</ModuleGuard>
|
|
||||||
</Suspense>
|
|
||||||
} />
|
|
||||||
<Route path="/automation/rules" element={
|
<Route path="/automation/rules" element={
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<ModuleGuard module="automation">
|
<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
|
// Define menu sections with useMemo to prevent recreation on every render
|
||||||
// Filter out disabled modules based on module enable settings
|
// Filter out disabled modules based on module enable settings
|
||||||
|
// New structure: Dashboard (standalone) → SETUP → WORKFLOW → SETTINGS
|
||||||
const menuSections: MenuSection[] = useMemo(() => {
|
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 />,
|
icon: <DocsIcon />,
|
||||||
name: "Setup",
|
name: "Industry, Sectors & Keywords",
|
||||||
subItems: [
|
path: "/setup/industries-sectors-keywords", // Merged page
|
||||||
{ name: "Keywords Opportunities", path: "/planner/keyword-opportunities" },
|
},
|
||||||
],
|
{
|
||||||
|
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')) {
|
if (moduleEnabled('planner')) {
|
||||||
workflowItems.push({
|
workflowItems.push({
|
||||||
icon: <ListIcon />,
|
icon: <ListIcon />,
|
||||||
name: "Planner",
|
name: "Planner",
|
||||||
subItems: [
|
path: "/planner/keywords", // Default to keywords, submenus shown as in-page navigation
|
||||||
{ name: "Dashboard", path: "/planner" },
|
|
||||||
{ name: "Keywords", path: "/planner/keywords" },
|
|
||||||
{ name: "Clusters", path: "/planner/clusters" },
|
|
||||||
{ name: "Ideas", path: "/planner/ideas" },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Writer if enabled
|
// Add Writer if enabled (single item, no dropdown)
|
||||||
if (moduleEnabled('writer')) {
|
if (moduleEnabled('writer')) {
|
||||||
workflowItems.push({
|
workflowItems.push({
|
||||||
icon: <TaskIcon />,
|
icon: <TaskIcon />,
|
||||||
name: "Writer",
|
name: "Writer",
|
||||||
subItems: [
|
path: "/writer/content", // Default to content, submenus shown as in-page navigation
|
||||||
{ name: "Dashboard", path: "/writer" },
|
|
||||||
{ name: "Tasks", path: "/writer/tasks" },
|
|
||||||
{ name: "Content", path: "/writer/content" },
|
|
||||||
{ name: "Images", path: "/writer/images" },
|
|
||||||
{ name: "Published", path: "/writer/published" },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Thinker if enabled
|
// Add Linker if enabled (single item, no dropdown)
|
||||||
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
|
|
||||||
if (moduleEnabled('linker')) {
|
if (moduleEnabled('linker')) {
|
||||||
workflowItems.push({
|
workflowItems.push({
|
||||||
icon: <PlugInIcon />,
|
icon: <PlugInIcon />,
|
||||||
name: "Linker",
|
name: "Linker",
|
||||||
subItems: [
|
path: "/linker/content",
|
||||||
{ name: "Dashboard", path: "/linker" },
|
|
||||||
{ name: "Content", path: "/linker/content" },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Optimizer if enabled
|
// Add Optimizer if enabled (single item, no dropdown)
|
||||||
if (moduleEnabled('optimizer')) {
|
if (moduleEnabled('optimizer')) {
|
||||||
workflowItems.push({
|
workflowItems.push({
|
||||||
icon: <BoltIcon />,
|
icon: <BoltIcon />,
|
||||||
name: "Optimizer",
|
name: "Optimizer",
|
||||||
subItems: [
|
path: "/optimizer/content",
|
||||||
{ name: "Dashboard", path: "/optimizer" },
|
|
||||||
{ name: "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 [
|
return [
|
||||||
|
// Dashboard is standalone (no section header)
|
||||||
{
|
{
|
||||||
label: "OVERVIEW",
|
label: "", // Empty label for standalone Dashboard
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
icon: <GridIcon />,
|
icon: <GridIcon />,
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
path: "/",
|
path: "/",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: <DocsIcon />,
|
|
||||||
name: "Industry / Sectors",
|
|
||||||
path: "/reference/industries",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "WORKFLOWS",
|
label: "SETUP",
|
||||||
|
items: setupItems,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "WORKFLOW",
|
||||||
items: workflowItems,
|
items: workflowItems,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "ACCOUNT & SETTINGS",
|
label: "SETTINGS",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
icon: <PlugInIcon />,
|
icon: <PlugInIcon />,
|
||||||
name: "Settings",
|
name: "Settings",
|
||||||
@@ -215,28 +185,23 @@ const AppSidebar: React.FC = () => {
|
|||||||
{ name: "Import / Export", path: "/settings/import-export" },
|
{ name: "Import / Export", path: "/settings/import-export" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <DollarLineIcon />,
|
icon: <DollarLineIcon />,
|
||||||
name: "Billing",
|
name: "Billing",
|
||||||
subItems: [
|
subItems: [
|
||||||
{ name: "Credits", path: "/billing/credits" },
|
{ name: "Credits", path: "/billing/credits" },
|
||||||
{ name: "Transactions", path: "/billing/transactions" },
|
{ name: "Transactions", path: "/billing/transactions" },
|
||||||
{ name: "Usage", path: "/billing/usage" },
|
{ name: "Usage", path: "/billing/usage" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
},
|
icon: <DocsIcon />,
|
||||||
{
|
name: "Help & Documentation",
|
||||||
label: "HELP",
|
path: "/help",
|
||||||
items: [
|
},
|
||||||
{
|
],
|
||||||
icon: <DocsIcon />,
|
},
|
||||||
name: "Help & Documentation",
|
];
|
||||||
path: "/help",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [moduleEnabled]);
|
}, [moduleEnabled]);
|
||||||
|
|
||||||
// Admin section - only shown for users in aws-admin account
|
// Admin section - only shown for users in aws-admin account
|
||||||
@@ -585,20 +550,22 @@ const AppSidebar: React.FC = () => {
|
|||||||
<nav className="mb-6">
|
<nav className="mb-6">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{allSections.map((section, sectionIndex) => (
|
{allSections.map((section, sectionIndex) => (
|
||||||
<div key={section.label}>
|
<div key={section.label || `section-${sectionIndex}`}>
|
||||||
<h2
|
{section.label && (
|
||||||
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
<h2
|
||||||
!isExpanded && !isHovered
|
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
||||||
? "lg:justify-center"
|
!isExpanded && !isHovered
|
||||||
: "justify-start"
|
? "lg:justify-center"
|
||||||
}`}
|
: "justify-start"
|
||||||
>
|
}`}
|
||||||
{isExpanded || isHovered || isMobileOpen ? (
|
>
|
||||||
section.label
|
{isExpanded || isHovered || isMobileOpen ? (
|
||||||
) : (
|
section.label
|
||||||
<HorizontaLDots className="size-6" />
|
) : (
|
||||||
)}
|
<HorizontaLDots className="size-6" />
|
||||||
</h2>
|
)}
|
||||||
|
</h2>
|
||||||
|
)}
|
||||||
{renderMenuItems(section.items, sectionIndex)}
|
{renderMenuItems(section.items, sectionIndex)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import PageHeader from '../../components/common/PageHeader';
|
|||||||
import ComponentCard from '../../components/common/ComponentCard';
|
import ComponentCard from '../../components/common/ComponentCard';
|
||||||
import { automationApi, AutomationRule } from '../../api/automation.api';
|
import { automationApi, AutomationRule } from '../../api/automation.api';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { useSiteStore } from '../../store/siteStore';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function AutomationRules() {
|
export default function AutomationRules() {
|
||||||
const navigate = useNavigate();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Automation Rules" />
|
<PageMeta title="Automation Rules" />
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import PageHeader from '../../components/common/PageHeader';
|
|||||||
import ComponentCard from '../../components/common/ComponentCard';
|
import ComponentCard from '../../components/common/ComponentCard';
|
||||||
import { automationApi, ScheduledTask } from '../../api/automation.api';
|
import { automationApi, ScheduledTask } from '../../api/automation.api';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { useSiteStore } from '../../store/siteStore';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function AutomationTasks() {
|
export default function AutomationTasks() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -95,6 +96,12 @@ export default function AutomationTasks() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Automation navigation tabs
|
||||||
|
const automationTabs = [
|
||||||
|
{ label: 'Rules', path: '/automation/rules', icon: <BoltIcon /> },
|
||||||
|
{ label: 'Tasks', path: '/automation/tasks', icon: <ClockIcon /> },
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Scheduled Tasks" />
|
<PageMeta title="Scheduled Tasks" />
|
||||||
@@ -107,6 +114,7 @@ export default function AutomationTasks() {
|
|||||||
color: 'blue',
|
color: 'blue',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={automationTabs} />
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ import FormModal from '../../components/common/FormModal';
|
|||||||
import ProgressModal from '../../components/common/ProgressModal';
|
import ProgressModal from '../../components/common/ProgressModal';
|
||||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { createClustersPageConfig } from '../../config/pages/clusters.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function Clusters() {
|
export default function Clusters() {
|
||||||
const toast = useToast();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Keyword Clusters"
|
title="Keyword Clusters"
|
||||||
badge={{ icon: <GroupIcon />, color: 'purple' }}
|
badge={{ icon: <GroupIcon />, color: 'purple' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={clusters}
|
data={clusters}
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ import FormModal from '../../components/common/FormModal';
|
|||||||
import ProgressModal from '../../components/common/ProgressModal';
|
import ProgressModal from '../../components/common/ProgressModal';
|
||||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { createIdeasPageConfig } from '../../config/pages/ideas.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function Ideas() {
|
export default function Ideas() {
|
||||||
const toast = useToast();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Content Ideas"
|
title="Content Ideas"
|
||||||
badge={{ icon: <BoltIcon />, color: 'orange' }}
|
badge={{ icon: <BoltIcon />, color: 'orange' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={ideas}
|
data={ideas}
|
||||||
|
|||||||
@@ -27,13 +27,14 @@ import { useSiteStore } from '../../store/siteStore';
|
|||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
|
||||||
import FormModal from '../../components/common/FormModal';
|
import FormModal from '../../components/common/FormModal';
|
||||||
import ProgressModal from '../../components/common/ProgressModal';
|
import ProgressModal from '../../components/common/ProgressModal';
|
||||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { useKeywordsImportExport } from '../../config/import-export.config';
|
||||||
import { createKeywordsPageConfig } from '../../config/pages/keywords.config';
|
import { createKeywordsPageConfig } from '../../config/pages/keywords.config';
|
||||||
|
|
||||||
@@ -750,12 +751,20 @@ export default function Keywords() {
|
|||||||
setIsModalOpen(true);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Keywords"
|
title="Keywords"
|
||||||
badge={{ icon: <ListIcon />, color: 'green' }}
|
badge={{ icon: <ListIcon />, color: 'green' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={plannerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={keywords}
|
data={keywords}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
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 { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { fetchAuthorProfiles, createAuthorProfile, updateAuthorProfile, deleteAuthorProfile, AuthorProfile } from '../../services/api';
|
import { fetchAuthorProfiles, createAuthorProfile, updateAuthorProfile, deleteAuthorProfile, AuthorProfile } from '../../services/api';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Button from '../../components/ui/button/Button';
|
import Button from '../../components/ui/button/Button';
|
||||||
import FormModal, { FormField } from '../../components/common/FormModal';
|
import FormModal, { FormField } from '../../components/common/FormModal';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
import { PlusIcon } from '../../icons';
|
import { PlusIcon, BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
|
||||||
|
|
||||||
export default function AuthorProfiles() {
|
export default function AuthorProfiles() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -97,14 +99,23 @@ export default function AuthorProfiles() {
|
|||||||
{ name: 'is_active', label: 'Active', type: 'checkbox', required: false },
|
{ 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 (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<PageMeta title="Author Profiles" />
|
<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 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">
|
<Button onClick={handleCreate} variant="primary">
|
||||||
<PlusIcon className="w-4 h-4 mr-2" />
|
<PlusIcon className="w-4 h-4 mr-2" />
|
||||||
Create Profile
|
Create Profile
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
import PageMeta from "../../components/common/PageMeta";
|
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 ComponentCard from "../../components/common/ComponentCard";
|
||||||
|
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
|
||||||
|
|
||||||
export default function ImageTesting() {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Image Testing - IGNY8" description="AI image testing" />
|
<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">
|
<ComponentCard title="Coming Soon" desc="AI image testing">
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
<p className="text-gray-600 dark:text-gray-400">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
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 Button from '../../components/ui/button/Button';
|
||||||
import TextArea from '../../components/form/input/TextArea';
|
import TextArea from '../../components/form/input/TextArea';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { BoltIcon } from '../../icons';
|
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
|
|
||||||
interface PromptData {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Prompts - IGNY8" description="AI prompts management" />
|
<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">
|
<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 */}
|
{/* Planner Prompts Section */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
import PageMeta from "../../components/common/PageMeta";
|
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 ComponentCard from "../../components/common/ComponentCard";
|
||||||
|
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
|
||||||
|
|
||||||
export default function Strategies() {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Strategies - IGNY8" description="Content strategies" />
|
<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">
|
<ComponentCard title="Coming Soon" desc="Content strategies">
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
<p className="text-gray-600 dark:text-gray-400">
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ import {
|
|||||||
import { optimizerApi } from '../../api/optimizer.api';
|
import { optimizerApi } from '../../api/optimizer.api';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { createContentPageConfig } from '../../config/pages/content.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import ProgressModal from '../../components/common/ProgressModal';
|
import ProgressModal from '../../components/common/ProgressModal';
|
||||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function Content() {
|
export default function Content() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -197,12 +198,21 @@ export default function Content() {
|
|||||||
}
|
}
|
||||||
}, [toast, progressModal, loadContent, navigate]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Content"
|
title="Content"
|
||||||
badge={{ icon: <FileIcon />, color: 'purple' }}
|
badge={{ icon: <FileIcon />, color: 'purple' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={writerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={content}
|
data={content}
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ import {
|
|||||||
ContentImage,
|
ContentImage,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { createImagesPageConfig } from '../../config/pages/images.config';
|
||||||
import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQueueModal';
|
import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQueueModal';
|
||||||
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
|
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
|
||||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
import { Modal } from '../../components/ui/modal';
|
import { Modal } from '../../components/ui/modal';
|
||||||
|
|
||||||
export default function Images() {
|
export default function Images() {
|
||||||
@@ -460,12 +461,21 @@ export default function Images() {
|
|||||||
}));
|
}));
|
||||||
}, [pageConfig?.headerMetrics, images, totalCount]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Content Images"
|
title="Content Images"
|
||||||
badge={{ icon: <FileIcon />, color: 'orange' }}
|
badge={{ icon: <FileIcon />, color: 'orange' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={writerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={images}
|
data={images}
|
||||||
|
|||||||
@@ -25,11 +25,12 @@ import ProgressModal from '../../components/common/ProgressModal';
|
|||||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
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 { createTasksPageConfig } from '../../config/pages/tasks.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|
||||||
export default function Tasks() {
|
export default function Tasks() {
|
||||||
const toast = useToast();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Tasks"
|
title="Tasks"
|
||||||
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
||||||
/>
|
/>
|
||||||
|
<ModuleNavigationTabs tabs={writerTabs} />
|
||||||
<TablePageTemplate
|
<TablePageTemplate
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={tasks}
|
data={tasks}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
359
refactor-plan/FINAL_REFACTOR_TASKS.md
Normal file
359
refactor-plan/FINAL_REFACTOR_TASKS.md
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
# Final Refactor Tasks - Account/Plan Validation & Design Consistency
|
||||||
|
|
||||||
|
**Status:** Planning Phase
|
||||||
|
**Last Updated:** 2025-01-27
|
||||||
|
**Objective:** Enforce account/plan requirements at authentication level, fix design inconsistencies in Sites pages, and add welcome/guide screen for new user onboarding.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Implementation Tasks
|
||||||
|
|
||||||
|
### 1. Remove Account Fallback - Fail Authentication
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `backend/igny8_core/auth/views.py:912-953` | Login allows `account = None` (line 934) | Add validation: if `user.account is None`, return error: `"No account exists for this user. Please contact support."` with status 403 | **HIGH** |
|
||||||
|
| `backend/igny8_core/auth/urls.py:76-89` | Same issue in legacy LoginView | Same validation | **HIGH** |
|
||||||
|
| `frontend/src/store/authStore.ts:48-89` | No account validation after login | After login success, check `user.account` - if null, throw error and prevent login | **HIGH** |
|
||||||
|
| `frontend/src/components/auth/SignInForm.tsx:30` | No account validation | Display error message if account is missing | **HIGH** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- After line 934 in `views.py`, add: `if not account: return error_response(error='No account exists for this user', status_code=403)`
|
||||||
|
- After line 74 in `authStore.ts`, add: `if (!user.account) { throw new Error('No account exists') }`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Remove Plan Fallback - Fail Authentication & Redirect
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `backend/igny8_core/auth/views.py:912-953` | No plan validation | Add validation: if `user.account.plan is None`, return error: `"No plan subscribed. Please subscribe to a plan."` with status 403 and error code `NO_PLAN` | **HIGH** |
|
||||||
|
| `frontend/src/store/authStore.ts:48-89` | No plan validation | After login, check `user.account?.plan` - if null, logout and redirect to `https://igny8.com/pricing` | **HIGH** |
|
||||||
|
| `frontend/src/components/auth/SignInForm.tsx:30-36` | No plan validation | Catch `NO_PLAN` error and redirect to pricing page | **HIGH** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- After account check in `views.py`, add: `if not account.plan: return error_response(error='No plan subscribed', error_code='NO_PLAN', status_code=403)`
|
||||||
|
- After account check in `authStore.ts`, add: `if (!user.account.plan) { logout(); window.location.href = 'https://igny8.com/pricing'; throw new Error('No plan subscribed') }`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Account Settings - Specific Error Handling
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `frontend/src/services/api.ts:1479-1485` | No try-catch, throws generic error | Add try-catch with specific error types: `ACCOUNT_SETTINGS_API_ERROR`, `ACCOUNT_SETTINGS_NOT_FOUND`, `ACCOUNT_SETTINGS_VALIDATION_ERROR` | **MEDIUM** |
|
||||||
|
| Components using `fetchAccountSettings()` | Generic error handling | Display specific error messages based on error type | **MEDIUM** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Wrap `fetchAccountSettings()` in try-catch
|
||||||
|
- Return structured error with type
|
||||||
|
- Update components to display specific error messages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. User/Account Access - Component Null Handling Review
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| All components using `user.account` | Uses optional chaining `user?.account?.slug` but no enforcement | Audit all components, add validation: if `user.account` is null during session, logout user immediately | **HIGH** |
|
||||||
|
| `frontend/src/components/common/SiteAndSectorSelector.tsx:50` | Uses `user?.account?.id` | Add validation check | **MEDIUM** |
|
||||||
|
| `frontend/src/layout/AppSidebar.tsx:45` | Uses `user?.account?.slug` | Add validation check | **MEDIUM** |
|
||||||
|
| `frontend/src/components/header/SiteSwitcher.tsx:93` | Uses `user?.account?.id` | Add validation check | **MEDIUM** |
|
||||||
|
| `frontend/src/store/authStore.ts:192-217` | `refreshUser()` doesn't validate account | After refresh, validate `user.account` - if null, logout | **HIGH** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Create utility function to validate user account/plan
|
||||||
|
- Add validation in `refreshUser()` method
|
||||||
|
- Add validation checks in components that access `user.account`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Site/Sector Null Handling Review
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| Components using `activeSite` | Can be `null`, some components may not handle | Audit all components, ensure null checks before accessing `activeSite` properties | **MEDIUM** |
|
||||||
|
| Components using `activeSector` | Can be `null`, some components may not handle | Audit all components, ensure null checks before accessing `activeSector` properties | **MEDIUM** |
|
||||||
|
| `frontend/src/pages/Planner/Dashboard.tsx:84-88` | Uses `activeSector?.id` and `activeSite?.id` | Already has optional chaining - verify all usages | **LOW** |
|
||||||
|
| `frontend/src/services/api.ts:453-466` | Uses `getActiveSiteId()` and `getActiveSectorId()` | Already has null checks - verify all usages | **LOW** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Audit all components using `activeSite` and `activeSector`
|
||||||
|
- Ensure all components handle null cases gracefully
|
||||||
|
- Add null checks where missing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Optimize Missing Fallbacks - Session Validation
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `frontend/src/components/auth/ProtectedRoute.tsx:15-95` | Only checks `isAuthenticated` | Add validation: check `user.account` and `user.account.plan` - if null, logout and redirect | **HIGH** |
|
||||||
|
| `frontend/src/store/authStore.ts:192-217` | `refreshUser()` doesn't validate | After refresh, validate account/plan - if null, logout immediately | **HIGH** |
|
||||||
|
| `frontend/src/App.tsx` | No global validation | Add useEffect hook to validate account/plan on route changes | **HIGH** |
|
||||||
|
| `backend/igny8_core/auth/middleware.py:24-121` | Sets `request.account = None` on error | Should return 403 if account is required but missing | **MEDIUM** |
|
||||||
|
| `backend/igny8_core/api/authentication.py:21-73` | Sets `account = None` if not found | Should fail authentication if account is required | **MEDIUM** |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Add validation function to check account/plan
|
||||||
|
- Add validation in `ProtectedRoute` before returning children
|
||||||
|
- Add global validation in `App.tsx` useEffect
|
||||||
|
- Update middleware to fail on missing account
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Design Consistency - Sites Pages
|
||||||
|
|
||||||
|
**Issue:** Sites pages use completely different layouts, components, and colors compared to the standard app design used in Planner, Writer, Thinker, and Automation dashboards.
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `frontend/src/pages/Sites/Dashboard.tsx` | Custom header, raw `Card` components | Replace with `PageHeader` and `ComponentCard` | **HIGH** |
|
||||||
|
| `frontend/src/pages/Sites/List.tsx` | Custom styling, different layout | Use standard components and spacing patterns | **HIGH** |
|
||||||
|
| `frontend/src/pages/Sites/Builder/*` | Different components and colors | Use standard design system components | **HIGH** |
|
||||||
|
| `frontend/src/pages/Sites/Settings.tsx` | Custom layout | Use standard components | **MEDIUM** |
|
||||||
|
| `frontend/src/pages/Sites/Content.tsx` | Custom styling | Use standard components | **MEDIUM** |
|
||||||
|
| `frontend/src/pages/Sites/PageManager.tsx` | Custom layout | Use standard components | **MEDIUM** |
|
||||||
|
| `frontend/src/pages/Sites/SyncDashboard.tsx` | Custom styling | Use standard components | **MEDIUM** |
|
||||||
|
| `frontend/src/pages/Sites/DeploymentPanel.tsx` | Custom styling | Use standard components | **MEDIUM** |
|
||||||
|
|
||||||
|
**Design Consistency Details:**
|
||||||
|
|
||||||
|
| Issue | Reference (Planner/Writer) | Sites Pages (Broken) | Fix Required |
|
||||||
|
|-------|---------------------------|----------------------|-------------|
|
||||||
|
| **Page Headers** | Uses `PageHeader` component with `title`, `lastUpdated`, `showRefresh` | Custom `<h1>` and manual layout | Replace with `PageHeader` |
|
||||||
|
| **Info Cards** | Uses `ComponentCard` with `title` and `desc` props | Raw `Card` with custom styling | Replace with `ComponentCard` |
|
||||||
|
| **Metric Cards** | Uses `EnhancedMetricCard` with consistent styling | Custom stat cards with different styling | Replace with `EnhancedMetricCard` |
|
||||||
|
| **Spacing** | Uses `space-y-6` for consistent vertical spacing | Mixed spacing (`mb-6`, `mb-4`, etc.) | Standardize to `space-y-6` |
|
||||||
|
| **Colors** | Uses CSS variables (`var(--color-primary)`) | Hardcoded colors (`text-gray-900`, `bg-blue-500`) | Replace with CSS variables |
|
||||||
|
| **Loading States** | Consistent loading spinner pattern | Different loading patterns | Standardize loading states |
|
||||||
|
| **Button Styles** | Standard `Button` component with variants | Mixed button styles | Use standard `Button` component |
|
||||||
|
| **Layout Patterns** | Consistent grid layouts (`grid-cols-1 md:grid-cols-2 lg:grid-cols-4`) | Inconsistent grid patterns | Standardize grid layouts |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Replace all custom headers with `PageHeader` component
|
||||||
|
- Replace raw `Card` components with `ComponentCard` where appropriate
|
||||||
|
- Replace custom metric cards with `EnhancedMetricCard`
|
||||||
|
- Standardize spacing to `space-y-6` pattern
|
||||||
|
- Replace hardcoded colors with CSS variables
|
||||||
|
- Standardize loading states
|
||||||
|
- Use standard `Button` component consistently
|
||||||
|
- Standardize grid layout patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Welcome/Guide Screen - Workflow Explainer for New Users
|
||||||
|
|
||||||
|
**Issue:** The Dashboard home page (`frontend/src/pages/Dashboard/Home.tsx`) lacks onboarding guidance for new users. After signup, users see generic metrics without understanding the complete workflow or next steps.
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `frontend/src/pages/Dashboard/Home.tsx` | Shows generic metrics, module cards, recent activity | Add workflow guide component inline at top, push dashboard content below | **HIGH** |
|
||||||
|
| `frontend/src/components/onboarding/WorkflowGuide.tsx` | Does not exist | Create component with visual workflow map, inline in page | **HIGH** |
|
||||||
|
| `frontend/src/components/header/AppHeader.tsx` | No guide button | Add orange-colored "Show Guide" button in top right corner | **HIGH** |
|
||||||
|
| `frontend/src/store/onboardingStore.ts` | Does not exist | Create store for "Don't show again" preference and visibility toggle | **MEDIUM** |
|
||||||
|
| Backend - User model/settings | No field for guide dismissal | Add `guide_dismissed` or `show_workflow_guide` field | **MEDIUM** |
|
||||||
|
|
||||||
|
**Workflow Guide Features:**
|
||||||
|
|
||||||
|
| Feature | Description | Implementation Details |
|
||||||
|
|---------|-------------|----------------------|
|
||||||
|
| **Inline Page Display** | Shows directly in Home page content area (not modal/overlay) | Appears at top of page, pushes dashboard content below |
|
||||||
|
| **Dismissible** | User can hide it with "Don't show this again" checkbox | Store preference in `localStorage` + backend |
|
||||||
|
| **Re-open Button** | Orange-colored button in top right header | Text: "Show Guide" or "Workflow Guide" - stands out with orange color scheme |
|
||||||
|
| **Progress Tracking** | Show completed steps (✓) vs pending (○) | Track via `WorkflowState`, sites, clusters, content, published posts |
|
||||||
|
| **Contextual CTAs** | "Start Building Site", "Explore Planner", etc. based on progress | Conditional rendering based on user state |
|
||||||
|
|
||||||
|
**Workflow Flow Structure:**
|
||||||
|
|
||||||
|
| Main Choice | Sub-Option 1 | Sub-Option 2 | Description |
|
||||||
|
|-------------|--------------|-------------|-------------|
|
||||||
|
| **Build New Site** | WordPress Self-Hosted Site | IGNY8-Powered IGNY8-Hosted Site | User wants to create a new site from scratch |
|
||||||
|
| **Integrate Existing Site** | Integrate WordPress/Shopify | Custom Site | User wants to connect an existing site |
|
||||||
|
|
||||||
|
**Button Specifications:**
|
||||||
|
|
||||||
|
| Property | Value | Notes |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| **Location** | Top right corner of header | Next to user profile/notifications |
|
||||||
|
| **Color** | Orange color scheme | Distinct from primary/secondary colors |
|
||||||
|
| **Text** | "Show Guide" or "Workflow Guide" | Clear, action-oriented label |
|
||||||
|
| **Behavior** | Toggles guide visibility | Shows/hides inline guide component |
|
||||||
|
| **Icon** | Book/Guide icon (optional) | Visual indicator |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Create `<WorkflowGuide>` component with inline display (not modal)
|
||||||
|
- Include complete workflow visualization from `COMPLETE_USER_WORKFLOW_GUIDE.md`
|
||||||
|
- Track user progress: check if user has sites, clusters, content, published posts
|
||||||
|
- Show progress percentage (0% → 100%) based on completion
|
||||||
|
- Add "Don't show again" checkbox at bottom
|
||||||
|
- Store dismissal preference in `useOnboardingStore` and backend
|
||||||
|
- Add orange "Show Guide" button in `AppHeader` or header component
|
||||||
|
- Guide appears at top of Home page, dashboard content appears below it
|
||||||
|
- Use standard `Card`, `Button`, `Badge` components for consistency
|
||||||
|
- Make responsive for mobile/tablet views
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Sidebar Restructuring - Simplified Navigation with In-Page Tabs
|
||||||
|
|
||||||
|
**Issue:** Current sidebar has too many dropdown menus, making navigation cluttered. Dashboard pages are redundant when metrics can be shown on table pages. Need clearer organization: Setup → Workflow → Settings.
|
||||||
|
|
||||||
|
| Location | Current State | Required Changes | Priority |
|
||||||
|
|----------|---------------|------------------|----------|
|
||||||
|
| `frontend/src/layout/AppSidebar.tsx` | Multiple dropdown menus, dashboard sub-items | Restructure to single items with in-page navigation | **HIGH** |
|
||||||
|
| `frontend/src/pages/Planner/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Writer/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Linker/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Optimizer/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Thinker/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Automation/*` | Separate dashboard page | Remove dashboard, add in-page navigation tabs | **HIGH** |
|
||||||
|
| `frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx` | Does not exist | Create merged page for Industry/Sectors/Keywords | **HIGH** |
|
||||||
|
| `frontend/src/components/navigation/ModuleNavigationTabs.tsx` | Does not exist | Create reusable tab navigation component | **HIGH** |
|
||||||
|
| `frontend/src/components/dashboard/ModuleMetricsFooter.tsx` | Does not exist | Create compact metrics footer for table pages | **MEDIUM** |
|
||||||
|
|
||||||
|
**New Sidebar Structure:**
|
||||||
|
|
||||||
|
| Section | Menu Items | Type | Default Route | Notes |
|
||||||
|
|---------|------------|------|---------------|-------|
|
||||||
|
| **(No Section)** | Dashboard | Single | `/` | Standalone, no section header |
|
||||||
|
| **SETUP** | Industry/Sectors/Keywords | Single | `/setup/industries-sectors-keywords` | Merged page: Industry/Sectors + Keyword Opportunities, saved to user account |
|
||||||
|
| | Sites | Single | `/sites` | Submenus shown as in-page navigation |
|
||||||
|
| | Automation | Single | `/automation/rules` | Submenus shown as in-page navigation |
|
||||||
|
| | Thinker | Single | `/thinker/prompts` | Submenus shown as in-page navigation |
|
||||||
|
| **WORKFLOW** | Planner | Single | `/planner/keywords` | Submenus shown as in-page navigation |
|
||||||
|
| | Writer | Single | `/writer/content` | Submenus shown as in-page navigation |
|
||||||
|
| | Linker | Single | `/linker/content` | Submenus shown as in-page navigation |
|
||||||
|
| | Optimizer | Single | `/optimizer/content` | Submenus shown as in-page navigation |
|
||||||
|
| **SETTINGS** | Settings | Dropdown | `/settings` | Keep dropdown (General, Plans, Integration, Publishing, Import/Export) |
|
||||||
|
| | Billing | Dropdown | `/billing/credits` | Keep dropdown (Credits, Transactions, Usage) |
|
||||||
|
| | Help & Documentation | Single | `/help` | No dropdown |
|
||||||
|
|
||||||
|
**In-Page Navigation Details:**
|
||||||
|
|
||||||
|
| Module | In-Page Navigation Tabs | Tab Routes |
|
||||||
|
|--------|-------------------------|------------|
|
||||||
|
| **Industry/Sectors/Keywords** | Industry/Sectors \| Keyword Opportunities | Single page with tabs/sections |
|
||||||
|
| **Sites** | All Sites \| Create Site \| Blueprints \| Sync Dashboard \| Deployment Panel | `/sites`, `/sites/builder`, `/sites/blueprints`, `/sites/sync`, `/sites/deploy` |
|
||||||
|
| **Automation** | Rules \| Tasks | `/automation/rules`, `/automation/tasks` |
|
||||||
|
| **Thinker** | Prompts \| Author Profiles \| Strategies \| Image Testing | `/thinker/prompts`, `/thinker/author-profiles`, `/thinker/strategies`, `/thinker/image-testing` |
|
||||||
|
| **Planner** | Keywords \| Clusters \| Ideas | `/planner/keywords`, `/planner/clusters`, `/planner/ideas` |
|
||||||
|
| **Writer** | Tasks \| Content \| Images \| Published | `/writer/tasks`, `/writer/content`, `/writer/images`, `/writer/published` |
|
||||||
|
| **Linker** | Content | `/linker/content` (single item, no tabs needed) |
|
||||||
|
| **Optimizer** | Content | `/optimizer/content` (single item, no tabs needed) |
|
||||||
|
|
||||||
|
**Industry/Sectors/Keywords Integration:**
|
||||||
|
|
||||||
|
| Feature | Description | Implementation |
|
||||||
|
|---------|-------------|----------------|
|
||||||
|
| **Purpose** | Pre-setup page before creating sites | User selects industry, sectors, and keywords before site creation |
|
||||||
|
| **Data Storage** | Saved to user account (not site-specific) | Available when creating sites |
|
||||||
|
| **Layout** | Tabbed interface or sections | Industry/Sectors tab + Keyword Opportunities tab |
|
||||||
|
| **Site Builder Integration** | Load pre-selected data | Site builder loads industries/sectors from user account |
|
||||||
|
| **Site Settings** | Show only pre-selected options | Site settings show only industries/sectors user intended to use |
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Remove all "Dashboard" sub-items from sidebar
|
||||||
|
- Convert dropdown menus to single items with in-page tab navigation
|
||||||
|
- Create `ModuleNavigationTabs` component for reusable tab navigation
|
||||||
|
- Create `ModuleMetricsFooter` component for compact metrics on table pages
|
||||||
|
- Create merged `IndustriesSectorsKeywords` page combining Industry/Sectors and Keyword Opportunities
|
||||||
|
- Update Site Builder to load industries/sectors from user account (not site-specific)
|
||||||
|
- Update Site Settings to show only pre-selected industries/sectors
|
||||||
|
- Add metrics footer to all Planner, Writer, Linker, Optimizer table pages
|
||||||
|
- Remove separate dashboard routes for Planner, Writer, Linker, Optimizer, Thinker, Automation
|
||||||
|
- Update routing to remove dashboard paths from sidebar navigation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Priority Summary
|
||||||
|
|
||||||
|
| Priority | Count | Tasks |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| **HIGH** | 27 | Backend account/plan validation, Frontend login validation, Session validation, Design consistency for Sites pages, Welcome/Guide screen component, Guide button, Sidebar restructuring, In-page navigation, Dashboard removal, Industry/Sectors/Keywords merge |
|
||||||
|
| **MEDIUM** | 13 | Account settings errors, Component null checks, Design consistency for remaining Sites pages, Guide persistence/store, Module metrics footer |
|
||||||
|
| **LOW** | 2 | Site/Sector null handling (mostly handled) |
|
||||||
|
|
||||||
|
**Total Tasks:** 42
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Backend Authentication (HIGH Priority)
|
||||||
|
1. Add account validation to login endpoints
|
||||||
|
2. Add plan validation to login endpoints
|
||||||
|
3. Update middleware to fail on missing account
|
||||||
|
|
||||||
|
### Phase 2: Frontend Authentication (HIGH Priority)
|
||||||
|
1. Validate account after login
|
||||||
|
2. Validate plan after login
|
||||||
|
3. Handle NO_PLAN error with redirect
|
||||||
|
4. Add validation to ProtectedRoute
|
||||||
|
5. Add global session validation in App.tsx
|
||||||
|
|
||||||
|
### Phase 3: Component Null Handling (HIGH Priority)
|
||||||
|
1. Audit all components using `user.account`
|
||||||
|
2. Add validation to `refreshUser()`
|
||||||
|
3. Add validation checks in components
|
||||||
|
|
||||||
|
### Phase 4: Design Consistency - Core Sites Pages (HIGH Priority)
|
||||||
|
1. Refactor Sites Dashboard
|
||||||
|
2. Refactor Sites List
|
||||||
|
3. Refactor Sites Builder pages
|
||||||
|
|
||||||
|
### Phase 5: Design Consistency - Remaining Sites Pages (MEDIUM Priority)
|
||||||
|
1. Refactor Sites Settings
|
||||||
|
2. Refactor Sites Content
|
||||||
|
3. Refactor Sites PageManager
|
||||||
|
4. Refactor Sites SyncDashboard
|
||||||
|
5. Refactor Sites DeploymentPanel
|
||||||
|
|
||||||
|
### Phase 6: Account Settings & Site/Sector Handling (MEDIUM/LOW Priority)
|
||||||
|
1. Add specific error handling for account settings
|
||||||
|
2. Audit and fix site/sector null handling
|
||||||
|
|
||||||
|
### Phase 7: Welcome/Guide Screen & Onboarding (HIGH Priority)
|
||||||
|
1. Create WorkflowGuide component (inline, not modal)
|
||||||
|
2. Create onboarding store for state management
|
||||||
|
3. Add orange "Show Guide" button in header
|
||||||
|
4. Implement flow structure (Build New Site vs Integrate Existing Site)
|
||||||
|
5. Add backend dismissal field
|
||||||
|
6. Implement progress tracking logic
|
||||||
|
7. Integrate guide at top of Home page (pushes dashboard below)
|
||||||
|
8. Test responsive design on mobile/tablet views
|
||||||
|
|
||||||
|
### Phase 8: Sidebar Restructuring & Navigation (HIGH Priority)
|
||||||
|
1. Restructure sidebar: Dashboard (standalone) → SETUP → WORKFLOW → SETTINGS
|
||||||
|
2. Remove all dashboard sub-items from sidebar
|
||||||
|
3. Convert dropdown menus to single items (Planner, Writer, Linker, Optimizer, Thinker, Automation, Sites)
|
||||||
|
4. Create ModuleNavigationTabs component for in-page tab navigation
|
||||||
|
5. Create merged IndustriesSectorsKeywords page (Industry/Sectors + Keyword Opportunities)
|
||||||
|
6. Update Site Builder to load industries/sectors from user account
|
||||||
|
7. Update Site Settings to show only pre-selected industries/sectors
|
||||||
|
8. Add in-page navigation tabs to all module pages
|
||||||
|
9. Remove separate dashboard routes for Planner, Writer, Linker, Optimizer, Thinker, Automation
|
||||||
|
10. Create ModuleMetricsFooter component for compact metrics on table pages
|
||||||
|
11. Add metrics footer to all table pages (Planner, Writer, Linker, Optimizer)
|
||||||
|
12. Test navigation flow and responsive design
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Success Criteria
|
||||||
|
|
||||||
|
1. **Authentication:** Users without account or plan cannot sign in
|
||||||
|
2. **Session Validation:** Users with missing account/plan are logged out automatically
|
||||||
|
3. **Design Consistency:** All Sites pages match the standard app design
|
||||||
|
4. **Error Handling:** Specific, actionable error messages for all failure cases
|
||||||
|
5. **Null Safety:** All components handle null cases gracefully
|
||||||
|
6. **Onboarding:** New users see an inline workflow guide on first login with clear flow options (Build New Site vs Integrate Existing Site) and progress tracking
|
||||||
|
7. **Guide Access:** Users can dismiss the guide and re-open it via an orange "Show Guide" button in the header
|
||||||
|
8. **Sidebar Navigation:** Sidebar simplified to 12 items with clear Setup → Workflow → Settings organization
|
||||||
|
9. **In-Page Navigation:** All modules use in-page tab navigation instead of sidebar dropdowns
|
||||||
|
10. **Pre-Setup Data:** Industry/Sectors/Keywords saved to user account and available during site creation
|
||||||
|
11. **Dashboard Removal:** All module dashboards removed, metrics shown as compact footer on table pages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This plan ensures strict account/plan validation and design consistency across the entire application.*
|
||||||
|
|
||||||
Reference in New Issue
Block a user