208 lines
5.7 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|