asdfasf
This commit is contained in:
@@ -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
|
SELECT
|
||||||
COUNT(DISTINCT al.id) as total_applications,
|
COUNT(*)::int AS total_applications,
|
||||||
COUNT(DISTINCT ap.id) as total_plans,
|
COALESCE(SUM(area_covered), 0)::float AS total_area_treated,
|
||||||
COUNT(DISTINCT CASE WHEN ap.status = 'completed' THEN ap.id END) as completed_plans,
|
COALESCE(AVG(average_speed), 0)::float AS avg_application_speed
|
||||||
COUNT(DISTINCT CASE WHEN ap.status = 'planned' THEN ap.id END) as planned_applications,
|
FROM application_logs
|
||||||
COALESCE(SUM(al.area_covered), 0) as total_area_treated,
|
WHERE user_id = $1
|
||||||
COALESCE(AVG(al.average_speed), 0) as avg_application_speed
|
AND EXTRACT(YEAR FROM application_date) = $2
|
||||||
FROM application_logs al
|
),
|
||||||
FULL OUTER JOIN application_plans ap ON al.plan_id = ap.id OR ap.user_id = $1
|
plan_stats AS (
|
||||||
WHERE EXTRACT(YEAR FROM COALESCE(al.application_date, ap.planned_date)) = $2
|
SELECT
|
||||||
AND (al.user_id = $1 OR ap.user_id = $1)
|
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
|
||||||
|
ls.total_applications,
|
||||||
|
ps.total_plans,
|
||||||
|
ps.completed_plans,
|
||||||
|
ps.planned_applications,
|
||||||
|
ls.total_area_treated,
|
||||||
|
ls.avg_application_speed
|
||||||
|
FROM log_stats ls
|
||||||
|
CROSS JOIN plan_stats ps;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
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),
|
||||||
|
|||||||
@@ -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'
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user