MIF_E31220277/denta-api/app/api/stats/route.ts

208 lines
5.7 KiB
TypeScript

import { NextResponse } from 'next/server';
import db from '@/lib/db';
function formatDate(date: Date | string) {
const d = new Date(date);
return d.toISOString().split('T')[0];
}
export async function GET() {
try {
// Ambil total count dari setiap tabel utama
const [
userCount,
koasCount,
pasienCount,
fasilitatorCount,
appointmentCount,
postCount,
reviewCount,
scheduleCount,
timeslotCount,
universityCount,
treatmentTypeCount,
notificationCount,
likeCount,
] = await Promise.all([
db.user.count(),
db.koasProfile.count(),
db.pasienProfile.count(),
db.fasilitatorProfile.count(),
db.appointment.count(),
db.post.count(),
db.review.count(),
db.schedule.count(),
db.timeslot.count(),
db.university.count(),
db.treatmentType.count(),
db.notification.count(),
db.like.count(),
]);
// Stats by status (contoh untuk appointment & post)
const [appointmentStatus, postStatus, koasStatus] = await Promise.all([
db.appointment.groupBy({
by: ['status'],
_count: { _all: true },
}),
db.post.groupBy({
by: ['status'],
_count: { _all: true },
}),
db.koasProfile.groupBy({
by: ['status'],
_count: { _all: true },
}),
]);
// Stats tambahan untuk chart
// 1. Appointment per hari (Line/Area Chart)
const appointmentPerDay = await db.appointment.groupBy({
by: ['date'],
_count: { _all: true },
orderBy: { date: 'asc' },
});
// 2. Post per hari (Line/Area Chart)
// FIX: Prisma groupBy expects array of string, not object
const postPerDay = await db.post.groupBy({
by: ['createdAt'],
_count: { _all: true },
orderBy: { createdAt: 'asc' },
});
// 3. Appointment per status (Donut/Pie Chart)
const appointmentStatusDonut = appointmentStatus.map((s) => ({
label: s.status,
value: s._count._all,
}));
// 4. Post per status (Donut/Pie Chart)
const postStatusDonut = postStatus.map((s) => ({
label: s.status,
value: s._count._all,
}));
// 5. Jumlah user per role (Bar/Donut Chart)
const userRoleGroup = await db.user.groupBy({
by: ['role'],
_count: { _all: true },
});
// 6. Jumlah koas per universitas (Bar Chart)
const koasPerUniversity = await db.koasProfile.groupBy({
by: ['universityId'],
_count: { _all: true },
});
// Ambil nama universitas
const universityMap: Record<string, string> = {};
const universities = await db.university.findMany();
universities.forEach((u) => {
universityMap[u.id] = u.name;
});
// 7. Jumlah pasien per gender (Donut/Bar Chart)
const pasienGenderGroup = await db.pasienProfile.groupBy({
by: ['gender'],
_count: { _all: true },
});
// 8. Jumlah koas per status (Bar/Donut Chart)
const koasStatusDonut = koasStatus.map((s) => ({
label: s.status,
value: s._count._all,
}));
// 9. Jumlah review per rating (Bar/Donut Chart)
const reviewRatingGroup = await db.review.groupBy({
by: ['rating'],
_count: { _all: true },
orderBy: { rating: 'asc' },
});
// 10. Jumlah timeslot per isAvailable (Donut/Bar Chart)
const timeslotAvailableGroup = await db.timeslot.groupBy({
by: ['isAvailable'],
_count: { _all: true },
});
// Format data untuk chart
const chartData = {
appointmentPerDay: appointmentPerDay.map((a) => ({
date: a.date,
count: a._count._all,
})),
postPerDay: postPerDay.map((p) => ({
date: formatDate(p.createdAt as Date),
count: p._count ? p._count._all : 0,
})),
appointmentStatusDonut,
postStatusDonut,
userRole: userRoleGroup.map((r) => ({
role: r.role,
count: r._count._all,
})),
koasPerUniversity: koasPerUniversity.map((k) => ({
universityId: k.universityId,
university:
k.universityId && universityMap[k.universityId]
? universityMap[k.universityId]
: 'Unknown',
count: k._count._all,
})),
pasienGender: pasienGenderGroup.map((g) => ({
gender: g.gender,
count: g._count._all,
})),
koasStatus: koasStatusDonut,
reviewRating: reviewRatingGroup.map((r) => ({
rating: r.rating,
count: r._count._all,
})),
timeslotAvailable: timeslotAvailableGroup.map((t) => ({
isAvailable: t.isAvailable,
count: t._count._all,
})),
};
return NextResponse.json({
status: 'Success',
message: 'Stats overview fetched successfully',
data: {
users: userCount,
koas: koasCount,
pasien: pasienCount,
fasilitator: fasilitatorCount,
appointments: appointmentCount,
posts: postCount,
reviews: reviewCount,
schedules: scheduleCount,
timeslots: timeslotCount,
universities: universityCount,
treatmentTypes: treatmentTypeCount,
notifications: notificationCount,
likes: likeCount,
appointmentStatus: appointmentStatus.map((s) => ({
status: s.status,
count: s._count._all,
})),
postStatus: postStatus.map((s) => ({
status: s.status,
count: s._count._all,
})),
koasStatus: koasStatus.map((s) => ({
status: s.status,
count: s._count._all,
})),
chartData, // <-- data siap pakai untuk berbagai macam chart
},
});
} catch (error) {
console.error('Error fetching stats overview:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
}