Added Participation Page
This commit is contained in:
@@ -23,25 +23,26 @@ export async function GET(request: NextRequest) {
|
||||
const startOfYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
||||
const endOfYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 23, 59, 59, 999);
|
||||
|
||||
// 1. Participations du mois (montant total des trajets validés/terminés + nombre de factures)
|
||||
// Pour l'instant, on considère qu'un trajet terminé = une facture
|
||||
// Montant estimé : 6.80€ par trajet (valeur moyenne basée sur l'image)
|
||||
const trajetsMois = await prisma.trajet.findMany({
|
||||
// 1. Participations du mois (trajets validés/terminés ce mois)
|
||||
const participationsMoisData = await prisma.participationFinanciere.findMany({
|
||||
where: {
|
||||
archived: false,
|
||||
statut: {
|
||||
in: ['Terminé', 'Validé'],
|
||||
},
|
||||
date: {
|
||||
gte: startOfMonth,
|
||||
lte: endOfMonth,
|
||||
trajet: {
|
||||
date: {
|
||||
gte: startOfMonth,
|
||||
lte: endOfMonth,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: { trajet: { select: { date: true } } },
|
||||
});
|
||||
|
||||
const montantMoyenParTrajet = 6.80; // Montant moyen par trajet en euros
|
||||
const participationsMois = trajetsMois.length * montantMoyenParTrajet;
|
||||
const nombreFactures = trajetsMois.length;
|
||||
const participationsCeMois = participationsMoisData;
|
||||
const montantMoyenParTrajet = 6.80;
|
||||
const participationsMois = participationsCeMois.reduce(
|
||||
(sum, p) => sum + (p.montant ?? montantMoyenParTrajet),
|
||||
0
|
||||
);
|
||||
const nombreFactures = participationsCeMois.length;
|
||||
|
||||
// 2. Trajets aujourd'hui
|
||||
const trajetsAujourdhui = await prisma.trajet.count({
|
||||
|
||||
57
app/api/participations/[id]/pdf/route.ts
Normal file
57
app/api/participations/[id]/pdf/route.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { generateParticipationPDF, getParticipationStoragePath } from '@/lib/participation-pdf';
|
||||
|
||||
// GET - Récupérer le PDF d'une participation (régénéré à chaque vue pour le design à jour)
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return new NextResponse('Non autorisé', { status: 401 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: true,
|
||||
trajet: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return new NextResponse('Participation non trouvée', { status: 404 });
|
||||
}
|
||||
|
||||
const filePath = getParticipationStoragePath(participation.id);
|
||||
const pdfBuffer = await generateParticipationPDF(
|
||||
{
|
||||
adherentNom: participation.adherent.nom,
|
||||
adherentPrenom: participation.adherent.prenom,
|
||||
adherentAdresse: participation.adherent.adresse,
|
||||
destinataireEmail: participation.destinataireEmail,
|
||||
destinataireNom: participation.destinataireNom,
|
||||
dateTrajet: participation.trajet.date,
|
||||
adresseDepart: participation.trajet.adresseDepart,
|
||||
adresseArrivee: participation.trajet.adresseArrivee,
|
||||
montant: participation.montant ?? undefined,
|
||||
complement: participation.complement ?? undefined,
|
||||
participationId: participation.id,
|
||||
},
|
||||
filePath
|
||||
);
|
||||
|
||||
return new NextResponse(pdfBuffer, {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Disposition': `inline; filename="participation-${params.id}.pdf"`,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération du PDF:', error);
|
||||
return new NextResponse('Erreur serveur', { status: 500 });
|
||||
}
|
||||
}
|
||||
203
app/api/participations/[id]/route.ts
Normal file
203
app/api/participations/[id]/route.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { generateParticipationPDF, getParticipationStoragePath } from '@/lib/participation-pdf';
|
||||
|
||||
// GET - Récupérer une participation
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: true,
|
||||
trajet: {
|
||||
include: {
|
||||
chauffeur: { select: { nom: true, prenom: true } },
|
||||
universPro: { select: { nom: true, prenom: true, nomEntreprise: true, email: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return NextResponse.json({ error: 'Participation non trouvée' }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(participation);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération de la participation:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT - Modifier une participation
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: true,
|
||||
trajet: { include: { adherent: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return NextResponse.json({ error: 'Participation non trouvée' }, { status: 404 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { destinataireEmail, destinataireNom, destinataireType, montant, complement } = body;
|
||||
|
||||
const updated = await prisma.participationFinanciere.update({
|
||||
where: { id: params.id },
|
||||
data: {
|
||||
...(destinataireEmail && { destinataireEmail }),
|
||||
...(destinataireNom && { destinataireNom }),
|
||||
...(destinataireType && { destinataireType }),
|
||||
...(montant !== undefined && { montant }),
|
||||
...(complement !== undefined && { complement }),
|
||||
},
|
||||
});
|
||||
|
||||
// Régénérer le PDF si les données ont changé
|
||||
const dataToUpdate =
|
||||
destinataireEmail || destinataireNom || montant !== undefined || complement !== undefined;
|
||||
if (dataToUpdate) {
|
||||
const filePath = getParticipationStoragePath(participation.id);
|
||||
await generateParticipationPDF(
|
||||
{
|
||||
adherentNom: participation.adherent.nom,
|
||||
adherentPrenom: participation.adherent.prenom,
|
||||
adherentAdresse: participation.adherent.adresse,
|
||||
destinataireEmail: updated.destinataireEmail,
|
||||
destinataireNom: updated.destinataireNom,
|
||||
dateTrajet: participation.trajet.date,
|
||||
adresseDepart: participation.trajet.adresseDepart,
|
||||
adresseArrivee: participation.trajet.adresseArrivee,
|
||||
montant: updated.montant ?? undefined,
|
||||
complement: updated.complement ?? undefined,
|
||||
participationId: participation.id,
|
||||
},
|
||||
filePath
|
||||
);
|
||||
await prisma.participationFinanciere.update({
|
||||
where: { id: params.id },
|
||||
data: { filePath },
|
||||
});
|
||||
}
|
||||
|
||||
const fullUpdated = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: { select: { id: true, nom: true, prenom: true, email: true } },
|
||||
trajet: { select: { id: true, date: true, adresseDepart: true, adresseArrivee: true, statut: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(fullUpdated);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la mise à jour de la participation:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH - Changer le statut d'une participation
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { statut } = body;
|
||||
|
||||
const validStatuts = ['en_attente', 'envoye', 'paye', 'archive'];
|
||||
if (!statut || !validStatuts.includes(statut)) {
|
||||
return NextResponse.json({ error: 'Statut invalide' }, { status: 400 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return NextResponse.json({ error: 'Participation non trouvée' }, { status: 404 });
|
||||
}
|
||||
|
||||
const updated = await prisma.participationFinanciere.update({
|
||||
where: { id: params.id },
|
||||
data: { statut },
|
||||
});
|
||||
|
||||
return NextResponse.json(updated);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du changement de statut:', error);
|
||||
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE - Supprimer une participation
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return NextResponse.json({ error: 'Participation non trouvée' }, { status: 404 });
|
||||
}
|
||||
|
||||
const fs = await import('fs');
|
||||
const path = await import('path');
|
||||
const filePath = getParticipationStoragePath(participation.id);
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
await prisma.participationFinanciere.delete({
|
||||
where: { id: params.id },
|
||||
});
|
||||
|
||||
return NextResponse.json({ message: 'Participation supprimée' });
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la suppression de la participation:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
110
app/api/participations/[id]/send/route.ts
Normal file
110
app/api/participations/[id]/send/route.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { getParticipationStoragePath } from '@/lib/participation-pdf';
|
||||
import fs from 'fs';
|
||||
|
||||
// POST - Envoyer la participation par email
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const participation = await prisma.participationFinanciere.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: { select: { prenom: true, nom: true } },
|
||||
trajet: { select: { date: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!participation) {
|
||||
return NextResponse.json({ error: 'Participation non trouvée' }, { status: 404 });
|
||||
}
|
||||
|
||||
const filePath = getParticipationStoragePath(participation.id);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Le document PDF n\'a pas été généré' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier la configuration email
|
||||
const smtpHost = process.env.SMTP_HOST;
|
||||
const smtpUser = process.env.SMTP_USER;
|
||||
const smtpPass = process.env.SMTP_PASS;
|
||||
|
||||
if (!smtpHost || !smtpUser || !smtpPass) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
"L'envoi par email n'est pas configuré. Configurez SMTP_HOST, SMTP_USER et SMTP_PASS dans les variables d'environnement.",
|
||||
},
|
||||
{ status: 503 }
|
||||
);
|
||||
}
|
||||
|
||||
const nodemailer = await import('nodemailer');
|
||||
const transporter = nodemailer.default.createTransport({
|
||||
host: smtpHost,
|
||||
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth: {
|
||||
user: smtpUser,
|
||||
pass: smtpPass,
|
||||
},
|
||||
});
|
||||
|
||||
const pdfBuffer = fs.readFileSync(filePath);
|
||||
const dateTrajet = participation.trajet?.date
|
||||
? new Date(participation.trajet.date).toLocaleDateString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})
|
||||
: '';
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM || smtpUser,
|
||||
to: participation.destinataireEmail,
|
||||
subject: `Participation financière - ${participation.adherent.prenom} ${participation.adherent.nom} - ${dateTrajet}`,
|
||||
text: `Bonjour,\n\nVeuillez trouver ci-joint la participation financière concernant le trajet du ${dateTrajet} pour ${participation.adherent.prenom} ${participation.adherent.nom}.\n\nCordialement`,
|
||||
html: `
|
||||
<p>Bonjour,</p>
|
||||
<p>Veuillez trouver ci-joint la participation financière concernant le trajet du <strong>${dateTrajet}</strong> pour <strong>${participation.adherent.prenom} ${participation.adherent.nom}</strong>.</p>
|
||||
<p>Cordialement</p>
|
||||
`,
|
||||
attachments: [
|
||||
{
|
||||
filename: `participation-financiere-${params.id}.pdf`,
|
||||
content: pdfBuffer,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Mettre à jour le statut en "envoyé"
|
||||
await prisma.participationFinanciere.update({
|
||||
where: { id: params.id },
|
||||
data: { statut: 'envoye' },
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Participation envoyée à ${participation.destinataireEmail}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de l\'envoi de l\'email:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
error instanceof Error ? error.message : 'Erreur lors de l\'envoi de l\'email',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
51
app/api/participations/route.ts
Normal file
51
app/api/participations/route.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
|
||||
// GET - Liste toutes les participations financières
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||
}
|
||||
|
||||
const participations = await prisma.participationFinanciere.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: {
|
||||
adherent: {
|
||||
select: {
|
||||
id: true,
|
||||
nom: true,
|
||||
prenom: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
trajet: {
|
||||
select: {
|
||||
id: true,
|
||||
date: true,
|
||||
adresseDepart: true,
|
||||
adresseArrivee: true,
|
||||
statut: true,
|
||||
chauffeur: {
|
||||
select: {
|
||||
id: true,
|
||||
nom: true,
|
||||
prenom: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(participations);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des participations:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { createNotificationForAllUsers } from '@/lib/notifications';
|
||||
import { createParticipationForTrajet } from '@/lib/participation-financiere';
|
||||
|
||||
// GET - Récupérer un trajet spécifique
|
||||
export async function GET(
|
||||
@@ -69,6 +70,13 @@ export async function PUT(
|
||||
const body = await request.json();
|
||||
const { date, adresseDepart, adresseArrivee, commentaire, instructions, statut, adherentId, chauffeurId } = body;
|
||||
|
||||
const previousTrajet = statut
|
||||
? await prisma.trajet.findUnique({
|
||||
where: { id: params.id },
|
||||
select: { statut: true },
|
||||
})
|
||||
: null;
|
||||
|
||||
const trajet = await prisma.trajet.update({
|
||||
where: { id: params.id },
|
||||
data: {
|
||||
@@ -80,6 +88,7 @@ export async function PUT(
|
||||
...(statut && { statut }),
|
||||
...(adherentId && { adherentId }),
|
||||
...(chauffeurId !== undefined && { chauffeurId }),
|
||||
...(body.universProId !== undefined && { universProId: body.universProId || null }),
|
||||
},
|
||||
include: {
|
||||
adherent: {
|
||||
@@ -105,52 +114,47 @@ export async function PUT(
|
||||
});
|
||||
|
||||
// Créer une notification si le statut a changé
|
||||
if (statut) {
|
||||
const oldTrajet = await prisma.trajet.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
adherent: {
|
||||
select: {
|
||||
nom: true,
|
||||
prenom: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
if (statut && previousTrajet && previousTrajet.statut !== statut) {
|
||||
const dateFormatted = new Date(trajet.date).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
||||
if (oldTrajet && oldTrajet.statut !== statut) {
|
||||
const dateFormatted = new Date(trajet.date).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
let notificationType: 'trajet_cancelled' | 'trajet_completed' | null = null;
|
||||
let notificationTitle = '';
|
||||
let notificationMessage = '';
|
||||
|
||||
let notificationType: 'trajet_cancelled' | 'trajet_completed' | null = null;
|
||||
let notificationTitle = '';
|
||||
let notificationMessage = '';
|
||||
if (statut === 'Annulé') {
|
||||
notificationType = 'trajet_cancelled';
|
||||
notificationTitle = 'Trajet annulé';
|
||||
notificationMessage = `Le trajet pour ${trajet.adherent.prenom} ${trajet.adherent.nom} du ${dateFormatted} a été annulé`;
|
||||
} else if (statut === 'Terminé') {
|
||||
notificationType = 'trajet_completed';
|
||||
notificationTitle = 'Trajet terminé';
|
||||
notificationMessage = `Le trajet pour ${trajet.adherent.prenom} ${trajet.adherent.nom} du ${dateFormatted} est terminé`;
|
||||
}
|
||||
|
||||
if (statut === 'Annulé') {
|
||||
notificationType = 'trajet_cancelled';
|
||||
notificationTitle = 'Trajet annulé';
|
||||
notificationMessage = `Le trajet pour ${trajet.adherent.prenom} ${trajet.adherent.nom} du ${dateFormatted} a été annulé`;
|
||||
} else if (statut === 'Terminé') {
|
||||
notificationType = 'trajet_completed';
|
||||
notificationTitle = 'Trajet terminé';
|
||||
notificationMessage = `Le trajet pour ${trajet.adherent.prenom} ${trajet.adherent.nom} du ${dateFormatted} est terminé`;
|
||||
}
|
||||
if (notificationType) {
|
||||
await createNotificationForAllUsers(
|
||||
{
|
||||
type: notificationType,
|
||||
title: notificationTitle,
|
||||
message: notificationMessage,
|
||||
link: '/dashboard/calendrier',
|
||||
},
|
||||
user.id
|
||||
);
|
||||
}
|
||||
|
||||
if (notificationType) {
|
||||
await createNotificationForAllUsers(
|
||||
{
|
||||
type: notificationType,
|
||||
title: notificationTitle,
|
||||
message: notificationMessage,
|
||||
link: '/dashboard/calendrier',
|
||||
},
|
||||
user.id
|
||||
);
|
||||
// Créer la participation financière quand le trajet est terminé ou validé
|
||||
if (statut === 'Terminé' || statut === 'Validé') {
|
||||
try {
|
||||
await createParticipationForTrajet(params.id);
|
||||
} catch (err) {
|
||||
console.error('Erreur création participation:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { createParticipationForTrajet } from '@/lib/participation-financiere';
|
||||
|
||||
// POST - Valider un trajet et déduire les heures du chauffeur
|
||||
export async function POST(
|
||||
@@ -23,11 +24,12 @@ export async function POST(
|
||||
);
|
||||
}
|
||||
|
||||
// Récupérer le trajet avec le chauffeur
|
||||
// Récupérer le trajet avec le chauffeur et univers pro
|
||||
const trajet = await prisma.trajet.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
chauffeur: true,
|
||||
universPro: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -97,6 +99,13 @@ export async function POST(
|
||||
}),
|
||||
]);
|
||||
|
||||
// Créer la participation financière (document)
|
||||
try {
|
||||
await createParticipationForTrajet(params.id);
|
||||
} catch (err) {
|
||||
console.error('Erreur création participation:', err);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
trajet: trajetUpdated,
|
||||
chauffeur: {
|
||||
|
||||
31
app/dashboard/factures/page.tsx
Normal file
31
app/dashboard/factures/page.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { hasPageAccess } from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import DashboardLayout from '@/components/DashboardLayout';
|
||||
import ParticipationFinanciereList from '@/components/ParticipationFinanciereList';
|
||||
|
||||
export default async function ParticipationFinancierePage() {
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
redirect('/login');
|
||||
}
|
||||
|
||||
const hasAccess = await hasPageAccess(user.id, '/dashboard/factures');
|
||||
if (!hasAccess) {
|
||||
redirect('/dashboard/parametres');
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardLayout user={user}>
|
||||
<div className="p-3 sm:p-4 md:p-6 lg:p-8">
|
||||
<h1 className="text-xl sm:text-2xl md:text-3xl font-semibold text-cblack mb-1 sm:mb-2">
|
||||
Participation financière
|
||||
</h1>
|
||||
<p className="text-xs md:text-sm text-cgray mb-4 sm:mb-6 md:mb-8">
|
||||
Documents de participation générés à la fin de chaque trajet
|
||||
</p>
|
||||
<ParticipationFinanciereList />
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
@@ -121,3 +121,17 @@ body {
|
||||
.animate-slideInRight {
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Sélection lisible dans les modales de formulaire */
|
||||
.participation-form input,
|
||||
.participation-form select,
|
||||
.participation-form textarea {
|
||||
color: #181818;
|
||||
color-scheme: light;
|
||||
}
|
||||
.participation-form input::selection,
|
||||
.participation-form select::selection,
|
||||
.participation-form textarea::selection {
|
||||
background-color: #17B6C4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user