final plolish phase 2

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-27 15:25:05 +00:00
parent 99982eb4fb
commit b9e4b6f7e2
27 changed files with 2229 additions and 280 deletions

View File

@@ -17,8 +17,7 @@ import {
bulkDeleteContent,
} from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, CheckCircleIcon, BoltIcon } from '../../icons';
import { RocketLaunchIcon } from '@heroicons/react/24/outline';
import { CheckCircleIcon, BoltIcon } from '../../icons';
import { createApprovedPageConfig } from '../../config/pages/approved.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
@@ -358,29 +357,87 @@ export default function Approved() {
getItemDisplayName={(row: Content) => row.title || `Content #${row.id}`}
/>
{/* Module Metrics Footer */}
{/* Module Metrics Footer - 3-Widget Layout */}
<ModuleMetricsFooter
metrics={[
{
title: 'Approved Content',
value: content.length.toLocaleString(),
subtitle: 'ready for publishing',
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'green',
submoduleColor="green"
threeWidgetLayout={{
pageProgress: {
title: 'Page Progress',
submoduleColor: 'green',
metrics: [
{ label: 'Total Approved', value: totalCount },
{ label: 'On Site', value: content.filter(c => c.external_id).length, percentage: `${totalCount > 0 ? Math.round((content.filter(c => c.external_id).length / totalCount) * 100) : 0}%` },
{ label: 'Pending Publish', value: content.filter(c => !c.external_id).length },
{ label: 'This Page', value: content.length },
],
progress: {
label: 'Published to Site',
value: totalCount > 0 ? Math.round((content.filter(c => c.external_id).length / totalCount) * 100) : 0,
color: 'green',
},
hint: content.filter(c => !c.external_id).length > 0
? `${content.filter(c => !c.external_id).length} items ready for site publishing`
: 'All approved content published!',
},
{
title: 'Published to Site',
value: content.filter(c => c.external_id).length.toLocaleString(),
subtitle: 'on WordPress',
icon: <RocketLaunchIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/writer/approved',
moduleStats: {
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: 0,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: 0,
toHref: '/writer/content',
progress: 100,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: 0,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: 0,
toHref: '/writer/images',
progress: 100,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: 0,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: totalCount,
progress: totalCount > 0 ? Math.round((content.filter(c => c.external_id).length / totalCount) * 100) : 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/approved' },
],
},
completion: {
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords', value: 0, color: 'blue' },
{ label: 'Clusters', value: 0, color: 'green' },
{ label: 'Ideas', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content', value: 0, color: 'purple' },
{ label: 'Images', value: 0, color: 'amber' },
{ label: 'Published', value: content.filter(c => c.external_id).length, color: 'green' },
],
creditsUsed: 0,
operationsCount: 0,
analyticsHref: '/analytics',
},
]}
progress={{
label: 'Site Publishing Progress',
value: totalCount > 0 ? Math.round((content.filter(c => c.external_id).length / totalCount) * 100) : 0,
color: 'success',
}}
/>
</>

View File

@@ -4,7 +4,7 @@
*/
import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import TablePageTemplate from '../../templates/TablePageTemplate';
import {
fetchContent,
@@ -16,14 +16,13 @@ import {
} from '../../services/api';
import { optimizerApi } from '../../api/optimizer.api';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, TaskIcon, CheckCircleIcon, ArrowRightIcon } 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 ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import ModuleMetricsFooter from '../../components/dashboard/ModuleMetricsFooter';
import { PencilSquareIcon } from '@heroicons/react/24/outline';
export default function Content() {
@@ -275,45 +274,86 @@ export default function Content() {
getItemDisplayName={(row: ContentType) => row.title || `Content #${row.id}`}
/>
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
{/* Module Metrics Footer - 3-Widget Layout */}
<ModuleMetricsFooter
metrics={[
{
title: 'Tasks',
value: content.length.toLocaleString(),
subtitle: 'generated from queue',
icon: <TaskIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/writer/tasks',
submoduleColor="purple"
threeWidgetLayout={{
pageProgress: {
title: 'Page Progress',
submoduleColor: 'purple',
metrics: [
{ label: 'Total Content', value: totalCount },
{ label: 'Draft', value: content.filter(c => c.status === 'draft').length },
{ label: 'In Review', value: content.filter(c => c.status === 'review').length },
{ label: 'Published', value: content.filter(c => c.status === 'published').length, percentage: `${totalCount > 0 ? Math.round((content.filter(c => c.status === 'published').length / totalCount) * 100) : 0}%` },
],
progress: {
label: 'Published',
value: totalCount > 0 ? Math.round((content.filter(c => c.status === 'published').length / totalCount) * 100) : 0,
color: 'green',
},
hint: content.filter(c => c.status === 'draft').length > 0
? `${content.filter(c => c.status === 'draft').length} drafts need images before review`
: 'All content processed!',
},
{
title: 'Draft',
value: content.filter(c => c.status === 'draft').length.toLocaleString(),
subtitle: 'needs editing',
icon: <FileIcon className="w-5 h-5" />,
accentColor: 'amber',
moduleStats: {
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: totalCount,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: content.filter(c => c.status === 'draft').length,
progress: 100,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: content.filter(c => c.status === 'draft').length,
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: 0,
toHref: '/writer/images',
progress: totalCount > 0 ? Math.round((content.filter(c => c.status !== 'draft').length / totalCount) * 100) : 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: content.filter(c => c.status === 'review').length,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: content.filter(c => c.status === 'published').length,
toHref: '/writer/approved',
progress: totalCount > 0 ? Math.round((content.filter(c => c.status === 'published').length / totalCount) * 100) : 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/approved' },
],
},
{
title: 'In Review',
value: content.filter(c => c.status === 'review').length.toLocaleString(),
subtitle: 'awaiting approval',
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/writer/review',
completion: {
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords', value: 0, color: 'blue' },
{ label: 'Clusters', value: 0, color: 'green' },
{ label: 'Ideas', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content', value: totalCount, color: 'purple' },
{ label: 'Images', value: 0, color: 'amber' },
{ label: 'Published', value: content.filter(c => c.status === 'published').length, color: 'green' },
],
creditsUsed: 0,
operationsCount: 0,
analyticsHref: '/analytics',
},
{
title: 'Published',
value: content.filter(c => c.status === 'published').length.toLocaleString(),
subtitle: 'ready for sync',
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'green',
href: '/writer/published',
},
]}
progress={{
label: 'Content Publishing Pipeline: Content moved from draft through review to published (Draft \u2192 Review \u2192 Published)',
value: totalCount > 0 ? Math.round((content.filter(c => c.status === 'published').length / totalCount) * 100) : 0,
color: 'success',
}}
/>

View File

@@ -455,15 +455,86 @@ export default function Review() {
onRowAction={handleRowAction}
/>
<ModuleMetricsFooter
metrics={[
{
title: 'Ready to Publish',
value: content.length,
subtitle: 'Total review items',
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'blue',
submoduleColor="amber"
threeWidgetLayout={{
pageProgress: {
title: 'Page Progress',
submoduleColor: 'amber',
metrics: [
{ label: 'In Review', value: totalCount },
{ label: 'This Page', value: content.length },
{ label: 'Ready', value: content.filter(c => c.word_count && c.word_count > 0).length, percentage: `${totalCount > 0 ? Math.round((content.filter(c => c.word_count && c.word_count > 0).length / totalCount) * 100) : 0}%` },
{ label: 'Pending', value: content.filter(c => !c.word_count || c.word_count === 0).length },
],
progress: {
label: 'Ready for Approval',
value: totalCount > 0 ? Math.round((content.filter(c => c.word_count && c.word_count > 0).length / totalCount) * 100) : 0,
color: 'amber',
},
hint: totalCount > 0
? `${totalCount} items in review queue awaiting approval`
: 'No items in review queue',
},
]}
moduleStats: {
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: 0,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: 0,
toHref: '/writer/content',
progress: 100,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: 0,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: 0,
toHref: '/writer/images',
progress: 100,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: totalCount,
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: 0,
toHref: '/writer/approved',
progress: 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/approved' },
],
},
completion: {
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords', value: 0, color: 'blue' },
{ label: 'Clusters', value: 0, color: 'green' },
{ label: 'Ideas', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content', value: 0, color: 'purple' },
{ label: 'In Review', value: totalCount, color: 'amber' },
{ label: 'Published', value: 0, color: 'green' },
],
creditsUsed: 0,
operationsCount: 0,
analyticsHref: '/analytics',
},
}}
/>
</>
);

View File

@@ -30,7 +30,7 @@ import { createTasksPageConfig } from '../../config/pages/tasks.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 ModuleMetricsFooter from '../../components/dashboard/ModuleMetricsFooter';
import { DocumentTextIcon } from '@heroicons/react/24/outline';
export default function Tasks() {
@@ -467,44 +467,89 @@ export default function Tasks() {
}}
/>
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
{/* Module Metrics Footer - 3-Widget Layout */}
<ModuleMetricsFooter
metrics={[
{
title: 'Ideas',
value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0).toLocaleString(),
subtitle: 'from planner',
icon: <TaskIcon className="w-5 h-5" />,
accentColor: 'orange',
href: '/planner/ideas',
submoduleColor="blue"
threeWidgetLayout={{
// Widget 1: Page Progress (Tasks)
pageProgress: {
title: 'Page Progress',
submoduleColor: 'blue',
metrics: [
{ label: 'Total', value: totalCount },
{ label: 'Complete', value: tasks.filter(t => t.status === 'completed').length, percentage: `${totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0}%` },
{ label: 'Queue', value: tasks.filter(t => t.status === 'queued').length },
{ label: 'Processing', value: tasks.filter(t => t.status === 'in_progress').length },
],
progress: {
value: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
label: 'Generated',
color: 'blue',
},
hint: tasks.filter(t => t.status === 'queued').length > 0
? `${tasks.filter(t => t.status === 'queued').length} tasks in queue for content generation`
: 'All tasks processed!',
},
{
title: 'In Queue',
value: tasks.filter(t => t.status === 'queued').length.toLocaleString(),
subtitle: 'waiting for processing',
icon: <TaskIcon className="w-5 h-5" />,
accentColor: 'amber',
// Widget 2: Module Stats (Writer Pipeline)
moduleStats: {
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: totalCount,
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: tasks.filter(t => t.status === 'completed').length,
toHref: '/writer/content',
progress: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: tasks.filter(t => t.status === 'completed').length,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: 0,
toHref: '/writer/images',
progress: 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: 0,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: 0,
toHref: '/writer/approved',
progress: 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/approved' },
],
},
{
title: 'Processing',
value: tasks.filter(t => t.status === 'in_progress').length.toLocaleString(),
subtitle: 'generating content',
icon: <TaskIcon className="w-5 h-5" />,
accentColor: 'blue',
// Widget 3: Completion Stats
completion: {
title: 'Workflow Completion',
plannerItems: [
{ label: 'Clusters', value: clusters.length, color: 'green' },
{ label: 'Ideas', value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0), color: 'amber' },
],
writerItems: [
{ label: 'Tasks', value: totalCount, color: 'blue' },
{ label: 'Content', value: tasks.filter(t => t.status === 'completed').length, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
creditsUsed: 0,
operationsCount: 0,
analyticsHref: '/analytics',
},
{
title: 'Ready for Review',
value: tasks.filter(t => t.status === 'completed').length.toLocaleString(),
subtitle: 'content generated',
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'green',
href: '/writer/content',
},
]}
progress={{
label: 'Content Generation Pipeline: Tasks successfully completed (Queued → Processing → Completed)',
value: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
color: 'success',
}}
/>