metricsa dn backedn fixes

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-29 04:33:22 +00:00
parent 53fdebf733
commit 0ffd21b9bf
17 changed files with 929 additions and 266 deletions

View File

@@ -8,6 +8,7 @@ import { Link, useNavigate } from 'react-router-dom';
import TablePageTemplate from '../../templates/TablePageTemplate';
import {
fetchContent,
fetchImages,
Content as ContentType,
ContentFilters,
generateImagePrompts,
@@ -34,7 +35,13 @@ export default function Content() {
// Data state
const [content, setContent] = useState<ContentType[]>([]);
const [loading, setLoading] = useState(true);
// Total counts for footer widget and header metrics (not page-filtered)
const [totalDraft, setTotalDraft] = useState(0);
const [totalReview, setTotalReview] = useState(0);
const [totalPublished, setTotalPublished] = useState(0);
const [totalImagesCount, setTotalImagesCount] = useState(0);
// Filter state
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('draft');
@@ -55,6 +62,46 @@ export default function Content() {
const progressModal = useProgressModal();
const hasReloadedRef = useRef(false);
// Load total metrics for footer widget and header metrics (not affected by pagination)
const loadTotalMetrics = useCallback(async () => {
try {
// Get content with status='draft'
const draftRes = await fetchContent({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'draft',
});
setTotalDraft(draftRes.count || 0);
// Get content with status='review'
const reviewRes = await fetchContent({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'review',
});
setTotalReview(reviewRes.count || 0);
// Get content with status='published'
const publishedRes = await fetchContent({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'published',
});
setTotalPublished(publishedRes.count || 0);
// Get actual total images count
const imagesRes = await fetchImages({ page_size: 1 });
setTotalImagesCount(imagesRes.count || 0);
} catch (error) {
console.error('Error loading total metrics:', error);
}
}, [activeSector]);
// Load total metrics when sector changes
useEffect(() => {
loadTotalMetrics();
}, [loadTotalMetrics]);
// Load content - wrapped in useCallback
const loadContent = useCallback(async () => {
setLoading(true);
@@ -115,18 +162,17 @@ export default function Content() {
setCurrentPage(1);
}, [pageSize]);
// Debounced search
// Debounced search - reset to page 1 when search term changes
// Only depend on searchTerm to avoid pagination reset on page navigation
useEffect(() => {
const timer = setTimeout(() => {
if (currentPage === 1) {
loadContent();
} else {
setCurrentPage(1);
}
// Always reset to page 1 when search changes
// The main useEffect will handle reloading when currentPage changes
setCurrentPage(1);
}, 500);
return () => clearTimeout(timer);
}, [searchTerm, currentPage, loadContent]);
}, [searchTerm]);
// Handle sorting
const handleSort = (field: string, direction: 'asc' | 'desc') => {
@@ -160,16 +206,43 @@ export default function Content() {
handleRowClick,
]);
// Calculate header metrics
// Calculate header metrics - use totals from API calls (not page data)
// This ensures metrics show correct totals across all pages, not just current page
const headerMetrics = useMemo(() => {
if (!pageConfig?.headerMetrics) return [];
return pageConfig.headerMetrics.map((metric) => ({
label: metric.label,
value: metric.calculate({ content, totalCount }),
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip,
}));
}, [pageConfig?.headerMetrics, content, totalCount]);
// Override the calculate function to use pre-loaded totals instead of filtering page data
return pageConfig.headerMetrics.map((metric) => {
let value: number;
switch (metric.label) {
case 'Content':
value = totalCount || 0;
break;
case 'Draft':
// Use totalDraft from loadTotalMetrics()
value = totalDraft;
break;
case 'In Review':
// Use totalReview from loadTotalMetrics()
value = totalReview;
break;
case 'Published':
// Use totalPublished from loadTotalMetrics()
value = totalPublished;
break;
default:
value = metric.calculate({ content, totalCount });
}
return {
label: metric.label,
value,
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip,
};
});
}, [pageConfig?.headerMetrics, content, totalCount, totalDraft, totalReview, totalPublished]);
const handleRowAction = useCallback(async (action: string, row: ContentType) => {
if (action === 'view_on_wordpress') {
@@ -347,7 +420,7 @@ export default function Content() {
],
writerItems: [
{ label: 'Content Generated', value: totalCount, color: 'blue' },
{ label: 'Images Created', value: content.filter(c => c.has_generated_images).length, color: 'purple' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: content.filter(c => c.status === 'published').length, color: 'green' },
],
analyticsHref: '/account/usage',