Section 6 COmpleted

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-27 03:41:51 +00:00
parent 4e9bf0ba56
commit e5959c3e72
22 changed files with 310 additions and 314 deletions

View File

@@ -1,6 +1,6 @@
# IGNY8 Change Log
**Current Version:** 1.1.7
**Current Version:** 1.1.8
**Last Updated:** December 27, 2025
---
@@ -9,6 +9,7 @@
| Version | Date | Summary |
|---------|------|---------|
| 1.1.8 | Dec 27, 2025 | Section 6 SIDEBAR restructure - Dropdowns, breadcrumbs, navigation cleanup |
| 1.1.7 | Dec 27, 2025 | Section 5 HELP module - Support links, Documentation, FAQ updates |
| 1.1.6 | Dec 27, 2025 | Section 4 ACCOUNT modules - Profile API, Password Change, Billing improvements |
| 1.1.5 | Jan 2, 2025 | Section 3 WORKFLOW modules - Planner, Writer, Progress Modal fixes |
@@ -26,6 +27,60 @@
---
## v1.1.8 - December 27, 2025
### Section 6 Sidebar & Navigation Restructure
**Sidebar Module Changes:**
- **REMOVED** Linker from sidebar (module not ready for launch)
- **REMOVED** Optimizer from sidebar (module not ready for launch)
- **REORDERED** SETUP menu: Sites → Add Keywords → Content Settings → Thinker
**Sidebar Dropdown Navigation:**
- Added sub-item dropdowns for major modules in AppSidebar.tsx:
- **Planner**: Keywords, Clusters, Ideas
- **Writer**: Queue, Drafts, Images, Review, Published
- **Content Settings**: Content Generation, Publishing, Image Settings
- **Thinker**: Prompts, Author Profiles
- **Account Settings**: Account, Profile, Team
- **Plans & Billing**: Current Plan, Upgrade Plan, History
- **Usage Analytics**: Limits & Usage, Credit History, Activity Log
**URL-Based Tab Navigation:**
- Converted Account pages from useState tabs to URL-based navigation:
- `AccountSettingsPage.tsx` - `/account/settings`, `/account/settings/profile`, `/account/settings/team`
- `PlansAndBillingPage.tsx` - `/account/plans`, `/account/plans/upgrade`, `/account/plans/history`
- `UsageAnalyticsPage.tsx` - `/account/usage`, `/account/usage/credits`, `/account/usage/activity`
- `ContentSettingsPage.tsx` - `/account/content-settings`, `/account/content-settings/publishing`, `/account/content-settings/images`
- Added corresponding routes in App.tsx
**ModuleNavigationTabs Removal:**
- Removed redundant ModuleNavigationTabs from all pages (navigation now via sidebar):
- Planner: Keywords.tsx, Clusters.tsx, Ideas.tsx
- Writer: Tasks.tsx, Content.tsx, Images.tsx, Review.tsx, Published.tsx
- Thinker: Prompts.tsx, AuthorProfiles.tsx, Strategies.tsx, ImageTesting.tsx
- Sites: List.tsx
**Breadcrumb Navigation:**
- Added `breadcrumb` prop to PageHeader component
- Implemented breadcrumb display across all module pages
- Format: "Module / Page" (e.g., "Planner / Keywords", "Writer / Drafts")
**Files Modified:**
- `frontend/src/layout/AppSidebar.tsx` - Sidebar restructure with dropdowns
- `frontend/src/App.tsx` - Added sub-routes for URL-based navigation
- `frontend/src/components/common/PageHeader.tsx` - Added breadcrumb prop
- `frontend/src/pages/account/AccountSettingsPage.tsx` - URL-based tabs
- `frontend/src/pages/account/PlansAndBillingPage.tsx` - URL-based tabs
- `frontend/src/pages/account/UsageAnalyticsPage.tsx` - URL-based tabs
- `frontend/src/pages/account/ContentSettingsPage.tsx` - URL-based tabs
- `frontend/src/pages/Planner/*.tsx` - Removed tabs, added breadcrumb
- `frontend/src/pages/Writer/*.tsx` - Removed tabs, added breadcrumb
- `frontend/src/pages/Thinker/*.tsx` - Removed tabs, added breadcrumb
- `frontend/src/pages/Sites/List.tsx` - Removed tabs, added breadcrumb
---
## v1.1.7 - December 27, 2025
### Section 5 HELP Module Implementation

View File

@@ -189,13 +189,28 @@ export default function App() {
<Route path="/billing/usage" element={<Usage />} />
{/* Account Section - Billing & Management Pages */}
<Route path="/account/plans" element={<PlansAndBillingPage />} />
<Route path="/account/purchase-credits" element={<PurchaseCreditsPage />} />
{/* Account Settings - with sub-routes for sidebar navigation */}
<Route path="/account/settings" element={<AccountSettingsPage />} />
<Route path="/account/settings/profile" element={<AccountSettingsPage />} />
<Route path="/account/settings/team" element={<AccountSettingsPage />} />
{/* Legacy redirect - Team is now a tab in Account Settings */}
<Route path="/account/team" element={<Navigate to="/account/settings" replace />} />
<Route path="/account/team" element={<Navigate to="/account/settings/team" replace />} />
{/* Plans & Billing - with sub-routes for sidebar navigation */}
<Route path="/account/plans" element={<PlansAndBillingPage />} />
<Route path="/account/plans/upgrade" element={<PlansAndBillingPage />} />
<Route path="/account/plans/history" element={<PlansAndBillingPage />} />
<Route path="/account/purchase-credits" element={<PurchaseCreditsPage />} />
{/* Usage - with sub-routes for sidebar navigation */}
<Route path="/account/usage" element={<UsageAnalyticsPage />} />
<Route path="/account/usage/credits" element={<UsageAnalyticsPage />} />
<Route path="/account/usage/activity" element={<UsageAnalyticsPage />} />
{/* Content Settings - with sub-routes for sidebar navigation */}
<Route path="/account/content-settings" element={<ContentSettingsPage />} />
<Route path="/account/content-settings/publishing" element={<ContentSettingsPage />} />
<Route path="/account/content-settings/images" element={<ContentSettingsPage />} />
{/* Reference Data */}
<Route path="/reference/seed-keywords" element={<SeedKeywords />} />

View File

@@ -14,6 +14,7 @@ import { WorkflowInsights, WorkflowInsight } from './WorkflowInsights';
interface PageHeaderProps {
title: string;
description?: string; // Optional page description shown below title
breadcrumb?: string; // Optional breadcrumb text (e.g., "Thinker / Prompts")
lastUpdated?: Date;
showRefresh?: boolean;
onRefresh?: () => void;
@@ -30,6 +31,7 @@ interface PageHeaderProps {
export default function PageHeader({
title,
description,
breadcrumb,
lastUpdated,
showRefresh = false,
onRefresh,
@@ -104,6 +106,13 @@ export default function PageHeader({
return (
<div className={`flex flex-col gap-3 ${className}`}>
{/* Breadcrumb */}
{breadcrumb && (
<div className="text-sm text-gray-500 dark:text-gray-400">
{breadcrumb}
</div>
)}
{/* Main header row - single row with 3 sections */}
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
{/* Left side: Title, badge, and site/sector info */}

View File

@@ -52,7 +52,13 @@ const AppSidebar: React.FC = () => {
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
const isActive = useCallback(
(path: string) => location.pathname === path,
(path: string) => {
// Exact match
if (location.pathname === path) return true;
// For sub-pages, match if pathname starts with the path (except for root)
if (path !== '/' && location.pathname.startsWith(path + '/')) return true;
return false;
},
[location.pathname]
);
@@ -60,35 +66,45 @@ const AppSidebar: React.FC = () => {
// New structure: Dashboard (standalone) → SETUP → WORKFLOW → SETTINGS
// Module visibility is controlled by GlobalModuleSettings (Django Admin only)
const menuSections: MenuSection[] = useMemo(() => {
// SETUP section items (single items, no dropdowns - submenus shown as in-page navigation)
const setupItems: NavItem[] = [
{
icon: <DocsIcon />,
name: "Add Keywords",
path: "/setup/add-keywords",
},
{
icon: <PageIcon />,
name: "Content Settings",
path: "/account/content-settings",
},
];
// SETUP section items - Ordered: Sites → Add Keywords → Content Settings → Thinker
const setupItems: NavItem[] = [];
// Add Sites (Site Builder) if enabled
// Add Sites first (if enabled)
if (isModuleEnabled('site_builder')) {
setupItems.push({
icon: <GridIcon />,
name: "Sites",
path: "/sites", // Submenus shown as in-page navigation
path: "/sites",
});
}
// Add Thinker if enabled (admin only - prompts and AI settings)
// Add Keywords second
setupItems.push({
icon: <DocsIcon />,
name: "Add Keywords",
path: "/setup/add-keywords",
});
// Content Settings third - with dropdown
setupItems.push({
icon: <PageIcon />,
name: "Content Settings",
subItems: [
{ name: "Content Generation", path: "/account/content-settings" },
{ name: "Publishing", path: "/account/content-settings/publishing" },
{ name: "Image Settings", path: "/account/content-settings/images" },
],
});
// Add Thinker last (admin only - prompts and AI settings)
if (isModuleEnabled('thinker')) {
setupItems.push({
icon: <BoltIcon />,
name: "Thinker",
path: "/thinker/prompts", // Default to prompts, submenus shown as in-page navigation
subItems: [
{ name: "Prompts", path: "/thinker/prompts" },
{ name: "Author Profiles", path: "/thinker/author-profiles" },
],
adminOnly: true, // Only visible to admin/staff users
});
}
@@ -96,25 +112,35 @@ const AppSidebar: React.FC = () => {
// WORKFLOW section items (conditionally shown based on global settings)
const workflowItems: NavItem[] = [];
// Add Planner if enabled
// Add Planner with dropdown if enabled
if (isModuleEnabled('planner')) {
workflowItems.push({
icon: <ListIcon />,
name: "Planner",
path: "/planner/keywords", // Default to keywords, submenus shown as in-page navigation
subItems: [
{ name: "Keywords", path: "/planner/keywords" },
{ name: "Clusters", path: "/planner/clusters" },
{ name: "Ideas", path: "/planner/ideas" },
],
});
}
// Add Writer if enabled
// Add Writer with dropdown if enabled
if (isModuleEnabled('writer')) {
workflowItems.push({
icon: <TaskIcon />,
name: "Writer",
path: "/writer/tasks", // Default to tasks, submenus shown as in-page navigation
subItems: [
{ name: "Queue", path: "/writer/tasks" },
{ name: "Drafts", path: "/writer/content" },
{ name: "Images", path: "/writer/images" },
{ name: "Review", path: "/writer/review" },
{ name: "Published", path: "/writer/published" },
],
});
}
// Add Automation if enabled
// Add Automation if enabled (no dropdown - single page)
if (isModuleEnabled('automation')) {
workflowItems.push({
icon: <BoltIcon />,
@@ -123,23 +149,7 @@ const AppSidebar: React.FC = () => {
});
}
// Add Linker if enabled
if (isModuleEnabled('linker')) {
workflowItems.push({
icon: <PlugInIcon />,
name: "Linker",
path: "/linker/content",
});
}
// Add Optimizer if enabled
if (isModuleEnabled('optimizer')) {
workflowItems.push({
icon: <BoltIcon />,
name: "Optimizer",
path: "/optimizer/content",
});
}
// Linker and Optimizer removed - not active modules
return [
// Dashboard is standalone (no section header)
@@ -167,17 +177,29 @@ const AppSidebar: React.FC = () => {
{
icon: <UserCircleIcon />,
name: "Account Settings",
path: "/account/settings",
subItems: [
{ name: "Account", path: "/account/settings" },
{ name: "Profile", path: "/account/settings/profile" },
{ name: "Team", path: "/account/settings/team" },
],
},
{
icon: <DollarLineIcon />,
name: "Plans & Billing",
path: "/account/plans",
subItems: [
{ name: "Current Plan", path: "/account/plans" },
{ name: "Upgrade Plan", path: "/account/plans/upgrade" },
{ name: "History", path: "/account/plans/history" },
],
},
{
icon: <PieChartIcon />,
name: "Usage",
path: "/account/usage",
subItems: [
{ name: "Limits & Usage", path: "/account/usage" },
{ name: "Credit History", path: "/account/usage/credits" },
{ name: "Activity", path: "/account/usage/activity" },
],
},
{
icon: <PlugInIcon />,

View File

@@ -27,7 +27,6 @@ import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
@@ -442,20 +441,13 @@ export default function Clusters() {
}
};
// Planner navigation tabs
const plannerTabs = [
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
];
return (
<>
<PageHeader
title="Clusters"
description="Keyword groups organized by topic. Generate content ideas from clusters to build topical authority."
badge={{ icon: <GroupIcon />, color: 'purple' }}
navigation={<ModuleNavigationTabs tabs={plannerTabs} />}
breadcrumb="Planner / Clusters"
workflowInsights={workflowInsights}
/>
<TablePageTemplate

View File

@@ -28,7 +28,6 @@ import { createIdeasPageConfig } from '../../config/pages/ideas.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
@@ -348,20 +347,13 @@ export default function Ideas() {
}
};
// Planner navigation tabs
const plannerTabs = [
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
];
return (
<>
<PageHeader
title="Ideas"
description="AI-generated content ideas with titles, outlines, and target keywords. Queue ideas to start content generation."
badge={{ icon: <BoltIcon />, color: 'orange' }}
navigation={<ModuleNavigationTabs tabs={plannerTabs} />}
breadcrumb="Planner / Ideas"
workflowInsights={workflowInsights}
/>
<TablePageTemplate

View File

@@ -25,7 +25,6 @@ import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
import FormModal from '../../components/common/FormModal';
@@ -609,20 +608,13 @@ export default function Keywords() {
setIsModalOpen(true);
}, []);
// Planner navigation tabs
const plannerTabs = [
{ label: 'Keywords', path: '/planner/keywords', icon: <ListIcon /> },
{ label: 'Clusters', path: '/planner/clusters', icon: <GroupIcon /> },
{ label: 'Ideas', path: '/planner/ideas', icon: <BoltIcon /> },
];
return (
<>
<PageHeader
title="Keywords"
description="Your target search terms organized for content creation. Import, cluster, and transform into content ideas."
badge={{ icon: <ListIcon />, color: 'green' }}
navigation={<ModuleNavigationTabs tabs={plannerTabs} />}
breadcrumb="Planner / Keywords"
workflowInsights={workflowInsights}
/>
<TablePageTemplate

View File

@@ -14,7 +14,6 @@ import Badge from '../../components/ui/badge/Badge';
import Alert from '../../components/ui/alert/Alert';
import Switch from '../../components/form/switch/Switch';
import ViewToggle from '../../components/common/ViewToggle';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import WorkflowGuide from '../../components/onboarding/WorkflowGuide';
import {
PlusIcon,
@@ -484,11 +483,6 @@ export default function SiteList() {
);
}
// Navigation tabs for Sites module
const sitesTabs = [
{ label: 'All Sites', path: '/sites', icon: <TableIcon className="w-4 h-4" /> },
];
return (
<div className="p-6">
<PageMeta title="Your Websites - IGNY8" />
@@ -496,7 +490,7 @@ export default function SiteList() {
title="Your Websites"
badge={{ icon: <GridIcon />, color: 'blue' }}
hideSiteSector={true}
navigation={<ModuleNavigationTabs tabs={sitesTabs} />}
breadcrumb="Setup / Sites"
/>
{/* Custom Header Actions - Add Site button and view toggle */}

View File

@@ -1,14 +1,13 @@
import { useState, useEffect } from 'react';
import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { fetchAuthorProfiles, createAuthorProfile, updateAuthorProfile, deleteAuthorProfile, AuthorProfile } from '../../services/api';
import { Card } from '../../components/ui/card';
import Button from '../../components/ui/button/Button';
import FormModal, { FormField } from '../../components/common/FormModal';
import Badge from '../../components/ui/badge/Badge';
import { PlusIcon, BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
import { PlusIcon, UserIcon } from '../../icons';
export default function AuthorProfiles() {
const toast = useToast();
@@ -99,21 +98,13 @@ export default function AuthorProfiles() {
{ name: 'is_active', label: 'Active', type: 'checkbox', required: false },
];
// Thinker navigation tabs
const thinkerTabs = [
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
];
return (
<div className="p-6">
<PageMeta title="Writing Styles" />
<PageHeader
title="Writing Styles"
badge={{ icon: <UserIcon />, color: 'blue' }}
navigation={<ModuleNavigationTabs tabs={thinkerTabs} />}
breadcrumb="Thinker / Author Profiles"
/>
<div className="mb-6 flex justify-between items-center">
<Button onClick={handleCreate} variant="primary">

View File

@@ -1,25 +1,16 @@
import PageMeta from "../../components/common/PageMeta";
import PageHeader from "../../components/common/PageHeader";
import ModuleNavigationTabs from "../../components/navigation/ModuleNavigationTabs";
import ComponentCard from "../../components/common/ComponentCard";
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
import { ImageIcon } from "../../icons";
export default function ImageTesting() {
// Thinker navigation tabs
const thinkerTabs = [
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
];
return (
<>
<PageMeta title="Image Settings - IGNY8" description="Configure image generation" />
<PageHeader
title="Image Settings"
badge={{ icon: <ImageIcon />, color: 'indigo' }}
navigation={<ModuleNavigationTabs tabs={thinkerTabs} />}
breadcrumb="Thinker / Image Testing"
/>
<ComponentCard title="Coming Soon" desc="AI image testing">
<div className="text-center py-8">

View File

@@ -1,11 +1,10 @@
import { useState, useEffect } from 'react';
import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import Button from '../../components/ui/button/Button';
import TextArea from '../../components/form/input/TextArea';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from '../../icons';
import { BoltIcon } from '../../icons';
import { fetchAPI } from '../../services/api';
interface PromptData {
@@ -199,21 +198,13 @@ export default function Prompts() {
);
}
// Thinker navigation tabs
const thinkerTabs = [
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
];
return (
<>
<PageMeta title="Prompt Library - IGNY8" description="Manage your AI writing prompts" />
<PageHeader
title="Prompt Library"
badge={{ icon: <BoltIcon />, color: 'orange' }}
navigation={<ModuleNavigationTabs tabs={thinkerTabs} />}
breadcrumb="Thinker / Prompts"
/>
<div className="p-6">

View File

@@ -1,25 +1,16 @@
import PageMeta from "../../components/common/PageMeta";
import PageHeader from "../../components/common/PageHeader";
import ModuleNavigationTabs from "../../components/navigation/ModuleNavigationTabs";
import ComponentCard from "../../components/common/ComponentCard";
import { BoltIcon, UserIcon, ShootingStarIcon, ImageIcon } from "../../icons";
import { ShootingStarIcon } from "../../icons";
export default function Strategies() {
// Thinker navigation tabs
const thinkerTabs = [
{ label: 'Prompts', path: '/thinker/prompts', icon: <BoltIcon /> },
{ label: 'Author Profiles', path: '/thinker/author-profiles', icon: <UserIcon /> },
{ label: 'Strategies', path: '/thinker/strategies', icon: <ShootingStarIcon /> },
{ label: 'Image Testing', path: '/thinker/image-testing', icon: <ImageIcon /> },
];
return (
<>
<PageMeta title="Content Plans - IGNY8" description="Plan your content strategy" />
<PageHeader
title="Content Plans"
badge={{ icon: <ShootingStarIcon />, color: 'purple' }}
navigation={<ModuleNavigationTabs tabs={thinkerTabs} />}
breadcrumb="Thinker / Strategies"
/>
<ComponentCard title="Coming Soon" desc="Content strategies">
<div className="text-center py-8">

View File

@@ -16,14 +16,13 @@ import {
import { optimizerApi } from '../../api/optimizer.api';
import { useNavigate } from 'react-router-dom';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
import { FileIcon, TaskIcon, CheckCircleIcon } from '../../icons';
import { createContentPageConfig } from '../../config/pages/content.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import ProgressModal from '../../components/common/ProgressModal';
import { useProgressModal } from '../../hooks/useProgressModal';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
@@ -278,22 +277,13 @@ export default function Content() {
return result;
}, [loadContent]);
// Writer navigation tabs
const writerTabs = [
{ label: 'Queue', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Drafts', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Review', path: '/writer/review', icon: <CheckCircleIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Drafts"
description="AI-generated content ready for review. Add images, edit, and publish when ready."
badge={{ icon: <FileIcon />, color: 'purple' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
breadcrumb="Writer / Drafts"
workflowInsights={workflowInsights}
/>
<TablePageTemplate

View File

@@ -18,12 +18,11 @@ import {
bulkDeleteContent,
} from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, DownloadIcon, BoltIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
import { FileIcon, DownloadIcon } from '../../icons';
import { createImagesPageConfig } from '../../config/pages/images.config';
import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQueueModal';
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import { Modal } from '../../components/ui/modal';
export default function Images() {
@@ -447,21 +446,12 @@ export default function Images() {
}));
}, [pageConfig?.headerMetrics, images, totalCount]);
// Writer navigation tabs
const writerTabs = [
{ label: 'Queue', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Drafts', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Review', path: '/writer/review', icon: <CheckCircleIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Images"
badge={{ icon: <FileIcon />, color: 'orange' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
breadcrumb="Writer / Images"
/>
<TablePageTemplate
columns={pageConfig.columns}

View File

@@ -17,12 +17,11 @@ import {
} from '../../services/api';
import { useNavigate } from 'react-router-dom';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
import { FileIcon, CheckCircleIcon } from '../../icons';
import { createPublishedPageConfig } from '../../config/pages/published.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter from '../../components/dashboard/ModuleMetricsFooter';
export default function Published() {
@@ -305,21 +304,12 @@ export default function Published() {
}));
}, [pageConfig?.headerMetrics, content, totalCount]);
// Writer navigation tabs
const writerTabs = [
{ label: 'Queue', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Drafts', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Review', path: '/writer/review', icon: <CheckCircleIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Published"
badge={{ icon: <CheckCircleIcon />, color: 'green' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
breadcrumb="Writer / Published"
/>
<TablePageTemplate
columns={pageConfig.columns}

View File

@@ -14,12 +14,11 @@ import {
} from '../../services/api';
import { useNavigate } from 'react-router-dom';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
import { CheckCircleIcon } from '../../icons';
import { createReviewPageConfig } from '../../config/pages/review.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter from '../../components/dashboard/ModuleMetricsFooter';
export default function Review() {
@@ -344,21 +343,12 @@ export default function Review() {
}
}, [loadContent, toast]);
// Writer navigation tabs
const writerTabs = [
{ label: 'Queue', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Drafts', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Review', path: '/writer/review', icon: <CheckCircleIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Review Queue"
badge={{ icon: <CheckCircleIcon />, color: 'blue' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
breadcrumb="Writer / Review"
/>
<TablePageTemplate
columns={pageConfig.columns}

View File

@@ -24,12 +24,11 @@ import FormModal from '../../components/common/FormModal';
import ProgressModal from '../../components/common/ProgressModal';
import { useProgressModal } from '../../hooks/useProgressModal';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { TaskIcon, PlusIcon, DownloadIcon, FileIcon, ImageIcon, CheckCircleIcon } from '../../icons';
import { TaskIcon, PlusIcon, DownloadIcon, CheckCircleIcon } from '../../icons';
import { createTasksPageConfig } from '../../config/pages/tasks.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
@@ -422,22 +421,13 @@ export default function Tasks() {
}
};
// Writer navigation tabs
const writerTabs = [
{ label: 'Queue', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Drafts', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Review', path: '/writer/review', icon: <CheckCircleIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Content Queue"
description="Manage content tasks waiting for AI generation. Queue ideas here to create articles automatically."
badge={{ icon: <TaskIcon />, color: 'indigo' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
breadcrumb="Writer / Queue"
workflowInsights={workflowInsights}
/>
<TablePageTemplate

View File

@@ -1,10 +1,11 @@
/**
* Account Settings Page - Consolidated Settings
* Tabs: Account, Profile, Team
* Consistent with system page structure (like Plans & Usage pages)
* Tab selection driven by URL path for sidebar navigation
*/
import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import {
Save, Loader2, Settings, User, Users, UserPlus, Shield, Lock, X
} from 'lucide-react';
@@ -28,10 +29,19 @@ import {
type TabType = 'account' | 'profile' | 'team';
// Map URL paths to tab types
function getTabFromPath(pathname: string): TabType {
if (pathname.includes('/profile')) return 'profile';
if (pathname.includes('/team')) return 'team';
return 'account';
}
export default function AccountSettingsPage() {
const toast = useToast();
const location = useLocation();
const { user, refreshUser } = useAuthStore();
const [activeTab, setActiveTab] = useState<TabType>('account');
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string>('');
@@ -271,35 +281,27 @@ export default function AccountSettingsPage() {
<div className="p-6">
<PageMeta title="Account Settings" description="Manage your account, profile, and team" />
{/* Page Header */}
{/* Page Header with Breadcrumb */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Account Settings</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Manage your account information, profile settings, and team members
</p>
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-2">
<span>Account Settings</span>
<span></span>
<span className="text-gray-900 dark:text-white font-medium">
{activeTab === 'account' && 'Account'}
{activeTab === 'profile' && 'Profile'}
{activeTab === 'team' && 'Team'}
</span>
</div>
{/* Tabs */}
<div className="mb-6 border-b border-gray-200 dark:border-gray-700">
<nav className="-mb-px flex space-x-8">
{tabs.map((tab) => (
<button
type="button"
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm
${activeTab === tab.id
? 'border-[var(--color-brand-500)] text-[var(--color-brand-600)] dark:text-[var(--color-brand-400)]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'
}
`}
>
{tab.icon}
{tab.label}
</button>
))}
</nav>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
{activeTab === 'account' && 'Account Information'}
{activeTab === 'profile' && 'Profile Settings'}
{activeTab === 'team' && 'Team Management'}
</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
{activeTab === 'account' && 'Manage your organization and billing information'}
{activeTab === 'profile' && 'Update your personal information and preferences'}
{activeTab === 'team' && 'Invite and manage team members'}
</p>
</div>
{/* Tab Content */}

View File

@@ -5,6 +5,7 @@
*/
import { useState, useEffect, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import {
Save, Loader2, Image as ImageIcon, FileText, Send, Settings
} from 'lucide-react';
@@ -83,9 +84,17 @@ const getImageSizes = (provider: string, model: string) => {
return [{ value: '1024x1024', label: '1024×1024 pixels' }];
};
// Get tab from URL path
function getTabFromPath(pathname: string): TabType {
if (pathname.includes('/publishing')) return 'publishing';
if (pathname.includes('/images')) return 'images';
return 'content';
}
export default function ContentSettingsPage() {
const toast = useToast();
const [activeTab, setActiveTab] = useState<TabType>('content');
const location = useLocation();
const activeTab = getTabFromPath(location.pathname);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
@@ -293,11 +302,11 @@ export default function ContentSettingsPage() {
}
};
const tabs = [
{ id: 'content' as TabType, label: 'Content Generation', icon: <FileText className="w-4 h-4" /> },
{ id: 'publishing' as TabType, label: 'Publishing', icon: <Send className="w-4 h-4" /> },
{ id: 'images' as TabType, label: 'Image Settings', icon: <ImageIcon className="w-4 h-4" /> },
];
const tabTitles: Record<TabType, string> = {
content: 'Content Generation',
publishing: 'Publishing',
images: 'Image Settings',
};
if (loading) {
return (
@@ -319,33 +328,15 @@ export default function ContentSettingsPage() {
{/* Page Header */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Content Settings</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Configure how your content and images are generated
</p>
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">
Content Settings / {tabTitles[activeTab]}
</div>
{/* Tabs */}
<div className="mb-6 border-b border-gray-200 dark:border-gray-700">
<nav className="-mb-px flex space-x-8">
{tabs.map((tab) => (
<button
type="button"
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm
${activeTab === tab.id
? 'border-[var(--color-brand-500)] text-[var(--color-brand-600)] dark:text-[var(--color-brand-400)]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'
}
`}
>
{tab.icon}
{tab.label}
</button>
))}
</nav>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{tabTitles[activeTab]}</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
{activeTab === 'content' && 'Customize how your articles are written'}
{activeTab === 'publishing' && 'Configure automatic publishing settings'}
{activeTab === 'images' && 'Set up AI image generation preferences'}
</p>
</div>
{/* Tab Content */}

View File

@@ -1,12 +1,13 @@
/**
* Plans & Billing Page - Subscription & Payment Management
* Tabs: Current Plan, Upgrade Plan, Billing History
* Tab selection driven by URL path for sidebar navigation
*
* Note: Usage tracking is consolidated in UsageAnalyticsPage (/account/usage)
*/
import { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
import {
CreditCard, Package, TrendingUp, FileText, Wallet, ArrowUpCircle,
Loader2, AlertCircle, CheckCircle, Download, Zap, Globe, Users, X
@@ -49,8 +50,17 @@ import { useAuthStore } from '../../store/authStore';
type TabType = 'plan' | 'upgrade' | 'invoices';
// Map URL paths to tab types
function getTabFromPath(pathname: string): TabType {
if (pathname.includes('/upgrade')) return 'upgrade';
if (pathname.includes('/history')) return 'invoices';
return 'plan';
}
export default function PlansAndBillingPage() {
const [activeTab, setActiveTab] = useState<TabType>('plan');
const location = useLocation();
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string>('');
const [planLoadingId, setPlanLoadingId] = useState<number | null>(null);
@@ -347,18 +357,25 @@ export default function PlansAndBillingPage() {
const subscriptionStatus = currentSubscription?.status || (hasActivePlan ? 'active' : 'none');
const hasPendingManualPayment = payments.some((p) => p.status === 'pending_approval');
const tabs = [
{ id: 'plan' as TabType, label: 'Current Plan', icon: <Package className="w-4 h-4" /> },
{ id: 'upgrade' as TabType, label: 'Upgrade Plan', icon: <Wallet className="w-4 h-4" /> },
{ id: 'invoices' as TabType, label: 'History', icon: <FileText className="w-4 h-4" /> },
];
// Page titles based on active tab
const pageTitles = {
plan: { title: 'Current Plan', description: 'View your subscription details and features' },
upgrade: { title: 'Upgrade Plan', description: 'Compare plans and upgrade your subscription' },
invoices: { title: 'Billing History', description: 'View invoices and manage payment methods' },
};
return (
<div className="p-6">
{/* Page Header with Breadcrumb */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Your Subscription</h1>
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-2">
<span>Plans & Billing</span>
<span></span>
<span className="text-gray-900 dark:text-white font-medium">{pageTitles[activeTab].title}</span>
</div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{pageTitles[activeTab].title}</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Manage your plan and view usage
{pageTitles[activeTab].description}
</p>
</div>
@@ -367,7 +384,7 @@ export default function PlansAndBillingPage() {
<div className="mb-4 p-4 rounded-lg border border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
No active plan. Choose a plan below to activate your account.
</div>
)}
)}}
{hasPendingManualPayment && (
<div className="mb-4 p-4 rounded-lg border border-[var(--color-info-200)] bg-[var(--color-info-50)] text-[var(--color-info-800)] dark:border-[var(--color-info-800)] dark:bg-[var(--color-info-900)]/20 dark:text-[var(--color-info-100)]">
We received your manual payment. Its pending admin approval; activation will complete once approved.
@@ -381,29 +398,6 @@ export default function PlansAndBillingPage() {
</div>
)}
{/* Tabs */}
<div className="mb-6 border-b border-gray-200 dark:border-gray-700">
<nav className="-mb-px flex space-x-8 overflow-x-auto">
{tabs.map((tab) => (
<button
type="button"
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap
${activeTab === tab.id
? 'border-[var(--color-brand-500)] text-[var(--color-brand-500)]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'
}
`}
>
{tab.icon}
{tab.label}
</button>
))}
</nav>
</div>
{/* Tab Content */}
<div className="mt-6">
{/* Current Plan Tab */}
@@ -476,7 +470,8 @@ export default function PlansAndBillingPage() {
<Button
variant="primary"
tone="brand"
onClick={() => setActiveTab('upgrade')}
as={Link}
to="/account/plans/upgrade"
startIcon={<ArrowUpCircle className="w-4 h-4" />}
>
Upgrade Plan

View File

@@ -1,9 +1,11 @@
/**
* Usage & Analytics Page - Refactored
* Organized tabs: Plan Limits & Usage, Credit Activity, API Usage
* Tab selection driven by URL path for sidebar navigation
*/
import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { TrendingUp, Activity, BarChart3, Zap, Calendar } from 'lucide-react';
import PageMeta from '../../components/common/PageMeta';
import { useToast } from '../../components/ui/toast/ToastContainer';
@@ -16,9 +18,18 @@ import Button from '../../components/ui/button/Button';
type TabType = 'limits' | 'activity' | 'api';
// Map URL paths to tab types
function getTabFromPath(pathname: string): TabType {
if (pathname.includes('/credits')) return 'activity';
if (pathname.includes('/activity')) return 'api';
return 'limits';
}
export default function UsageAnalyticsPage() {
const toast = useToast();
const [activeTab, setActiveTab] = useState<TabType>('limits');
const location = useLocation();
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [analytics, setAnalytics] = useState<UsageAnalytics | null>(null);
const [creditBalance, setCreditBalance] = useState<CreditBalance | null>(null);
const [loading, setLoading] = useState(true);
@@ -58,11 +69,11 @@ export default function UsageAnalyticsPage() {
);
}
const tabs = [
{ id: 'limits' as TabType, label: 'Your Limits & Usage (What you\'re using)', icon: <BarChart3 className="w-4 h-4" /> },
{ id: 'activity' as TabType, label: 'Credit History (Where credits go)', icon: <TrendingUp className="w-4 h-4" /> },
{ id: 'api' as TabType, label: 'API Activity (Technical requests)', icon: <Activity className="w-4 h-4" /> },
];
const tabTitles: Record<TabType, string> = {
limits: 'Limits & Usage',
activity: 'Credit History',
api: 'Activity Log',
};
return (
<div className="p-6">
@@ -70,9 +81,14 @@ export default function UsageAnalyticsPage() {
{/* Page Header */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Your Usage</h1>
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">
Usage & Analytics / {tabTitles[activeTab]}
</div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{tabTitles[activeTab]}</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
See how much you're using - Track your credits, content limits, and API activity
{activeTab === 'limits' && 'See how much you\'re using - Track your credits and content limits'}
{activeTab === 'activity' && 'See where your credits go - Track credit usage history'}
{activeTab === 'api' && 'Technical requests - Monitor API activity and usage'}
</p>
</div>
@@ -142,32 +158,9 @@ export default function UsageAnalyticsPage() {
</div>
)}
{/* Tabs and Period Selector */}
<div className="mb-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
{/* Tabs */}
<div className="border-b border-gray-200 dark:border-gray-700 w-full sm:w-auto">
<nav className="-mb-px flex space-x-8 overflow-x-auto">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap
${activeTab === tab.id
? 'border-brand-500 text-brand-600 dark:text-brand-400'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'
}
`}
>
{tab.icon}
{tab.label}
</button>
))}
</nav>
</div>
{/* Period Selector (only show on activity and api tabs) */}
{(activeTab === 'activity' || activeTab === 'api') && (
<div className="mb-6 flex justify-end">
<div className="flex gap-2">
{[7, 30, 90].map((value) => {
const isActive = period === value;
@@ -184,8 +177,8 @@ export default function UsageAnalyticsPage() {
);
})}
</div>
)}
</div>
)}
{/* Tab Content */}
<div className="mt-6">

View File

@@ -462,3 +462,33 @@ To support sidebar navigation, routes need restructuring:
- Clean, consistent navigation pattern across all modules
---
Section 6 implementation is complete. Here's a summary of what was done:
## Section 6 Implementation Complete ✅
### Sidebar Changes
- **Removed** Linker and Optimizer modules from sidebar (not ready for launch)
- **Reordered** SETUP menu: Sites → Add Keywords → Content Settings → Thinker
- **Added dropdown sub-items** for all major modules with clickable navigation
### URL-Based Tab Navigation
Account pages now use URL paths instead of internal state for tabs:
- `/account/settings` → Account tab, `/account/settings/profile` → Profile, `/account/settings/team` → Team
- `/account/plans` → Current Plan, `/account/plans/upgrade` → Upgrade, `/account/plans/history` → History
- `/account/usage` → Limits, `/account/usage/credits` → Credits, `/account/usage/activity` → Activity
- `/account/content-settings` → Content Generation, `/account/content-settings/publishing` → Publishing, `/account/content-settings/images` → Images
### Navigation Cleanup
- **Removed ModuleNavigationTabs** from all module pages (Planner, Writer, Thinker, Sites)
- **Added breadcrumb prop** to PageHeader component
- All pages now display breadcrumb navigation (e.g., "Planner / Keywords")
### Files Modified
- AppSidebar.tsx - Sidebar restructure
- App.tsx - Added sub-routes
- PageHeader.tsx - Added breadcrumb prop
- 4 account pages - URL-based tab navigation
- 3 Planner pages, 5 Writer pages, 4 Thinker pages, 1 Sites page - Removed tabs, added breadcrumbs
- CHANGELOG.md - Updated to v1.1.8