Section 6 COmpleted
This commit is contained in:
57
CHANGELOG.md
57
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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 />} />
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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 />,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,37 +281,29 @@ 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>
|
||||
<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>
|
||||
<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">
|
||||
Manage your account information, profile settings, and team members
|
||||
{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>
|
||||
|
||||
{/* 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>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="mt-6">
|
||||
{/* Account Tab */}
|
||||
|
||||
@@ -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,35 +328,17 @@ 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>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">
|
||||
Content Settings / {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">
|
||||
Configure how your content and images are generated
|
||||
{activeTab === 'content' && 'Customize how your articles are written'}
|
||||
{activeTab === 'publishing' && 'Configure automatic publishing settings'}
|
||||
{activeTab === 'images' && 'Set up AI image generation preferences'}
|
||||
</p>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="mt-6">
|
||||
{/* Content Generation Tab */}
|
||||
|
||||
@@ -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. It’s 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
|
||||
|
||||
@@ -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') && (
|
||||
{/* 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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="mt-6">
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user