Files
MAD-Platform/app/api/dashboard/stats/route.ts

281 lines
8.7 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getCurrentUser } from '@/lib/auth';
// Types de période: day | 7days | 30days | month | quarter | year
// Ou plage personnalisée via from & to (format ISO)
function getDateRange(period: string | null, from: string | null, to: string | null): {
start: Date;
end: Date;
prevStart: Date;
prevEnd: Date;
periodLabel: string;
} {
const now = new Date();
const endOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
if (from && to) {
const start = new Date(from);
const end = endOfDay(new Date(to));
const diff = end.getTime() - start.getTime();
const prevEnd = new Date(start.getTime() - 1);
const prevStart = new Date(prevEnd.getTime() - diff);
return {
start: startOfDay(start),
end,
prevStart: startOfDay(prevStart),
prevEnd: endOfDay(prevEnd),
periodLabel: `${start.toLocaleDateString('fr-FR')} - ${new Date(to).toLocaleDateString('fr-FR')}`,
};
}
switch (period) {
case 'day': {
const start = startOfDay(now);
const end = endOfDay(now);
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
return {
start,
end,
prevStart: startOfDay(yesterday),
prevEnd: endOfDay(yesterday),
periodLabel: "Aujourd'hui",
};
}
case 'yesterday': {
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
const start = startOfDay(yesterday);
const end = endOfDay(yesterday);
const dayBefore = new Date(yesterday);
dayBefore.setDate(dayBefore.getDate() - 1);
return {
start,
end,
prevStart: startOfDay(dayBefore),
prevEnd: endOfDay(dayBefore),
periodLabel: 'Hier',
};
}
case 'week': {
// Cette semaine : lundi à aujourd'hui (ISO week, lundi = 1)
const dayOfWeek = now.getDay();
const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
const startOfWeek = new Date(now);
startOfWeek.setDate(startOfWeek.getDate() + mondayOffset);
const start = startOfDay(startOfWeek);
const end = endOfDay(now);
const prevWeekStart = new Date(start);
prevWeekStart.setDate(prevWeekStart.getDate() - 7);
const prevWeekEnd = new Date(start);
prevWeekEnd.setDate(prevWeekEnd.getDate() - 1);
return {
start,
end,
prevStart: startOfDay(prevWeekStart),
prevEnd: endOfDay(prevWeekEnd),
periodLabel: 'Cette semaine',
};
}
case '7days': {
const start = new Date(now);
start.setDate(start.getDate() - 6);
const end = endOfDay(now);
const prevEnd = new Date(start.getTime() - 1);
const prevStart = new Date(prevEnd);
prevStart.setDate(prevStart.getDate() - 6);
return {
start: startOfDay(start),
end,
prevStart: startOfDay(prevStart),
prevEnd: endOfDay(prevEnd),
periodLabel: '7 derniers jours',
};
}
case '30days': {
const start = new Date(now);
start.setDate(start.getDate() - 29);
const end = endOfDay(now);
const prevEnd = new Date(start.getTime() - 1);
const prevStart = new Date(prevEnd);
prevStart.setDate(prevStart.getDate() - 29);
return {
start: startOfDay(start),
end,
prevStart: startOfDay(prevStart),
prevEnd: endOfDay(prevEnd),
periodLabel: '30 derniers jours',
};
}
case 'quarter': {
const q = Math.floor(now.getMonth() / 3) + 1;
const startOfQuarter = new Date(now.getFullYear(), (q - 1) * 3, 1);
const endOfQuarter = new Date(now.getFullYear(), q * 3, 0, 23, 59, 59, 999);
const prevQuarter = q === 1 ? 4 : q - 1;
const prevYear = q === 1 ? now.getFullYear() - 1 : now.getFullYear();
const prevStart = new Date(prevYear, (prevQuarter - 1) * 3, 1);
const prevEnd = new Date(prevYear, prevQuarter * 3, 0, 23, 59, 59, 999);
return {
start: startOfQuarter,
end: endOfQuarter,
prevStart: prevStart,
prevEnd: prevEnd,
periodLabel: `T${q} ${now.getFullYear()}`,
};
}
case 'year': {
const startOfYear = new Date(now.getFullYear(), 0, 1);
const endOfYear = new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999);
const prevStart = new Date(now.getFullYear() - 1, 0, 1);
const prevEnd = new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 999);
return {
start: startOfYear,
end: endOfYear,
prevStart: prevStart,
prevEnd: prevEnd,
periodLabel: `Année ${now.getFullYear()}`,
};
}
case 'month':
default: {
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
return {
start: startOfMonth,
end: endOfMonth,
prevStart: startOfLastMonth,
prevEnd: endOfLastMonth,
periodLabel: 'Mois en cours',
};
}
}
}
// GET - Récupérer les statistiques du dashboard
export async function GET(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
}
const { searchParams } = new URL(request.url);
const period = searchParams.get('period') || 'month';
const from = searchParams.get('from');
const to = searchParams.get('to');
const { start, end, prevStart, prevEnd, periodLabel } = getDateRange(period, from, to);
// 1. Participations sur la période (trajets validés/terminés)
const participationsMoisData = await prisma.participationFinanciere.findMany({
where: {
trajet: {
date: {
gte: start,
lte: end,
},
},
},
include: { trajet: { select: { date: true } } },
});
const participationsCeMois = participationsMoisData;
const montantMoyenParTrajet = 6.80;
const participationsMois = participationsCeMois.reduce(
(sum, p) => sum + (p.montant ?? montantMoyenParTrajet),
0
);
const nombreFactures = participationsCeMois.length;
// 2. Trajets sur la période
const trajetsPeriode = await prisma.trajet.count({
where: {
archived: false,
date: {
gte: start,
lte: end,
},
},
});
// Trajets période précédente pour comparaison
const trajetsPeriodePrecedente = await prisma.trajet.count({
where: {
archived: false,
date: {
gte: prevStart,
lte: prevEnd,
},
},
});
const differenceTrajets = trajetsPeriode - trajetsPeriodePrecedente;
// 3. Trajets réalisés sur la période (terminés)
const trajetsRealisesPeriode = await prisma.trajet.count({
where: {
archived: false,
statut: 'Terminé',
date: {
gte: start,
lte: end,
},
},
});
// Trajets réalisés période précédente pour comparaison
const trajetsRealisesPeriodePrecedente = await prisma.trajet.count({
where: {
archived: false,
statut: 'Terminé',
date: {
gte: prevStart,
lte: prevEnd,
},
},
});
const pourcentageEvolution = trajetsRealisesPeriodePrecedente > 0
? Math.round(((trajetsRealisesPeriode - trajetsRealisesPeriodePrecedente) / trajetsRealisesPeriodePrecedente) * 100)
: trajetsRealisesPeriode > 0 ? 100 : 0;
// 4. Chauffeurs actifs (disponibles)
const totalChauffeurs = await prisma.chauffeur.count();
const chauffeursActifs = await prisma.chauffeur.count({
where: {
status: 'Disponible',
},
});
return NextResponse.json({
periodLabel,
participationsMois: {
montant: participationsMois,
nombreFactures: nombreFactures,
},
trajetsAujourdhui: {
nombre: trajetsPeriode,
difference: differenceTrajets,
},
trajetsRealisesMois: {
nombre: trajetsRealisesPeriode,
pourcentageEvolution: pourcentageEvolution,
},
chauffeursActifs: {
nombre: chauffeursActifs,
total: totalChauffeurs,
},
});
} catch (error) {
console.error('Erreur lors de la récupération des statistiques:', error);
return NextResponse.json(
{ error: 'Erreur serveur' },
{ status: 500 }
);
}
}