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 } from 'react-router-dom';
import TablePageTemplate from '../../templates/TablePageTemplate';
import {
fetchTasks,
fetchImages,
createTask,
updateTask,
deleteTask,
@@ -42,6 +43,13 @@ export default function Tasks() {
const [tasks, setTasks] = useState<Task[]>([]);
const [clusters, setClusters] = useState<Cluster[]>([]);
const [loading, setLoading] = useState(true);
// Total counts for footer widget and header metrics (not page-filtered)
const [totalQueued, setTotalQueued] = useState(0);
const [totalProcessing, setTotalProcessing] = useState(0);
const [totalCompleted, setTotalCompleted] = useState(0);
const [totalFailed, setTotalFailed] = useState(0);
const [totalImagesCount, setTotalImagesCount] = useState(0);
// Filter state
const [searchTerm, setSearchTerm] = useState('');
@@ -97,6 +105,54 @@ export default function Tasks() {
loadClusters();
}, []);
// Load total metrics for footer widget and header metrics (not affected by pagination)
const loadTotalMetrics = useCallback(async () => {
try {
// Get tasks with status='queued'
const queuedRes = await fetchTasks({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'queued',
});
setTotalQueued(queuedRes.count || 0);
// Get tasks with status='in_progress'
const processingRes = await fetchTasks({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'in_progress',
});
setTotalProcessing(processingRes.count || 0);
// Get tasks with status='completed'
const completedRes = await fetchTasks({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'completed',
});
setTotalCompleted(completedRes.count || 0);
// Get tasks with status='failed'
const failedRes = await fetchTasks({
page_size: 1,
...(activeSector?.id && { sector_id: activeSector.id }),
status: 'failed',
});
setTotalFailed(failedRes.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 tasks - wrapped in useCallback
const loadTasks = useCallback(async () => {
setLoading(true);
@@ -167,18 +223,17 @@ export default function Tasks() {
}, [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) {
loadTasks();
} 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, loadTasks]);
}, [searchTerm]);
// Handle sorting
const handleSort = (field: string, direction: 'asc' | 'desc') => {
@@ -318,16 +373,47 @@ export default function Tasks() {
});
}, [clusters, activeSector, formData, searchTerm, statusFilter, clusterFilter, structureFilter, typeFilter, sourceFilter]);
// 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({ tasks, totalCount }),
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip,
}));
}, [pageConfig?.headerMetrics, tasks, 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 'Tasks':
value = totalCount || 0;
break;
case 'In Queue':
// Use totalQueued from loadTotalMetrics()
value = totalQueued;
break;
case 'Processing':
// Use totalProcessing from loadTotalMetrics()
value = totalProcessing;
break;
case 'Completed':
// Use totalCompleted from loadTotalMetrics()
value = totalCompleted;
break;
case 'Failed':
// Use totalFailed from loadTotalMetrics()
value = totalFailed;
break;
default:
value = metric.calculate({ tasks, totalCount });
}
return {
label: metric.label,
value,
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip,
};
});
}, [pageConfig?.headerMetrics, tasks, totalCount, totalQueued, totalProcessing, totalCompleted, totalFailed]);
const resetForm = useCallback(() => {
setFormData({
@@ -507,7 +593,7 @@ export default function Tasks() {
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: 0,
toValue: totalImagesCount,
toHref: '/writer/images',
progress: 0,
color: 'purple',
@@ -540,7 +626,7 @@ export default function Tasks() {
],
writerItems: [
{ label: 'Content Generated', value: tasks.filter(t => t.status === 'completed').length, color: 'blue' },
{ label: 'Images Created', value: 0, color: 'purple' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
analyticsHref: '/account/usage',