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
* Includes: Page title, last updated, site/sector info, and selectors
*/
import React, { ReactNode } from 'react';
import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
import SiteAndSectorSelector from './SiteAndSectorSelector';
@@ -13,6 +14,10 @@ interface PageHeaderProps {
showRefresh?: boolean;
onRefresh?: () => void;
className?: string;
badge?: {
icon: ReactNode;
color: 'blue' | 'green' | 'purple' | 'orange' | 'red' | 'indigo';
};
}
export default function PageHeader({
@@ -21,14 +26,33 @@ export default function PageHeader({
showRefresh = false,
onRefresh,
className = "",
badge,
}: PageHeaderProps) {
const { activeSite } = useSiteStore();
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 (
<div className={`flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 ${className}`}>
<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">
{lastUpdated && (
<>

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,16 @@
import PageMeta from "../../components/common/PageMeta";
import ComponentCard from "../../components/common/ComponentCard";
import PageHeader from "../../components/common/PageHeader";
import { PieChartIcon } from "../../icons";
export default function Mapping() {
return (
<>
<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">
<div className="text-center py-8">
<p className="text-gray-600 dark:text-gray-400">

View File

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

View File

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

View File

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

View File

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

View File

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