feat: implement core platform features including business directory, admin dashboard, authentication, and API infrastructure
Some checks failed
Build and Push App / build (push) Failing after 16m2s
Some checks failed
Build and Push App / build (push) Failing after 16m2s
This commit is contained in:
115
app/api/businesses/[id]/rate/route.ts
Normal file
115
app/api/businesses/[id]/rate/route.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import prisma from '@/lib/prisma';
|
||||
|
||||
export async function POST(
|
||||
req: NextRequest,
|
||||
context: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const { id: paramId } = await context.params;
|
||||
const userId = req.headers.get('x-user-id');
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'Utilisateur non identifié' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { value, comment } = await req.json();
|
||||
|
||||
if (!value || value < 1 || value > 5) {
|
||||
return NextResponse.json({ error: 'Valeur de note invalide (1-5)' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Resolve actual Business UUID if paramId is a slug
|
||||
const business = await prisma.business.findFirst({
|
||||
where: {
|
||||
OR: [
|
||||
{ id: paramId },
|
||||
{ slug: paramId }
|
||||
]
|
||||
},
|
||||
select: { id: true }
|
||||
});
|
||||
|
||||
if (!business) {
|
||||
return NextResponse.json({ error: "Les données de démonstration ne peuvent pas recevoir d'avis. Veuillez créer votre propre entreprise pour tester." }, { status: 403 });
|
||||
}
|
||||
|
||||
const businessId = business.id;
|
||||
const ownerId = (business as any).ownerId;
|
||||
|
||||
// 2. Security: Don't allow owner to rate themselves
|
||||
if (userId === ownerId) {
|
||||
return NextResponse.json({ error: "Vous ne pouvez pas noter votre propre entreprise." }, { status: 403 });
|
||||
}
|
||||
|
||||
// 3. Fetch existing rating to check rules
|
||||
const existingRating = await prisma.rating.findUnique({
|
||||
where: {
|
||||
userId_businessId: {
|
||||
userId,
|
||||
businessId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (existingRating) {
|
||||
// Rule: Score can only be re-evaluated UPWARDS
|
||||
if (value < existingRating.value) {
|
||||
return NextResponse.json({
|
||||
error: `La note ne peut être réévaluée qu'à la hausse. Votre note actuelle est de ${existingRating.value} étoiles.`
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
// Rule: Comment is IMMUTABLE once set
|
||||
// We only update the value. If the comment was empty, we can set it once.
|
||||
await prisma.rating.update({
|
||||
where: { id: existingRating.id },
|
||||
data: {
|
||||
value,
|
||||
comment: existingRating.comment ? undefined : comment, // Only set if it was null
|
||||
status: 'PENDING' // Reset to pending for re-moderation
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 4. Create new rating
|
||||
await prisma.rating.create({
|
||||
data: {
|
||||
value,
|
||||
comment,
|
||||
userId,
|
||||
businessId,
|
||||
status: 'PENDING'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Aggregate all ratings for this business
|
||||
const stats = await prisma.rating.aggregate({
|
||||
where: { businessId },
|
||||
_avg: { value: true },
|
||||
_count: { value: true }
|
||||
});
|
||||
|
||||
const newRating = stats._avg.value || 0;
|
||||
const newRatingCount = stats._count.value || 0;
|
||||
|
||||
// 4. Update the business
|
||||
const updatedBusiness = await prisma.business.update({
|
||||
where: { id: businessId },
|
||||
data: {
|
||||
rating: newRating,
|
||||
ratingCount: newRatingCount
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
rating: updatedBusiness.rating,
|
||||
ratingCount: updatedBusiness.ratingCount,
|
||||
message: 'Vote enregistré avec succès'
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Error rating business:', error);
|
||||
return NextResponse.json({ error: 'Erreur serveur lors du vote' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user