frontend-refactor-1

This commit is contained in:
alorig
2025-11-20 08:58:38 +05:00
parent a0de0cf6b1
commit 8e7afa76cd
18 changed files with 1810 additions and 975 deletions

View File

@@ -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">

View 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>
);
}

View 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>
);
}

View File

@@ -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>
))} ))}

View File

@@ -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" />

View File

@@ -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">

View File

@@ -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}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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

View File

@@ -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">

View File

@@ -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">

View File

@@ -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">

View File

@@ -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}

View File

@@ -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}

View File

@@ -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

View 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.*