header footer metrics update and credits by site fixes
This commit is contained in:
@@ -296,24 +296,45 @@ export function createApprovedPageConfig(params: {
|
|||||||
|
|
||||||
const headerMetrics: HeaderMetricConfig[] = [
|
const headerMetrics: HeaderMetricConfig[] = [
|
||||||
{
|
{
|
||||||
label: 'Approved',
|
label: 'Content',
|
||||||
accentColor: 'green',
|
|
||||||
calculate: (data: { totalCount: number }) => data.totalCount,
|
|
||||||
tooltip: 'Total approved content ready for publishing.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'On Site',
|
|
||||||
accentColor: 'blue',
|
accentColor: 'blue',
|
||||||
calculate: (data: { content: Content[] }) =>
|
calculate: (data: { totalCount: number }) => data.totalCount,
|
||||||
data.content.filter(c => c.external_id).length,
|
tooltip: 'Total content items tracked. Overall volume across all stages.',
|
||||||
tooltip: 'Content published to your website.',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Pending',
|
label: 'Draft',
|
||||||
accentColor: 'amber',
|
accentColor: 'amber',
|
||||||
calculate: (data: { content: Content[] }) =>
|
calculate: (data: { content: Content[] }) =>
|
||||||
data.content.filter(c => !c.external_id).length,
|
data.content.filter(c => c.status === 'draft').length,
|
||||||
tooltip: 'Approved content not yet published to site.',
|
tooltip: 'Content written, images not generated. Generate images to move to review.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'In Review',
|
||||||
|
accentColor: 'purple',
|
||||||
|
calculate: (data: { content: Content[] }) =>
|
||||||
|
data.content.filter(c => c.status === 'review').length,
|
||||||
|
tooltip: 'Images generated, awaiting approval. Review and approve to publish.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Approved',
|
||||||
|
accentColor: 'green',
|
||||||
|
calculate: (data: { content: Content[] }) =>
|
||||||
|
data.content.filter(c => c.status === 'approved').length,
|
||||||
|
tooltip: 'Approved content awaiting publishing. Publish to site when ready.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Published',
|
||||||
|
accentColor: 'green',
|
||||||
|
calculate: (data: { content: Content[] }) =>
|
||||||
|
data.content.filter(c => c.status === 'published').length,
|
||||||
|
tooltip: 'Live content on your website. Successfully published and accessible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Images',
|
||||||
|
accentColor: 'blue',
|
||||||
|
calculate: (data: { content: Content[] }) =>
|
||||||
|
data.content.filter(c => c.has_generated_images).length,
|
||||||
|
tooltip: 'Total images generated across all content. Tracks visual asset coverage.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -456,28 +456,42 @@ export const createContentPageConfig = (
|
|||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'blue' as const,
|
accentColor: 'blue' as const,
|
||||||
calculate: (data) => data.totalCount || 0,
|
calculate: (data) => data.totalCount || 0,
|
||||||
tooltip: 'Total content pieces generated. Includes drafts, review, and published content.',
|
tooltip: 'Total content items tracked. Overall volume across all stages.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Draft',
|
label: 'Draft',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'amber' as const,
|
accentColor: 'amber' as const,
|
||||||
calculate: (data) => data.content.filter((c: Content) => c.status === 'draft').length,
|
calculate: (data) => data.content.filter((c: Content) => c.status === 'draft').length,
|
||||||
tooltip: 'Content in draft stage. Edit and refine before moving to review.',
|
tooltip: 'Content written, images not generated. Generate images to move to review.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'In Review',
|
label: 'In Review',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'blue' as const,
|
accentColor: 'purple' as const,
|
||||||
calculate: (data) => data.content.filter((c: Content) => c.status === 'review').length,
|
calculate: (data) => data.content.filter((c: Content) => c.status === 'review').length,
|
||||||
tooltip: 'Content awaiting review and approval. Review for quality before publishing.',
|
tooltip: 'Images generated, awaiting approval. Review and approve to publish.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Approved',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'green' as const,
|
||||||
|
calculate: (data) => data.content.filter((c: Content) => c.status === 'approved').length,
|
||||||
|
tooltip: 'Approved content awaiting publishing. Publish to site when ready.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Published',
|
label: 'Published',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'green' as const,
|
accentColor: 'green' as const,
|
||||||
calculate: (data) => data.content.filter((c: Content) => c.status === 'published').length,
|
calculate: (data) => data.content.filter((c: Content) => c.status === 'published').length,
|
||||||
tooltip: 'Published content ready for WordPress sync. Track your published library.',
|
tooltip: 'Live content on your website. Successfully published and accessible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Images',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'blue' as const,
|
||||||
|
calculate: (data) => 0,
|
||||||
|
tooltip: 'Total images generated across all content. Tracks visual asset coverage.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -207,28 +207,42 @@ export const createImagesPageConfig = (
|
|||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'blue' as const,
|
accentColor: 'blue' as const,
|
||||||
calculate: (data) => data.totalCount || 0,
|
calculate: (data) => data.totalCount || 0,
|
||||||
tooltip: 'Total content pieces with image generation. Track image coverage across all content.',
|
tooltip: 'Total content items tracked. Overall volume across all stages.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Complete',
|
label: 'Draft',
|
||||||
value: 0,
|
|
||||||
accentColor: 'green' as const,
|
|
||||||
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'complete').length,
|
|
||||||
tooltip: 'Content with all images generated. Ready for publishing with full visual coverage.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Partial',
|
|
||||||
value: 0,
|
|
||||||
accentColor: 'blue' as const,
|
|
||||||
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'partial').length,
|
|
||||||
tooltip: 'Content with some images missing. Generate remaining images to complete visual assets.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Pending',
|
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'amber' as const,
|
accentColor: 'amber' as const,
|
||||||
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'pending').length,
|
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.content_status === 'draft').length,
|
||||||
tooltip: 'Content waiting for image generation. Queue these to start creating visual assets.',
|
tooltip: 'Content written, images not generated. Generate images to move to review.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'In Review',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'purple' as const,
|
||||||
|
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.content_status === 'review').length,
|
||||||
|
tooltip: 'Images generated, awaiting approval. Review and approve to publish.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Approved',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'green' as const,
|
||||||
|
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.content_status === 'approved').length,
|
||||||
|
tooltip: 'Approved content awaiting publishing. Publish to site when ready.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Published',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'green' as const,
|
||||||
|
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.content_status === 'published').length,
|
||||||
|
tooltip: 'Live content on your website. Successfully published and accessible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Images',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'blue' as const,
|
||||||
|
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'complete').length,
|
||||||
|
tooltip: 'Total images generated across all content. Tracks visual asset coverage.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
maxInArticleImages: maxImages,
|
maxInArticleImages: maxImages,
|
||||||
|
|||||||
@@ -259,28 +259,40 @@ export function createReviewPageConfig(params: {
|
|||||||
],
|
],
|
||||||
headerMetrics: [
|
headerMetrics: [
|
||||||
{
|
{
|
||||||
label: 'Ready',
|
label: 'Content',
|
||||||
accentColor: 'blue',
|
accentColor: 'blue',
|
||||||
calculate: ({ totalCount }) => totalCount,
|
calculate: ({ totalCount }) => totalCount,
|
||||||
tooltip: 'Content ready for final review. Review quality, SEO, and images before publishing.',
|
tooltip: 'Total content items tracked. Overall volume across all stages.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Images',
|
label: 'Draft',
|
||||||
accentColor: 'green',
|
|
||||||
calculate: ({ content }) => content.filter(c => c.has_generated_images).length,
|
|
||||||
tooltip: 'Content with generated images. Visual assets complete and ready for review.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Optimized',
|
|
||||||
accentColor: 'purple',
|
|
||||||
calculate: ({ content }) => content.filter(c => c.optimization_scores && c.optimization_scores.overall_score >= 80).length,
|
|
||||||
tooltip: 'Content with high SEO optimization scores (80%+). Well-optimized for search engines.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Sync Ready',
|
|
||||||
accentColor: 'amber',
|
accentColor: 'amber',
|
||||||
calculate: ({ content }) => content.filter(c => c.has_generated_images && c.optimization_scores && c.optimization_scores.overall_score >= 70).length,
|
calculate: ({ content }) => content.filter(c => c.status === 'draft').length,
|
||||||
tooltip: 'Content ready for WordPress sync. Has images and good optimization score.',
|
tooltip: 'Content written, images not generated. Generate images to move to review.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'In Review',
|
||||||
|
accentColor: 'purple',
|
||||||
|
calculate: ({ content }) => content.filter(c => c.status === 'review').length,
|
||||||
|
tooltip: 'Images generated, awaiting approval. Review and approve to publish.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Approved',
|
||||||
|
accentColor: 'green',
|
||||||
|
calculate: ({ content }) => content.filter(c => c.status === 'approved').length,
|
||||||
|
tooltip: 'Approved content awaiting publishing. Publish to site when ready.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Published',
|
||||||
|
accentColor: 'green',
|
||||||
|
calculate: ({ content }) => content.filter(c => c.status === 'published').length,
|
||||||
|
tooltip: 'Live content on your website. Successfully published and accessible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Images',
|
||||||
|
accentColor: 'blue',
|
||||||
|
calculate: ({ content }) => content.filter(c => c.has_generated_images).length,
|
||||||
|
tooltip: 'Total images generated across all content. Tracks visual asset coverage.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -454,39 +454,46 @@ export const createTasksPageConfig = (
|
|||||||
],
|
],
|
||||||
headerMetrics: [
|
headerMetrics: [
|
||||||
{
|
{
|
||||||
label: 'Tasks',
|
label: 'Content',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'blue' as const,
|
accentColor: 'blue' as const,
|
||||||
calculate: (data) => data.totalCount || 0,
|
calculate: (data) => data.totalCount || 0,
|
||||||
tooltip: 'Total content generation tasks. Tasks process ideas into written content automatically.',
|
tooltip: 'Total content items tracked. Overall volume across all stages.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'In Queue',
|
label: 'Draft',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'amber' as const,
|
accentColor: 'amber' as const,
|
||||||
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'queued').length,
|
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'draft').length,
|
||||||
tooltip: 'Tasks queued for processing. These will be picked up by the content generation system.',
|
tooltip: 'Content written, images not generated. Generate images to move to review.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Processing',
|
label: 'In Review',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'blue' as const,
|
accentColor: 'purple' as const,
|
||||||
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'in_progress').length,
|
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'review').length,
|
||||||
tooltip: 'Tasks currently being processed. Content is being generated by AI right now.',
|
tooltip: 'Images generated, awaiting approval. Review and approve to publish.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Completed',
|
label: 'Approved',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'green' as const,
|
accentColor: 'green' as const,
|
||||||
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'completed').length,
|
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'approved').length,
|
||||||
tooltip: 'Successfully completed tasks. Generated content is ready for review and publishing.',
|
tooltip: 'Approved content awaiting publishing. Publish to site when ready.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Failed',
|
label: 'Published',
|
||||||
value: 0,
|
value: 0,
|
||||||
accentColor: 'red' as const,
|
accentColor: 'green' as const,
|
||||||
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'failed').length,
|
calculate: (data) => data.tasks.filter((t: Task) => t.status === 'published').length,
|
||||||
tooltip: 'Failed tasks that need attention. Review error logs and retry or modify the task.',
|
tooltip: 'Live content on your website. Successfully published and accessible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Images',
|
||||||
|
value: 0,
|
||||||
|
accentColor: 'blue' as const,
|
||||||
|
calculate: (data) => 0,
|
||||||
|
tooltip: 'Total images generated across all content. Tracks visual asset coverage.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export function useModuleStats() {
|
|||||||
// Total images
|
// Total images
|
||||||
fetchImages({ ...baseFilters }),
|
fetchImages({ ...baseFilters }),
|
||||||
// Credits usage from billing summary
|
// Credits usage from billing summary
|
||||||
fetchAPI('/v1/billing/credits/usage/summary/').catch(() => ({
|
fetchAPI(`/v1/billing/credits/usage/summary/?site_id=${activeSite.id}${activeSector?.id ? `§or_id=${activeSector.id}` : ''}`).catch(() => ({
|
||||||
data: { by_operation: {} }
|
data: { by_operation: {} }
|
||||||
})),
|
})),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { FileIcon, CheckCircleIcon, BoltIcon } from '../../icons';
|
|||||||
import { RocketLaunchIcon } from '@heroicons/react/24/outline';
|
import { RocketLaunchIcon } from '@heroicons/react/24/outline';
|
||||||
import { createApprovedPageConfig } from '../../config/pages/approved.config';
|
import { createApprovedPageConfig } from '../../config/pages/approved.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
|
import { useSiteStore } from '../../store/siteStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
||||||
@@ -30,6 +31,7 @@ export default function Approved() {
|
|||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { activeSector } = useSectorStore();
|
const { activeSector } = useSectorStore();
|
||||||
|
const { activeSite } = useSiteStore();
|
||||||
const { pageSize } = usePageSizeStore();
|
const { pageSize } = usePageSizeStore();
|
||||||
|
|
||||||
// Data state
|
// Data state
|
||||||
@@ -37,8 +39,11 @@ export default function Approved() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Total counts for footer widget and header metrics (not page-filtered)
|
// Total counts for footer widget and header metrics (not page-filtered)
|
||||||
const [totalOnSite, setTotalOnSite] = useState(0);
|
const [totalContent, setTotalContent] = useState(0);
|
||||||
const [totalPendingPublish, setTotalPendingPublish] = useState(0);
|
const [totalDraft, setTotalDraft] = useState(0);
|
||||||
|
const [totalReview, setTotalReview] = useState(0);
|
||||||
|
const [totalApproved, setTotalApproved] = useState(0);
|
||||||
|
const [totalPublished, setTotalPublished] = useState(0);
|
||||||
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||||
|
|
||||||
// Filter state - default to approved status
|
// Filter state - default to approved status
|
||||||
@@ -59,27 +64,26 @@ export default function Approved() {
|
|||||||
// Load total metrics for footer widget and header metrics (not affected by pagination)
|
// Load total metrics for footer widget and header metrics (not affected by pagination)
|
||||||
const loadTotalMetrics = useCallback(async () => {
|
const loadTotalMetrics = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch all approved+published content to calculate totals
|
// Fetch counts in parallel for performance
|
||||||
const data = await fetchContent({
|
const [allRes, draftRes, reviewRes, approvedRes, publishedRes, imagesRes] = await Promise.all([
|
||||||
status__in: 'approved,published', // Both approved and published content
|
fetchContent({ page_size: 1, site_id: activeSite?.id }),
|
||||||
page_size: 1000, // Fetch enough to count
|
fetchContent({ page_size: 1, status: 'draft', site_id: activeSite?.id }),
|
||||||
});
|
fetchContent({ page_size: 1, status: 'review', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'approved', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'published', site_id: activeSite?.id }),
|
||||||
|
fetchImages({ page_size: 1, site_id: activeSite?.id }),
|
||||||
|
]);
|
||||||
|
|
||||||
const allContent = data.results || [];
|
setTotalContent(allRes.count || 0);
|
||||||
// Count by external_id presence
|
setTotalDraft(draftRes.count || 0);
|
||||||
const onSite = allContent.filter(c => c.external_id).length;
|
setTotalReview(reviewRes.count || 0);
|
||||||
const pending = allContent.filter(c => !c.external_id).length;
|
setTotalApproved(approvedRes.count || 0);
|
||||||
|
setTotalPublished(publishedRes.count || 0);
|
||||||
setTotalOnSite(onSite);
|
|
||||||
setTotalPendingPublish(pending);
|
|
||||||
|
|
||||||
// Get actual total images count
|
|
||||||
const imagesRes = await fetchImages({ page_size: 1 });
|
|
||||||
setTotalImagesCount(imagesRes.count || 0);
|
setTotalImagesCount(imagesRes.count || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading total metrics:', error);
|
console.error('Error loading total metrics:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [activeSite]);
|
||||||
|
|
||||||
// Load total metrics on mount
|
// Load total metrics on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -342,16 +346,23 @@ export default function Approved() {
|
|||||||
let value: number;
|
let value: number;
|
||||||
|
|
||||||
switch (metric.label) {
|
switch (metric.label) {
|
||||||
|
case 'Content':
|
||||||
|
value = totalContent || 0;
|
||||||
|
break;
|
||||||
|
case 'Draft':
|
||||||
|
value = totalDraft;
|
||||||
|
break;
|
||||||
|
case 'In Review':
|
||||||
|
value = totalReview;
|
||||||
|
break;
|
||||||
case 'Approved':
|
case 'Approved':
|
||||||
value = totalCount || 0;
|
value = totalApproved;
|
||||||
break;
|
break;
|
||||||
case 'On Site':
|
case 'Published':
|
||||||
// Use totalOnSite from loadTotalMetrics()
|
value = totalPublished;
|
||||||
value = totalOnSite;
|
|
||||||
break;
|
break;
|
||||||
case 'Pending':
|
case 'Total Images':
|
||||||
// Use totalPendingPublish from loadTotalMetrics()
|
value = totalImagesCount;
|
||||||
value = totalPendingPublish;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
value = metric.calculate({ content, totalCount });
|
value = metric.calculate({ content, totalCount });
|
||||||
@@ -361,9 +372,10 @@ export default function Approved() {
|
|||||||
label: metric.label,
|
label: metric.label,
|
||||||
value,
|
value,
|
||||||
accentColor: metric.accentColor,
|
accentColor: metric.accentColor,
|
||||||
|
tooltip: (metric as any).tooltip,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [pageConfig?.headerMetrics, content, totalCount, totalOnSite, totalPendingPublish]);
|
}, [pageConfig?.headerMetrics, content, totalCount, totalContent, totalDraft, totalReview, totalApproved, totalPublished, totalImagesCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ export default function Content() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Total counts for footer widget and header metrics (not page-filtered)
|
// Total counts for footer widget and header metrics (not page-filtered)
|
||||||
|
const [totalContent, setTotalContent] = useState(0);
|
||||||
const [totalDraft, setTotalDraft] = useState(0);
|
const [totalDraft, setTotalDraft] = useState(0);
|
||||||
const [totalReview, setTotalReview] = useState(0);
|
const [totalReview, setTotalReview] = useState(0);
|
||||||
|
const [totalApproved, setTotalApproved] = useState(0);
|
||||||
const [totalPublished, setTotalPublished] = useState(0);
|
const [totalPublished, setTotalPublished] = useState(0);
|
||||||
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ export default function Content() {
|
|||||||
const loadTotalMetrics = useCallback(async () => {
|
const loadTotalMetrics = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Batch all API calls in parallel for better performance
|
// Batch all API calls in parallel for better performance
|
||||||
const [allRes, draftRes, reviewRes, publishedRes, imagesRes] = await Promise.all([
|
const [allRes, draftRes, reviewRes, approvedRes, publishedRes, imagesRes] = await Promise.all([
|
||||||
// Get all content (site-wide)
|
// Get all content (site-wide)
|
||||||
fetchContent({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
@@ -86,19 +88,26 @@ export default function Content() {
|
|||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status: 'review',
|
status: 'review',
|
||||||
}),
|
}),
|
||||||
// Get content with status='approved' or 'published' (ready for publishing or on site)
|
// Get content with status='approved'
|
||||||
fetchContent({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status__in: 'approved,published',
|
status: 'approved',
|
||||||
|
}),
|
||||||
|
// Get content with status='published'
|
||||||
|
fetchContent({
|
||||||
|
page_size: 1,
|
||||||
|
site_id: activeSite?.id,
|
||||||
|
status: 'published',
|
||||||
}),
|
}),
|
||||||
// Get actual total images count
|
// Get actual total images count
|
||||||
fetchImages({ page_size: 1 }),
|
fetchImages({ page_size: 1, site_id: activeSite?.id }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setTotalCount(allRes.count || 0);
|
setTotalContent(allRes.count || 0);
|
||||||
setTotalDraft(draftRes.count || 0);
|
setTotalDraft(draftRes.count || 0);
|
||||||
setTotalReview(reviewRes.count || 0);
|
setTotalReview(reviewRes.count || 0);
|
||||||
|
setTotalApproved(approvedRes.count || 0);
|
||||||
setTotalPublished(publishedRes.count || 0);
|
setTotalPublished(publishedRes.count || 0);
|
||||||
setTotalImagesCount(imagesRes.count || 0);
|
setTotalImagesCount(imagesRes.count || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -226,20 +235,23 @@ export default function Content() {
|
|||||||
|
|
||||||
switch (metric.label) {
|
switch (metric.label) {
|
||||||
case 'Content':
|
case 'Content':
|
||||||
value = totalCount || 0;
|
value = totalContent || 0;
|
||||||
break;
|
break;
|
||||||
case 'Draft':
|
case 'Draft':
|
||||||
// Use totalDraft from loadTotalMetrics()
|
|
||||||
value = totalDraft;
|
value = totalDraft;
|
||||||
break;
|
break;
|
||||||
case 'In Review':
|
case 'In Review':
|
||||||
// Use totalReview from loadTotalMetrics()
|
|
||||||
value = totalReview;
|
value = totalReview;
|
||||||
break;
|
break;
|
||||||
|
case 'Approved':
|
||||||
|
value = totalApproved;
|
||||||
|
break;
|
||||||
case 'Published':
|
case 'Published':
|
||||||
// Use totalPublished from loadTotalMetrics()
|
|
||||||
value = totalPublished;
|
value = totalPublished;
|
||||||
break;
|
break;
|
||||||
|
case 'Total Images':
|
||||||
|
value = totalImagesCount;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
value = metric.calculate({ content, totalCount });
|
value = metric.calculate({ content, totalCount });
|
||||||
}
|
}
|
||||||
@@ -251,7 +263,7 @@ export default function Content() {
|
|||||||
tooltip: (metric as any).tooltip,
|
tooltip: (metric as any).tooltip,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [pageConfig?.headerMetrics, content, totalCount, totalDraft, totalReview, totalPublished]);
|
}, [pageConfig?.headerMetrics, content, totalContent, totalDraft, totalReview, totalApproved, totalPublished, totalImagesCount]);
|
||||||
|
|
||||||
const handleRowAction = useCallback(async (action: string, row: ContentType) => {
|
const handleRowAction = useCallback(async (action: string, row: ContentType) => {
|
||||||
if (action === 'view_on_wordpress') {
|
if (action === 'view_on_wordpress') {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import TablePageTemplate from '../../templates/TablePageTemplate';
|
|||||||
import {
|
import {
|
||||||
fetchContentImages,
|
fetchContentImages,
|
||||||
fetchImages,
|
fetchImages,
|
||||||
|
fetchContent,
|
||||||
ContentImagesGroup,
|
ContentImagesGroup,
|
||||||
ContentImagesResponse,
|
ContentImagesResponse,
|
||||||
fetchImageGenerationSettings,
|
fetchImageGenerationSettings,
|
||||||
@@ -28,19 +29,28 @@ import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordS
|
|||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import { Modal } from '../../components/ui/modal';
|
import { Modal } from '../../components/ui/modal';
|
||||||
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
||||||
|
import { useSiteStore } from '../../store/siteStore';
|
||||||
|
|
||||||
export default function Images() {
|
export default function Images() {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const { activeSite } = useSiteStore();
|
||||||
|
|
||||||
// Data state
|
// Data state
|
||||||
const [images, setImages] = useState<ContentImagesGroup[]>([]);
|
const [images, setImages] = useState<ContentImagesGroup[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Total counts for footer widget and header metrics (not page-filtered)
|
// Total counts for footer widget and header metrics (not page-filtered)
|
||||||
|
const [totalContent, setTotalContent] = useState(0);
|
||||||
|
const [totalDraft, setTotalDraft] = useState(0);
|
||||||
|
const [totalReview, setTotalReview] = useState(0);
|
||||||
|
const [totalApproved, setTotalApproved] = useState(0);
|
||||||
|
const [totalPublished, setTotalPublished] = useState(0);
|
||||||
|
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||||
|
|
||||||
|
// Footer widget specific counts (image-based)
|
||||||
const [totalComplete, setTotalComplete] = useState(0);
|
const [totalComplete, setTotalComplete] = useState(0);
|
||||||
const [totalPartial, setTotalPartial] = useState(0);
|
const [totalPartial, setTotalPartial] = useState(0);
|
||||||
const [totalPending, setTotalPending] = useState(0);
|
const [totalPending, setTotalPending] = useState(0);
|
||||||
const [totalImagesCount, setTotalImagesCount] = useState(0); // Actual images count
|
|
||||||
|
|
||||||
// Filter state
|
// Filter state
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
@@ -79,40 +89,26 @@ export default function Images() {
|
|||||||
// Load total metrics for footer widget and header metrics (not affected by pagination)
|
// Load total metrics for footer widget and header metrics (not affected by pagination)
|
||||||
const loadTotalMetrics = useCallback(async () => {
|
const loadTotalMetrics = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch content-grouped images for status counts
|
// Fetch counts in parallel for performance
|
||||||
const data: ContentImagesResponse = await fetchContentImages({});
|
const [allRes, draftRes, reviewRes, approvedRes, publishedRes, imagesRes] = await Promise.all([
|
||||||
const allImages = data.results || [];
|
fetchContent({ page_size: 1, site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'draft', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'review', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'approved', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'published', site_id: activeSite?.id }),
|
||||||
|
fetchImages({ page_size: 1, site_id: activeSite?.id }),
|
||||||
|
]);
|
||||||
|
|
||||||
// Count by overall_status (content-level status)
|
setTotalContent(allRes.count || 0);
|
||||||
let complete = 0;
|
setTotalDraft(draftRes.count || 0);
|
||||||
let partial = 0;
|
setTotalReview(reviewRes.count || 0);
|
||||||
let pending = 0;
|
setTotalApproved(approvedRes.count || 0);
|
||||||
|
setTotalPublished(publishedRes.count || 0);
|
||||||
allImages.forEach(img => {
|
setTotalImagesCount(imagesRes.count || 0);
|
||||||
switch (img.overall_status) {
|
|
||||||
case 'complete':
|
|
||||||
complete++;
|
|
||||||
break;
|
|
||||||
case 'partial':
|
|
||||||
partial++;
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
pending++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setTotalComplete(complete);
|
|
||||||
setTotalPartial(partial);
|
|
||||||
setTotalPending(pending);
|
|
||||||
|
|
||||||
// Fetch ACTUAL total images count from the images endpoint
|
|
||||||
const imagesData = await fetchImages({ page_size: 1 });
|
|
||||||
setTotalImagesCount(imagesData.count || 0);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading total metrics:', error);
|
console.error('Error loading total metrics:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [activeSite]);
|
||||||
|
|
||||||
// Load total metrics on mount
|
// Load total metrics on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -502,19 +498,22 @@ export default function Images() {
|
|||||||
|
|
||||||
switch (metric.label) {
|
switch (metric.label) {
|
||||||
case 'Content':
|
case 'Content':
|
||||||
value = totalCount || 0;
|
value = totalContent || 0;
|
||||||
break;
|
break;
|
||||||
case 'Complete':
|
case 'Draft':
|
||||||
// Use totalComplete from loadTotalMetrics()
|
value = totalDraft;
|
||||||
value = totalComplete;
|
|
||||||
break;
|
break;
|
||||||
case 'Partial':
|
case 'In Review':
|
||||||
// Use totalPartial from loadTotalMetrics()
|
value = totalReview;
|
||||||
value = totalPartial;
|
|
||||||
break;
|
break;
|
||||||
case 'Pending':
|
case 'Approved':
|
||||||
// Use totalPending from loadTotalMetrics()
|
value = totalApproved;
|
||||||
value = totalPending;
|
break;
|
||||||
|
case 'Published':
|
||||||
|
value = totalPublished;
|
||||||
|
break;
|
||||||
|
case 'Total Images':
|
||||||
|
value = totalImagesCount;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
value = metric.calculate({ images, totalCount });
|
value = metric.calculate({ images, totalCount });
|
||||||
@@ -528,16 +527,8 @@ export default function Images() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add total images count metric
|
|
||||||
baseMetrics.push({
|
|
||||||
label: 'Total Images',
|
|
||||||
value: totalImagesCount,
|
|
||||||
accentColor: 'purple' as const,
|
|
||||||
tooltip: 'Total number of images across all content',
|
|
||||||
});
|
|
||||||
|
|
||||||
return baseMetrics;
|
return baseMetrics;
|
||||||
}, [pageConfig?.headerMetrics, images, totalCount, totalComplete, totalPartial, totalPending, totalImagesCount]);
|
}, [pageConfig?.headerMetrics, images, totalCount, totalContent, totalDraft, totalReview, totalApproved, totalPublished, totalImagesCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { CheckCircleIcon } from '../../icons';
|
|||||||
import { ClipboardDocumentCheckIcon } from '@heroicons/react/24/outline';
|
import { ClipboardDocumentCheckIcon } from '@heroicons/react/24/outline';
|
||||||
import { createReviewPageConfig } from '../../config/pages/review.config';
|
import { createReviewPageConfig } from '../../config/pages/review.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
|
import { useSiteStore } from '../../store/siteStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
|
||||||
@@ -27,17 +28,20 @@ export default function Review() {
|
|||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { activeSector } = useSectorStore();
|
const { activeSector } = useSectorStore();
|
||||||
|
const { activeSite } = useSiteStore();
|
||||||
const { pageSize } = usePageSizeStore();
|
const { pageSize } = usePageSizeStore();
|
||||||
|
|
||||||
// Data state
|
// Data state
|
||||||
const [content, setContent] = useState<Content[]>([]);
|
const [content, setContent] = useState<Content[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
|
||||||
|
|
||||||
// Total metrics for footer widget (not page-filtered)
|
// Total metrics for footer widget and header metrics (not page-filtered)
|
||||||
const [totalDrafts, setTotalDrafts] = useState(0);
|
const [totalContent, setTotalContent] = useState(0);
|
||||||
|
const [totalDraft, setTotalDraft] = useState(0);
|
||||||
|
const [totalReview, setTotalReview] = useState(0);
|
||||||
const [totalApproved, setTotalApproved] = useState(0);
|
const [totalApproved, setTotalApproved] = useState(0);
|
||||||
const [totalTasks, setTotalTasks] = useState(0);
|
const [totalPublished, setTotalPublished] = useState(0);
|
||||||
|
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||||
|
|
||||||
// Filter state - default to review status
|
// Filter state - default to review status
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
@@ -94,21 +98,25 @@ export default function Review() {
|
|||||||
const loadTotalMetrics = useCallback(async () => {
|
const loadTotalMetrics = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch counts in parallel for performance
|
// Fetch counts in parallel for performance
|
||||||
const [imagesRes, draftsRes, approvedRes, tasksRes] = await Promise.all([
|
const [allRes, draftRes, reviewRes, approvedRes, publishedRes, imagesRes] = await Promise.all([
|
||||||
fetchImages({ page_size: 1 }),
|
fetchContent({ page_size: 1, site_id: activeSite?.id }),
|
||||||
fetchContent({ page_size: 1, status: 'draft' }),
|
fetchContent({ page_size: 1, status: 'draft', site_id: activeSite?.id }),
|
||||||
fetchContent({ page_size: 1, status: 'approved' }),
|
fetchContent({ page_size: 1, status: 'review', site_id: activeSite?.id }),
|
||||||
fetchAPI<{ count: number }>('/writer/tasks/?page_size=1'),
|
fetchContent({ page_size: 1, status: 'approved', site_id: activeSite?.id }),
|
||||||
|
fetchContent({ page_size: 1, status: 'published', site_id: activeSite?.id }),
|
||||||
|
fetchImages({ page_size: 1, site_id: activeSite?.id }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setTotalImagesCount(imagesRes.count || 0);
|
setTotalContent(allRes.count || 0);
|
||||||
setTotalDrafts(draftsRes.count || 0);
|
setTotalDraft(draftRes.count || 0);
|
||||||
|
setTotalReview(reviewRes.count || 0);
|
||||||
setTotalApproved(approvedRes.count || 0);
|
setTotalApproved(approvedRes.count || 0);
|
||||||
setTotalTasks(tasksRes.count || 0);
|
setTotalPublished(publishedRes.count || 0);
|
||||||
|
setTotalImagesCount(imagesRes.count || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading metrics:', error);
|
console.error('Error loading metrics:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [activeSite]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTotalMetrics();
|
loadTotalMetrics();
|
||||||
@@ -161,12 +169,37 @@ export default function Review() {
|
|||||||
|
|
||||||
// Header metrics (calculated from loaded data)
|
// Header metrics (calculated from loaded data)
|
||||||
const headerMetrics = useMemo(() =>
|
const headerMetrics = useMemo(() =>
|
||||||
pageConfig.headerMetrics.map(metric => ({
|
pageConfig.headerMetrics.map(metric => {
|
||||||
|
let value: number;
|
||||||
|
switch (metric.label) {
|
||||||
|
case 'Content':
|
||||||
|
value = totalContent || 0;
|
||||||
|
break;
|
||||||
|
case 'Draft':
|
||||||
|
value = totalDraft;
|
||||||
|
break;
|
||||||
|
case 'In Review':
|
||||||
|
value = totalReview;
|
||||||
|
break;
|
||||||
|
case 'Approved':
|
||||||
|
value = totalApproved;
|
||||||
|
break;
|
||||||
|
case 'Published':
|
||||||
|
value = totalPublished;
|
||||||
|
break;
|
||||||
|
case 'Total Images':
|
||||||
|
value = totalImagesCount;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = metric.calculate({ content, totalCount });
|
||||||
|
}
|
||||||
|
return {
|
||||||
...metric,
|
...metric,
|
||||||
value: metric.calculate({ content, totalCount }),
|
value,
|
||||||
tooltip: (metric as any).tooltip,
|
tooltip: (metric as any).tooltip,
|
||||||
})),
|
};
|
||||||
[pageConfig.headerMetrics, content, totalCount]
|
}),
|
||||||
|
[pageConfig.headerMetrics, content, totalCount, totalContent, totalDraft, totalReview, totalApproved, totalPublished, totalImagesCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Export handler
|
// Export handler
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import TablePageTemplate from '../../templates/TablePageTemplate';
|
|||||||
import {
|
import {
|
||||||
fetchTasks,
|
fetchTasks,
|
||||||
fetchImages,
|
fetchImages,
|
||||||
|
fetchContent,
|
||||||
createTask,
|
createTask,
|
||||||
updateTask,
|
updateTask,
|
||||||
deleteTask,
|
deleteTask,
|
||||||
@@ -47,11 +48,17 @@ export default function Tasks() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Total counts for footer widget and header metrics (not page-filtered)
|
// Total counts for footer widget and header metrics (not page-filtered)
|
||||||
|
const [totalContent, setTotalContent] = useState(0);
|
||||||
|
const [totalDraft, setTotalDraft] = useState(0);
|
||||||
|
const [totalReview, setTotalReview] = useState(0);
|
||||||
|
const [totalApproved, setTotalApproved] = useState(0);
|
||||||
|
const [totalPublished, setTotalPublished] = useState(0);
|
||||||
|
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||||
|
|
||||||
|
// Footer widget specific counts (task-based)
|
||||||
const [totalQueued, setTotalQueued] = useState(0);
|
const [totalQueued, setTotalQueued] = useState(0);
|
||||||
const [totalProcessing, setTotalProcessing] = useState(0);
|
const [totalProcessing, setTotalProcessing] = useState(0);
|
||||||
const [totalCompleted, setTotalCompleted] = useState(0);
|
const [totalCompleted, setTotalCompleted] = useState(0);
|
||||||
const [totalFailed, setTotalFailed] = useState(0);
|
|
||||||
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
|
||||||
|
|
||||||
// Filter state
|
// Filter state
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
@@ -111,45 +118,45 @@ export default function Tasks() {
|
|||||||
const loadTotalMetrics = useCallback(async () => {
|
const loadTotalMetrics = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Batch all API calls in parallel for better performance
|
// Batch all API calls in parallel for better performance
|
||||||
const [allRes, queuedRes, processingRes, completedRes, failedRes, imagesRes] = await Promise.all([
|
const [allRes, draftRes, reviewRes, approvedRes, publishedRes, imagesRes] = await Promise.all([
|
||||||
// Get all tasks (site-wide)
|
// Get all content (site-wide)
|
||||||
fetchTasks({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
}),
|
}),
|
||||||
// Get tasks with status='queued'
|
// Get content with status='draft'
|
||||||
fetchTasks({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status: 'queued',
|
status: 'draft',
|
||||||
}),
|
}),
|
||||||
// Get tasks with status='in_progress'
|
// Get content with status='review'
|
||||||
fetchTasks({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status: 'in_progress',
|
status: 'review',
|
||||||
}),
|
}),
|
||||||
// Get tasks with status='completed'
|
// Get content with status='approved'
|
||||||
fetchTasks({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status: 'completed',
|
status: 'approved',
|
||||||
}),
|
}),
|
||||||
// Get tasks with status='failed'
|
// Get content with status='published'
|
||||||
fetchTasks({
|
fetchContent({
|
||||||
page_size: 1,
|
page_size: 1,
|
||||||
site_id: activeSite?.id,
|
site_id: activeSite?.id,
|
||||||
status: 'failed',
|
status: 'published',
|
||||||
}),
|
}),
|
||||||
// Get actual total images count
|
// Get actual total images count
|
||||||
fetchImages({ page_size: 1 }),
|
fetchImages({ page_size: 1, site_id: activeSite?.id }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setTotalCount(allRes.count || 0);
|
setTotalContent(allRes.count || 0);
|
||||||
setTotalQueued(queuedRes.count || 0);
|
setTotalDraft(draftRes.count || 0);
|
||||||
setTotalProcessing(processingRes.count || 0);
|
setTotalReview(reviewRes.count || 0);
|
||||||
setTotalCompleted(completedRes.count || 0);
|
setTotalApproved(approvedRes.count || 0);
|
||||||
setTotalFailed(failedRes.count || 0);
|
setTotalPublished(publishedRes.count || 0);
|
||||||
setTotalImagesCount(imagesRes.count || 0);
|
setTotalImagesCount(imagesRes.count || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading total metrics:', error);
|
console.error('Error loading total metrics:', error);
|
||||||
@@ -384,24 +391,23 @@ export default function Tasks() {
|
|||||||
let value: number;
|
let value: number;
|
||||||
|
|
||||||
switch (metric.label) {
|
switch (metric.label) {
|
||||||
case 'Tasks':
|
case 'Content':
|
||||||
value = totalCount || 0;
|
value = totalContent || 0;
|
||||||
break;
|
break;
|
||||||
case 'In Queue':
|
case 'Draft':
|
||||||
// Use totalQueued from loadTotalMetrics()
|
value = totalDraft;
|
||||||
value = totalQueued;
|
|
||||||
break;
|
break;
|
||||||
case 'Processing':
|
case 'In Review':
|
||||||
// Use totalProcessing from loadTotalMetrics()
|
value = totalReview;
|
||||||
value = totalProcessing;
|
|
||||||
break;
|
break;
|
||||||
case 'Completed':
|
case 'Approved':
|
||||||
// Use totalCompleted from loadTotalMetrics()
|
value = totalApproved;
|
||||||
value = totalCompleted;
|
|
||||||
break;
|
break;
|
||||||
case 'Failed':
|
case 'Published':
|
||||||
// Use totalFailed from loadTotalMetrics()
|
value = totalPublished;
|
||||||
value = totalFailed;
|
break;
|
||||||
|
case 'Total Images':
|
||||||
|
value = totalImagesCount;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
value = metric.calculate({ tasks, totalCount });
|
value = metric.calculate({ tasks, totalCount });
|
||||||
@@ -414,7 +420,7 @@ export default function Tasks() {
|
|||||||
tooltip: (metric as any).tooltip,
|
tooltip: (metric as any).tooltip,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [pageConfig?.headerMetrics, tasks, totalCount, totalQueued, totalProcessing, totalCompleted, totalFailed]);
|
}, [pageConfig?.headerMetrics, tasks, totalContent, totalDraft, totalReview, totalApproved, totalPublished, totalImagesCount]);
|
||||||
|
|
||||||
const resetForm = useCallback(() => {
|
const resetForm = useCallback(() => {
|
||||||
setFormData({
|
setFormData({
|
||||||
|
|||||||
@@ -1425,6 +1425,7 @@ export interface ImageListResponse {
|
|||||||
export interface ImageFilters {
|
export interface ImageFilters {
|
||||||
content_id?: number;
|
content_id?: number;
|
||||||
task_id?: number;
|
task_id?: number;
|
||||||
|
site_id?: number;
|
||||||
image_type?: string;
|
image_type?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
ordering?: string;
|
ordering?: string;
|
||||||
@@ -1502,6 +1503,7 @@ export async function fetchImages(filters: ImageFilters = {}): Promise<ImageList
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (filters.content_id) params.append('content_id', filters.content_id.toString());
|
if (filters.content_id) params.append('content_id', filters.content_id.toString());
|
||||||
if (filters.task_id) params.append('task_id', filters.task_id.toString());
|
if (filters.task_id) params.append('task_id', filters.task_id.toString());
|
||||||
|
if (filters.site_id) params.append('site_id', filters.site_id.toString());
|
||||||
if (filters.image_type) params.append('image_type', filters.image_type);
|
if (filters.image_type) params.append('image_type', filters.image_type);
|
||||||
if (filters.status) params.append('status', filters.status);
|
if (filters.status) params.append('status', filters.status);
|
||||||
if (filters.ordering) params.append('ordering', filters.ordering);
|
if (filters.ordering) params.append('ordering', filters.ordering);
|
||||||
|
|||||||
Reference in New Issue
Block a user