This commit is contained in:
Desktop
2025-11-12 23:04:52 +05:00
parent 3fca67858e
commit 459cabf921
11 changed files with 85 additions and 47 deletions

View File

@@ -3,6 +3,7 @@
* Used across all Planner and Writer module pages * Used across all Planner and Writer module pages
* Includes: Page title, last updated, site/sector info, and selectors * Includes: Page title, last updated, site/sector info, and selectors
*/ */
import React, { ReactNode } from 'react';
import { useSiteStore } from '../../store/siteStore'; import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore'; import { useSectorStore } from '../../store/sectorStore';
import SiteAndSectorSelector from './SiteAndSectorSelector'; import SiteAndSectorSelector from './SiteAndSectorSelector';
@@ -13,6 +14,10 @@ interface PageHeaderProps {
showRefresh?: boolean; showRefresh?: boolean;
onRefresh?: () => void; onRefresh?: () => void;
className?: string; className?: string;
badge?: {
icon: ReactNode;
color: 'blue' | 'green' | 'purple' | 'orange' | 'red' | 'indigo';
};
} }
export default function PageHeader({ export default function PageHeader({
@@ -21,14 +26,33 @@ export default function PageHeader({
showRefresh = false, showRefresh = false,
onRefresh, onRefresh,
className = "", className = "",
badge,
}: PageHeaderProps) { }: PageHeaderProps) {
const { activeSite } = useSiteStore(); const { activeSite } = useSiteStore();
const { activeSector } = useSectorStore(); const { activeSector } = useSectorStore();
const badgeColors = {
blue: 'bg-blue-600 dark:bg-blue-500',
green: 'bg-green-600 dark:bg-green-500',
purple: 'bg-purple-600 dark:bg-purple-500',
orange: 'bg-orange-600 dark:bg-orange-500',
red: 'bg-red-600 dark:bg-red-500',
indigo: 'bg-indigo-600 dark:bg-indigo-500',
};
return ( return (
<div className={`flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 ${className}`}> <div className={`flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 ${className}`}>
<div className="flex-1"> <div className="flex-1">
<h2 className="text-2xl font-bold text-gray-800 dark:text-white/90">{title}</h2> <div className="flex items-center gap-3">
{badge && (
<div className={`flex items-center justify-center w-10 h-10 rounded-xl ${badgeColors[badge.color]} flex-shrink-0`}>
{badge.icon && typeof badge.icon === 'object' && 'type' in badge.icon
? React.cloneElement(badge.icon as React.ReactElement, { className: 'text-white size-5' })
: badge.icon}
</div>
)}
<h2 className="text-2xl font-bold text-gray-800 dark:text-white/90">{title}</h2>
</div>
<div className="flex items-center gap-3 mt-1"> <div className="flex items-center gap-3 mt-1">
{lastUpdated && ( {lastUpdated && (
<> <>

View File

@@ -385,11 +385,12 @@ export default function Clusters() {
return ( return (
<> <>
<PageHeader title="Keyword Clusters" /> <PageHeader
title="Keyword Clusters"
badge={{ icon: <GroupIcon />, color: 'green' }}
/>
<TablePageTemplate <TablePageTemplate
title="Keyword Clusters" hideHeader={true}
titleIcon={<GroupIcon className="text-success-500 size-5" />}
subtitle="Organize keywords into content clusters for better SEO strategy"
columns={pageConfig.columns} columns={pageConfig.columns}
data={clusters} data={clusters}
loading={loading} loading={loading}

View File

@@ -473,6 +473,7 @@ export default function PlannerDashboard() {
lastUpdated={lastUpdated} lastUpdated={lastUpdated}
showRefresh={true} showRefresh={true}
onRefresh={fetchDashboardData} onRefresh={fetchDashboardData}
badge={{ icon: <PieChartIcon />, color: 'blue' }}
/> />
{/* Hero Section - Key Metric */} {/* Hero Section - Key Metric */}

View File

@@ -295,11 +295,12 @@ export default function Ideas() {
return ( return (
<> <>
<PageHeader title="Content Ideas" /> <PageHeader
title="Content Ideas"
badge={{ icon: <BoltIcon />, color: 'orange' }}
/>
<TablePageTemplate <TablePageTemplate
title="Content Ideas" hideHeader={true}
titleIcon={<BoltIcon className="text-warning-500 size-5" />}
subtitle="Generate and organize content ideas based on keyword research"
columns={pageConfig.columns} columns={pageConfig.columns}
data={ideas} data={ideas}
loading={loading} loading={loading}

View File

@@ -752,11 +752,12 @@ export default function Keywords() {
return ( return (
<> <>
<PageHeader title="Keywords" /> <PageHeader
title="Keywords"
badge={{ icon: <ListIcon />, color: 'blue' }}
/>
<TablePageTemplate <TablePageTemplate
title="Keywords" hideHeader={true}
titleIcon={<ListIcon className="text-brand-500 size-5" />}
subtitle="Manage and organize SEO keywords for content planning"
columns={pageConfig.columns} columns={pageConfig.columns}
data={keywords} data={keywords}
loading={loading} loading={loading}

View File

@@ -1,12 +1,16 @@
import PageMeta from "../../components/common/PageMeta"; import PageMeta from "../../components/common/PageMeta";
import ComponentCard from "../../components/common/ComponentCard"; import ComponentCard from "../../components/common/ComponentCard";
import PageHeader from "../../components/common/PageHeader"; import PageHeader from "../../components/common/PageHeader";
import { PieChartIcon } from "../../icons";
export default function Mapping() { export default function Mapping() {
return ( return (
<> <>
<PageMeta title="Content Mapping - IGNY8" description="Keyword to content mapping" /> <PageMeta title="Content Mapping - IGNY8" description="Keyword to content mapping" />
<PageHeader title="Content Mapping" /> <PageHeader
title="Content Mapping"
badge={{ icon: <PieChartIcon />, color: 'indigo' }}
/>
<ComponentCard title="Coming Soon" desc="Keyword to content mapping"> <ComponentCard title="Coming Soon" desc="Keyword to content mapping">
<div className="text-center py-8"> <div className="text-center py-8">
<p className="text-gray-600 dark:text-gray-400"> <p className="text-gray-600 dark:text-gray-400">

View File

@@ -181,11 +181,12 @@ export default function Content() {
return ( return (
<> <>
<PageHeader title="Content" /> <PageHeader
title="Content"
badge={{ icon: <FileIcon />, color: 'purple' }}
/>
<TablePageTemplate <TablePageTemplate
title="Content" hideHeader={true}
titleIcon={<FileIcon className="text-brand-500 size-5" />}
subtitle="Review AI-generated content and metadata"
columns={pageConfig.columns} columns={pageConfig.columns}
data={content} data={content}
loading={loading} loading={loading}

View File

@@ -552,6 +552,7 @@ export default function WriterDashboard() {
lastUpdated={lastUpdated} lastUpdated={lastUpdated}
showRefresh={true} showRefresh={true}
onRefresh={fetchDashboardData} onRefresh={fetchDashboardData}
badge={{ icon: <PencilIcon />, color: 'green' }}
/> />
{/* Hero Section - Key Metric */} {/* Hero Section - Key Metric */}

View File

@@ -385,11 +385,12 @@ export default function Images() {
return ( return (
<> <>
<PageHeader title="Content Images" /> <PageHeader
title="Content Images"
badge={{ icon: <FileIcon />, color: 'purple' }}
/>
<TablePageTemplate <TablePageTemplate
title="Content Images" hideHeader={true}
titleIcon={<FileIcon className="text-purple-500 size-5" />}
subtitle="Manage images for content articles"
columns={pageConfig.columns} columns={pageConfig.columns}
data={images} data={images}
loading={loading} loading={loading}

View File

@@ -711,9 +711,7 @@ export default function Tasks() {
<ViewToggle currentView={currentView} onViewChange={setCurrentView} /> <ViewToggle currentView={currentView} onViewChange={setCurrentView} />
</div> </div>
<TablePageTemplate <TablePageTemplate
title="Tasks" hideHeader={true}
titleIcon={<TaskIcon className="text-brand-500 size-5" />}
subtitle="Manage content generation queue and tasks"
columns={pageConfig.columns} columns={pageConfig.columns}
data={tasks} data={tasks}
loading={loading} loading={loading}

View File

@@ -82,9 +82,10 @@ interface HeaderMetrics {
} }
interface TablePageTemplateProps { interface TablePageTemplateProps {
title: string; title?: string; // Optional - hide if PageHeader is used
titleIcon?: ReactNode; // Icon component for title (e.g., ListIcon) titleIcon?: ReactNode; // Icon component for title (e.g., ListIcon)
subtitle?: string; subtitle?: string; // Optional - hide if PageHeader is used
hideHeader?: boolean; // Hide the header section when PageHeader is used
columns: ColumnConfig[]; columns: ColumnConfig[];
data: any[]; data: any[];
loading?: boolean; loading?: boolean;
@@ -504,28 +505,32 @@ export default function TablePageTemplate({
return ( return (
<div className={className}> <div className={className}>
{/* Page Header - Match Keywords.tsx styling */} {/* Page Header - Match Keywords.tsx styling - Hide if hideHeader is true */}
<div className="flex justify-between items-center mb-6 overflow-visible"> {!hideHeader && (
<div className="flex-1 min-w-0"> <div className="flex justify-between items-center mb-6 overflow-visible">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white/90 flex items-center gap-2"> <div className="flex-1 min-w-0">
{titleIcon && ( {title && (
<div className="flex items-center justify-center w-10 h-10 bg-blue-50 rounded-xl dark:bg-blue-500/10"> <h2 className="text-xl font-semibold text-gray-800 dark:text-white/90 flex items-center gap-2">
{titleIcon} {titleIcon && (
</div> <div className="flex items-center justify-center w-10 h-10 bg-blue-50 rounded-xl dark:bg-blue-500/10">
{titleIcon}
</div>
)}
{title}
</h2>
)} )}
{title} {subtitle && (
</h2> <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
{subtitle && ( {subtitle}
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400"> </p>
{subtitle} )}
</p> </div>
)} {/* Sector Selector - Replaces notification card */}
<div className="flex-shrink-0 overflow-visible">
<SectorSelector />
</div>
</div> </div>
{/* Sector Selector - Replaces notification card */} )}
<div className="flex-shrink-0 overflow-visible">
<SectorSelector />
</div>
</div>
{/* Filters Row - 75% centered, container inside stretched to 100% */} {/* Filters Row - 75% centered, container inside stretched to 100% */}
{(renderFilters || filters.length > 0) && ( {(renderFilters || filters.length > 0) && (