123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/lib/auth';
|
|
import { db } from '@/lib/db';
|
|
import { cookies } from 'next/headers';
|
|
|
|
export async function GET(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
let userId: string | undefined;
|
|
|
|
// Try NextAuth session first
|
|
const session = await getServerSession(authOptions);
|
|
if (session?.user?.id) {
|
|
userId = session.user.id;
|
|
} else {
|
|
// Fallback: Check raw userId cookie (like /api/user does)
|
|
const cookieStore = await cookies();
|
|
userId = cookieStore.get('userId')?.value;
|
|
}
|
|
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
const { searchParams } = new URL(request.url);
|
|
const page = parseInt(searchParams.get('page') || '1');
|
|
const limit = parseInt(searchParams.get('limit') || '20');
|
|
const skip = (page - 1) * limit;
|
|
|
|
// Verify QR ownership and type
|
|
const qrCode = await db.qRCode.findUnique({
|
|
where: { id, userId: userId },
|
|
select: { id: true, contentType: true },
|
|
});
|
|
|
|
if (!qrCode) {
|
|
return NextResponse.json({ error: 'QR code not found' }, { status: 404 });
|
|
}
|
|
|
|
// Check if consistent with schema (Prisma enum mismatch fix)
|
|
// @ts-ignore - Temporary ignore until client regeneration catches up fully in all envs
|
|
if (qrCode.contentType !== 'FEEDBACK') {
|
|
return NextResponse.json({ error: 'Not a feedback QR code' }, { status: 400 });
|
|
}
|
|
|
|
// Fetch feedback entries (stored as QRScans with ipHash='feedback')
|
|
const [feedbackEntries, totalCount] = await Promise.all([
|
|
db.qRScan.findMany({
|
|
where: { qrId: id, ipHash: 'feedback' },
|
|
orderBy: { ts: 'desc' },
|
|
skip,
|
|
take: limit,
|
|
select: { id: true, userAgent: true, ts: true },
|
|
}),
|
|
db.qRScan.count({
|
|
where: { qrId: id, ipHash: 'feedback' },
|
|
}),
|
|
]);
|
|
|
|
// Parse feedback data from userAgent field
|
|
const feedbacks = feedbackEntries.map((entry) => {
|
|
const parsed = parseFeedback(entry.userAgent || '');
|
|
return {
|
|
id: entry.id,
|
|
rating: parsed.rating,
|
|
comment: parsed.comment,
|
|
date: entry.ts,
|
|
};
|
|
});
|
|
|
|
// Calculate stats
|
|
const allRatings = await db.qRScan.findMany({
|
|
where: { qrId: id, ipHash: 'feedback' },
|
|
select: { userAgent: true },
|
|
});
|
|
|
|
const ratings = allRatings.map((e) => parseFeedback(e.userAgent || '').rating).filter((r) => r > 0);
|
|
const avgRating = ratings.length > 0 ? ratings.reduce((a, b) => a + b, 0) / ratings.length : 0;
|
|
|
|
// Rating distribution
|
|
const distribution = {
|
|
5: ratings.filter((r) => r === 5).length,
|
|
4: ratings.filter((r) => r === 4).length,
|
|
3: ratings.filter((r) => r === 3).length,
|
|
2: ratings.filter((r) => r === 2).length,
|
|
1: ratings.filter((r) => r === 1).length,
|
|
};
|
|
|
|
return NextResponse.json({
|
|
feedbacks,
|
|
stats: {
|
|
total: totalCount,
|
|
avgRating: Math.round(avgRating * 10) / 10,
|
|
distribution,
|
|
},
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
totalPages: Math.ceil(totalCount / limit),
|
|
hasMore: skip + limit < totalCount,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching feedback:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
function parseFeedback(userAgent: string): { rating: number; comment: string } {
|
|
// Format: "rating:4|comment:Great service!"
|
|
const ratingMatch = userAgent.match(/rating:(\d)/);
|
|
const commentMatch = userAgent.match(/comment:(.+)/);
|
|
|
|
return {
|
|
rating: ratingMatch ? parseInt(ratingMatch[1]) : 0,
|
|
comment: commentMatch ? commentMatch[1] : '',
|
|
};
|
|
}
|