Section 3: Implement ThreeWidgetFooter on Planner & Writer pages
- Created ThreeWidgetFooter.tsx component with 3-column layout: - Widget 1: Page Progress (current page metrics + progress bar + hint) - Widget 2: Module Stats (workflow pipeline with links) - Widget 3: Completion (both modules summary) - Created useThreeWidgetFooter.ts hook for building widget props - Integrated ThreeWidgetFooter into: - Planner: Keywords, Clusters, Ideas pages - Writer: Tasks, Content pages - SiteCard already has SiteSetupChecklist integrated (compact mode) - Backend serializer returns all required fields
This commit is contained in:
@@ -29,7 +29,7 @@ import { createIdeasPageConfig } from '../../config/pages/ideas.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
|
||||
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
|
||||
|
||||
export default function Ideas() {
|
||||
const toast = useToast();
|
||||
@@ -414,45 +414,81 @@ export default function Ideas() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
|
||||
<ModuleMetricsFooter
|
||||
metrics={[
|
||||
{
|
||||
title: 'Clusters',
|
||||
value: clusters.length.toLocaleString(),
|
||||
subtitle: 'keyword groups',
|
||||
icon: <GroupIcon className="w-5 h-5" />,
|
||||
accentColor: 'purple',
|
||||
href: '/planner/clusters',
|
||||
{/* Three Widget Footer - Section 3 Layout */}
|
||||
<ThreeWidgetFooter
|
||||
submoduleColor="amber"
|
||||
pageProgress={{
|
||||
title: 'Page Progress',
|
||||
submoduleColor: 'amber',
|
||||
metrics: [
|
||||
{ label: 'Ideas', value: totalCount },
|
||||
{ label: 'In Tasks', value: ideas.filter(i => i.status === 'queued' || i.status === 'completed').length, percentage: `${totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'queued' || i.status === 'completed').length / totalCount) * 100) : 0}%` },
|
||||
{ label: 'Pending', value: ideas.filter(i => i.status === 'new').length },
|
||||
{ label: 'From Clusters', value: clusters.length },
|
||||
],
|
||||
progress: {
|
||||
value: totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'queued' || i.status === 'completed').length / totalCount) * 100) : 0,
|
||||
label: 'Converted',
|
||||
color: 'amber',
|
||||
},
|
||||
{
|
||||
title: 'Ready to Queue',
|
||||
value: ideas.filter(i => i.status === 'new').length.toLocaleString(),
|
||||
subtitle: 'awaiting approval',
|
||||
icon: <BoltIcon className="w-5 h-5" />,
|
||||
accentColor: 'orange',
|
||||
},
|
||||
{
|
||||
title: 'In Queue',
|
||||
value: ideas.filter(i => i.status === 'queued').length.toLocaleString(),
|
||||
subtitle: 'ready for tasks',
|
||||
icon: <ListIcon className="w-5 h-5" />,
|
||||
accentColor: 'blue',
|
||||
href: '/writer/tasks',
|
||||
},
|
||||
{
|
||||
title: 'Content Created',
|
||||
value: ideas.filter(i => i.status === 'completed').length.toLocaleString(),
|
||||
subtitle: `${totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'completed').length / totalCount) * 100) : 0}% completion`,
|
||||
icon: <BoltIcon className="w-5 h-5" />,
|
||||
accentColor: 'green',
|
||||
href: '/writer/content',
|
||||
},
|
||||
]}
|
||||
progress={{
|
||||
label: 'Idea-to-Content Pipeline: Ideas successfully converted into written content',
|
||||
value: totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'completed').length / totalCount) * 100) : 0,
|
||||
color: 'success',
|
||||
hint: ideas.filter(i => i.status === 'new').length > 0
|
||||
? `${ideas.filter(i => i.status === 'new').length} ideas ready to become tasks`
|
||||
: 'All ideas converted!',
|
||||
}}
|
||||
moduleStats={{
|
||||
title: 'Planner Module',
|
||||
pipeline: [
|
||||
{
|
||||
fromLabel: 'Keywords',
|
||||
fromValue: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0),
|
||||
fromHref: '/planner/keywords',
|
||||
actionLabel: 'Auto Cluster',
|
||||
toLabel: 'Clusters',
|
||||
toValue: clusters.length,
|
||||
toHref: '/planner/clusters',
|
||||
progress: 100,
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
fromLabel: 'Clusters',
|
||||
fromValue: clusters.length,
|
||||
fromHref: '/planner/clusters',
|
||||
actionLabel: 'Generate Ideas',
|
||||
toLabel: 'Ideas',
|
||||
toValue: totalCount,
|
||||
progress: 100,
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
fromLabel: 'Ideas',
|
||||
fromValue: totalCount,
|
||||
actionLabel: 'Create Tasks',
|
||||
toLabel: 'Tasks',
|
||||
toValue: ideas.filter(i => i.status === 'queued' || i.status === 'completed').length,
|
||||
toHref: '/writer/tasks',
|
||||
progress: totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'queued' || i.status === 'completed').length / totalCount) * 100) : 0,
|
||||
color: 'amber',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{ label: 'Keywords', href: '/planner/keywords' },
|
||||
{ label: 'Clusters', href: '/planner/clusters' },
|
||||
{ label: 'Ideas', href: '/planner/ideas' },
|
||||
],
|
||||
}}
|
||||
completion={{
|
||||
title: 'Workflow Completion',
|
||||
plannerItems: [
|
||||
{ label: 'Keywords Clustered', value: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0), color: 'blue' },
|
||||
{ label: 'Clusters Created', value: clusters.length, color: 'green' },
|
||||
{ label: 'Ideas Generated', value: totalCount, color: 'amber' },
|
||||
],
|
||||
writerItems: [
|
||||
{ label: 'Content Generated', value: ideas.filter(i => i.status === 'completed').length, color: 'blue' },
|
||||
{ label: 'Images Created', value: 0, color: 'purple' },
|
||||
{ label: 'Published', value: 0, color: 'green' },
|
||||
],
|
||||
analyticsHref: '/account/usage',
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user