329 lines
7.8 KiB
JavaScript
329 lines
7.8 KiB
JavaScript
const bcrypt = require("bcrypt");
|
|
const prisma = require("../config/prisma");
|
|
const { getGameWindow, expireOverdueClaims } = require("../utils/gameWindow");
|
|
|
|
const AGE_BRACKETS = ["-18", "18-24", "25-34", "35-44", "45-54", "55-64", "65+"];
|
|
|
|
function getAgeBracket(birthdate) {
|
|
if (!birthdate) return "unknown";
|
|
|
|
const now = new Date();
|
|
let age = now.getFullYear() - birthdate.getFullYear();
|
|
const hasHadBirthdayThisYear =
|
|
now.getMonth() > birthdate.getMonth() ||
|
|
(now.getMonth() === birthdate.getMonth() && now.getDate() >= birthdate.getDate());
|
|
if (!hasHadBirthdayThisYear) age -= 1;
|
|
|
|
if (age < 18) return "-18";
|
|
if (age <= 24) return "18-24";
|
|
if (age <= 34) return "25-34";
|
|
if (age <= 44) return "35-44";
|
|
if (age <= 54) return "45-54";
|
|
if (age <= 64) return "55-64";
|
|
return "65+";
|
|
}
|
|
|
|
function computeAgeStats(users) {
|
|
const counts = Object.fromEntries([...AGE_BRACKETS, "unknown"].map((b) => [b, 0]));
|
|
for (const user of users) {
|
|
counts[getAgeBracket(user.birthdate)] += 1;
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
const getStats = async (req, res) => {
|
|
try {
|
|
await expireOverdueClaims();
|
|
const gameWindow = await getGameWindow();
|
|
|
|
const [
|
|
ticketsTotal,
|
|
ticketsUsed,
|
|
participations,
|
|
claimsPending,
|
|
claimsDelivered,
|
|
claimsCancelled,
|
|
users,
|
|
men,
|
|
women,
|
|
clientsWithBirthdate,
|
|
] = await Promise.all([
|
|
prisma.ticket.count(),
|
|
prisma.ticket.count({ where: { isUsed: true } }),
|
|
prisma.participation.count(),
|
|
prisma.claim.count({ where: { status: "PENDING" } }),
|
|
prisma.claim.count({ where: { status: "DELIVERED" } }),
|
|
prisma.claim.count({ where: { status: "CANCELLED" } }),
|
|
prisma.user.count({ where: { role: "CLIENT" } }),
|
|
prisma.user.count({ where: { role: "CLIENT", gender: "Homme" } }),
|
|
prisma.user.count({ where: { role: "CLIENT", gender: "Femme" } }),
|
|
prisma.user.findMany({ where: { role: "CLIENT" }, select: { birthdate: true } }),
|
|
]);
|
|
|
|
const lotsStats = await prisma.lot.findMany({
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
percentage: true,
|
|
_count: {
|
|
select: {
|
|
tickets: true,
|
|
participations: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
return res.json({
|
|
ticketsTotal,
|
|
ticketsUsed,
|
|
ticketsRemaining: ticketsTotal - ticketsUsed,
|
|
participations,
|
|
claimsPending,
|
|
claimsDelivered,
|
|
claimsCancelled,
|
|
users,
|
|
gameWindow,
|
|
ageStats: computeAgeStats(clientsWithBirthdate),
|
|
genderStats: {
|
|
men,
|
|
women,
|
|
other: users - men - women,
|
|
},
|
|
lotsStats,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur stats admin:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors de la récupération des statistiques.",
|
|
});
|
|
}
|
|
};
|
|
|
|
const getUsers = async (req, res) => {
|
|
try {
|
|
const users = await prisma.user.findMany({
|
|
where: { role: "CLIENT" },
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
gender: true,
|
|
birthdate: true,
|
|
marketingOptIn: true,
|
|
createdAt: true,
|
|
_count: {
|
|
select: {
|
|
participations: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
|
|
return res.json({ users });
|
|
} catch (error) {
|
|
console.error("Erreur liste utilisateurs:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors de la récupération des utilisateurs.",
|
|
});
|
|
}
|
|
};
|
|
|
|
const getEmailingContacts = async (req, res) => {
|
|
try {
|
|
const contacts = await prisma.user.findMany({
|
|
where: {
|
|
role: "CLIENT",
|
|
marketingOptIn: true,
|
|
},
|
|
select: {
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
createdAt: true,
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
|
|
return res.json({
|
|
total: contacts.length,
|
|
contacts,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur emailing:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors de la récupération des contacts emailing.",
|
|
});
|
|
}
|
|
};
|
|
|
|
const createEmployee = async (req, res) => {
|
|
try {
|
|
const { firstname, lastname, email, password, birthdate, gender } = req.body;
|
|
|
|
const existingUser = await prisma.user.findUnique({
|
|
where: { email },
|
|
});
|
|
|
|
if (existingUser) {
|
|
return res.status(409).json({
|
|
message: "Un compte existe déjà avec cet email.",
|
|
});
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
const employee = await prisma.user.create({
|
|
data: {
|
|
firstname,
|
|
lastname,
|
|
email,
|
|
password: hashedPassword,
|
|
birthdate: birthdate ? new Date(birthdate) : null,
|
|
gender,
|
|
role: "EMPLOYEE",
|
|
marketingOptIn: false,
|
|
},
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
},
|
|
});
|
|
|
|
return res.status(201).json({
|
|
message: "Employé créé avec succès.",
|
|
employee,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur création employé:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors de la création de l'employé.",
|
|
});
|
|
}
|
|
};
|
|
|
|
const drawWinner = async (req, res) => {
|
|
try {
|
|
const existingWinner = await prisma.grandPrizeWinner.findFirst({
|
|
include: {
|
|
user: {
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (existingWinner) {
|
|
return res.status(409).json({
|
|
message: "Le tirage au sort a déjà été effectué.",
|
|
winner: existingWinner,
|
|
});
|
|
}
|
|
|
|
const participants = await prisma.user.findMany({
|
|
where: {
|
|
role: "CLIENT",
|
|
participations: {
|
|
some: {},
|
|
},
|
|
},
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
},
|
|
});
|
|
|
|
if (participants.length === 0) {
|
|
return res.status(400).json({
|
|
message: "Aucun participant disponible pour le tirage.",
|
|
});
|
|
}
|
|
|
|
const randomIndex = Math.floor(Math.random() * participants.length);
|
|
const winner = participants[randomIndex];
|
|
|
|
const savedWinner = await prisma.grandPrizeWinner.create({
|
|
data: {
|
|
userId: winner.id,
|
|
},
|
|
include: {
|
|
user: {
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
return res.status(201).json({
|
|
message: "Tirage au sort effectué avec succès.",
|
|
totalEligibleParticipants: participants.length,
|
|
winner: savedWinner,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur tirage au sort:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors du tirage au sort.",
|
|
});
|
|
}
|
|
};
|
|
|
|
const getGrandPrizeWinner = async (req, res) => {
|
|
try {
|
|
const winner = await prisma.grandPrizeWinner.findFirst({
|
|
include: {
|
|
user: {
|
|
select: {
|
|
id: true,
|
|
firstname: true,
|
|
lastname: true,
|
|
email: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!winner) {
|
|
return res.status(404).json({
|
|
message: "Aucun gagnant du gros lot n'a encore été tiré.",
|
|
});
|
|
}
|
|
|
|
return res.json({
|
|
winner,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur récupération gagnant:", error);
|
|
return res.status(500).json({
|
|
message: "Erreur serveur lors de la récupération du gagnant.",
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
getStats,
|
|
getUsers,
|
|
getEmailingContacts,
|
|
createEmployee,
|
|
drawWinner,
|
|
getGrandPrizeWinner,
|
|
}; |