This commit is contained in:
Jake Kasper
2025-09-04 09:01:54 -05:00
parent 6d20918bb7
commit c9729130fe
2 changed files with 45 additions and 22 deletions

View File

@@ -1174,36 +1174,59 @@ router.get('/stats', async (req, res, next) => {
try { try {
const { year = new Date().getFullYear() } = req.query; const { year = new Date().getFullYear() } = req.query;
const statsQuery = ` // Compute plan stats and log stats without FULL JOIN to satisfy Postgres
const combinedStatsQuery = `
WITH log_stats AS (
SELECT
COUNT(*)::int AS total_applications,
COALESCE(SUM(area_covered), 0)::float AS total_area_treated,
COALESCE(AVG(average_speed), 0)::float AS avg_application_speed
FROM application_logs
WHERE user_id = $1
AND EXTRACT(YEAR FROM application_date) = $2
),
plan_stats AS (
SELECT
COUNT(*)::int AS total_plans,
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed_plans,
COUNT(*) FILTER (WHERE status = 'planned')::int AS planned_applications
FROM application_plans
WHERE user_id = $1
AND EXTRACT(YEAR FROM planned_date) = $2
)
SELECT SELECT
COUNT(DISTINCT al.id) as total_applications, ls.total_applications,
COUNT(DISTINCT ap.id) as total_plans, ps.total_plans,
COUNT(DISTINCT CASE WHEN ap.status = 'completed' THEN ap.id END) as completed_plans, ps.completed_plans,
COUNT(DISTINCT CASE WHEN ap.status = 'planned' THEN ap.id END) as planned_applications, ps.planned_applications,
COALESCE(SUM(al.area_covered), 0) as total_area_treated, ls.total_area_treated,
COALESCE(AVG(al.average_speed), 0) as avg_application_speed ls.avg_application_speed
FROM application_logs al FROM log_stats ls
FULL OUTER JOIN application_plans ap ON al.plan_id = ap.id OR ap.user_id = $1 CROSS JOIN plan_stats ps;
WHERE EXTRACT(YEAR FROM COALESCE(al.application_date, ap.planned_date)) = $2
AND (al.user_id = $1 OR ap.user_id = $1)
`; `;
const statsResult = await pool.query(statsQuery, [req.user.id, year]); const statsResult = await pool.query(combinedStatsQuery, [req.user.id, year]);
const stats = statsResult.rows[0]; const stats = statsResult.rows[0] || {
total_applications: 0,
total_plans: 0,
completed_plans: 0,
planned_applications: 0,
total_area_treated: 0,
avg_application_speed: 0,
};
// Get monthly breakdown // Monthly breakdown from logs
const monthlyQuery = ` const monthlyQuery = `
SELECT SELECT
EXTRACT(MONTH FROM al.application_date) as month, EXTRACT(MONTH FROM al.application_date) AS month,
COUNT(*) as applications, COUNT(*) AS applications,
COALESCE(SUM(al.area_covered), 0) as area_covered COALESCE(SUM(al.area_covered), 0) AS area_covered
FROM application_logs al FROM application_logs al
WHERE al.user_id = $1 WHERE al.user_id = $1
AND EXTRACT(YEAR FROM al.application_date) = $2 AND EXTRACT(YEAR FROM al.application_date) = $2
GROUP BY EXTRACT(MONTH FROM al.application_date) GROUP BY EXTRACT(MONTH FROM al.application_date)
ORDER BY month ORDER BY month
`; `;
const monthlyResult = await pool.query(monthlyQuery, [req.user.id, year]); const monthlyResult = await pool.query(monthlyQuery, [req.user.id, year]);
res.json({ res.json({
@@ -1216,8 +1239,8 @@ router.get('/stats', async (req, res, next) => {
plannedApplications: parseInt(stats.planned_applications) || 0, plannedApplications: parseInt(stats.planned_applications) || 0,
totalAreaTreated: parseFloat(stats.total_area_treated) || 0, totalAreaTreated: parseFloat(stats.total_area_treated) || 0,
avgApplicationSpeed: parseFloat(stats.avg_application_speed) || 0, avgApplicationSpeed: parseFloat(stats.avg_application_speed) || 0,
completionRate: stats.total_plans > 0 ? completionRate: (parseInt(stats.total_plans) || 0) > 0 ?
Math.round((stats.completed_plans / stats.total_plans) * 100) : 0 Math.round((parseInt(stats.completed_plans) / parseInt(stats.total_plans)) * 100) : 0
}, },
monthlyBreakdown: monthlyResult.rows.map(row => ({ monthlyBreakdown: monthlyResult.rows.map(row => ({
month: parseInt(row.month), month: parseInt(row.month),

View File

@@ -140,8 +140,8 @@ const Dashboard = () => {
const appLogs = (appLogsRes.data?.data?.logs || []).map(l => ({ const appLogs = (appLogsRes.data?.data?.logs || []).map(l => ({
id: `app-${l.id}`, id: `app-${l.id}`,
type: 'application', type: 'application',
title: `${l.productNames?.length ? 'Applied ' + l.productNames.join(', ') : 'Application'} on ${l.section_names || 'section'}`, title: `Application on ${l.sectionName || 'section'}`,
property: l.property_name, property: l.propertyName,
date: l.applicationDate || l.createdAt, date: l.applicationDate || l.createdAt,
status: 'completed' status: 'completed'
})); }));