Fitur lengkap: - Auth JWT + RBAC (Admin/Teacher) + halaman login - Shell 3 kolom (left nav, topbar, right rail dashboard) - CRUD siswa + follow up kanban + status calon/student/ex - Dashboard: siswa aktif, kehadiran, bar sumber lead, perlu follow up - Pembayaran prorata per pertemuan (cash/transfer/qris) - Kelas, enrollment, generate sesi, kehadiran bulk - Jurnal aktivitas + upload multi-foto - Raport + placement test (tampil di detail siswa) - Pengaturan & seed data contoh Stack: Next.js 16, TypeScript, Tailwind v4, Prisma (SQLite), Recharts, Zod
280 lines
18 KiB
TypeScript
280 lines
18 KiB
TypeScript
/**
|
|
* seed.ts — Contoh data Kodeva (Sistem Manajemen Kursus Coding)
|
|
* Jalankan: pnpm db:seed
|
|
*
|
|
* Login contoh:
|
|
* admin@kodeva.id / password123 (ADMIN)
|
|
* budi@kodeva.id / password123 (TEACHER)
|
|
* rina@kodeva.id / password123 (TEACHER)
|
|
*/
|
|
import { PrismaClient } from "@prisma/client";
|
|
import bcrypt from "bcryptjs";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
const BASE = new Date("2026-09-18T00:00:00.000Z");
|
|
const addDays = (n: number, from: Date = BASE) => new Date(from.getTime() + n * 86_400_000);
|
|
const addWeeks = (n: number, from: Date = BASE) => addDays(n * 7, from);
|
|
const PASS = bcrypt.hashSync("password123", 10);
|
|
const avatar = (seed: string) => `https://i.pravatar.cc/240?u=${encodeURIComponent(seed)}`;
|
|
const photo = (seed: string) => `https://picsum.photos/seed/${encodeURIComponent(seed)}/900/600`;
|
|
|
|
type Att = "PRESENT" | "LATE" | "EXCUSED" | "SICK" | "ABSENT";
|
|
type Level = "BEGINNER" | "INTERMEDIATE" | "ADVANCED";
|
|
|
|
async function main() {
|
|
console.log("🌱 Seed Kodeva dimulai...");
|
|
|
|
await prisma.followUpActivity.deleteMany();
|
|
await prisma.followUp.deleteMany();
|
|
await prisma.placementScore.deleteMany();
|
|
await prisma.placementTest.deleteMany();
|
|
await prisma.reportScore.deleteMany();
|
|
await prisma.reportCard.deleteMany();
|
|
await prisma.journalStudent.deleteMany();
|
|
await prisma.journalPhoto.deleteMany();
|
|
await prisma.journal.deleteMany();
|
|
await prisma.payment.deleteMany();
|
|
await prisma.attendance.deleteMany();
|
|
await prisma.session.deleteMany();
|
|
await prisma.enrollment.deleteMany();
|
|
await prisma.student.deleteMany();
|
|
await prisma.class.deleteMany();
|
|
await prisma.user.deleteMany();
|
|
await prisma.setting.deleteMany();
|
|
|
|
// 1. USERS
|
|
const sari = await prisma.user.create({
|
|
data: { name: "Sari Wulandari", email: "admin@kodeva.id", passwordHash: PASS, role: "ADMIN", phone: "0811-2000-100", avatarUrl: avatar("sari") },
|
|
});
|
|
const budi = await prisma.user.create({
|
|
data: { name: "Budi Santoso", email: "budi@kodeva.id", passwordHash: PASS, role: "TEACHER", phone: "0812-3000-200", avatarUrl: avatar("budi") },
|
|
});
|
|
const rina = await prisma.user.create({
|
|
data: { name: "Rina Kartika", email: "rina@kodeva.id", passwordHash: PASS, role: "TEACHER", phone: "0813-4000-300", avatarUrl: avatar("rina") },
|
|
});
|
|
const ahmad = await prisma.user.create({
|
|
data: { name: "Ahmad Fauzi", email: "ahmad@kodeva.id", passwordHash: PASS, role: "TEACHER", phone: "0814-5000-400", avatarUrl: avatar("ahmad") },
|
|
});
|
|
|
|
// 2. KELAS
|
|
const classesData = [
|
|
{ code: "KOD-SCR-01", name: "Scratch Junior", level: "BEGINNER" as Level, teacherId: rina.id, totalMeetings: 8, packagePrice: 1_200_000, pricePerMeeting: 150_000, capacity: 10, schedule: "Senin & Rabu 16:00-17:30", color: "#2AA9FF", description: "Belajar logika pemrograman lewat game visual Scratch." },
|
|
{ code: "KOD-PYT-01", name: "Python Dasar", level: "BEGINNER" as Level, teacherId: budi.id, totalMeetings: 10, packagePrice: 2_000_000, pricePerMeeting: 200_000, capacity: 12, schedule: "Selasa & Kamis 17:00-18:30", color: "#7C5CFC", description: "Fondasi Python: variabel, logika, loop, dan fungsi." },
|
|
{ code: "KOD-WEB-01", name: "Web Dev: HTML & CSS", level: "BEGINNER" as Level, teacherId: ahmad.id, totalMeetings: 12, packagePrice: 2_400_000, pricePerMeeting: 200_000, capacity: 14, schedule: "Sabtu 09:00-12:00", color: "#12B886", description: "Membangun website pertamamu dari nol." },
|
|
{ code: "KOD-JS-01", name: "JavaScript Intermediate", level: "INTERMEDIATE" as Level, teacherId: budi.id, totalMeetings: 10, packagePrice: 2_500_000, pricePerMeeting: 250_000, capacity: 12, schedule: "Selasa & Kamis 18:45-20:15", color: "#FFB020", description: "DOM, event, async, dan mini project interaktif." },
|
|
{ code: "KOD-ARD-01", name: "Arduino & Robotics", level: "INTERMEDIATE" as Level, teacherId: rina.id, totalMeetings: 8, packagePrice: 1_800_000, pricePerMeeting: 225_000, capacity: 8, schedule: "Jumat 15:30-17:30", color: "#F65FA7", description: "Rakit dan program robot pertamamu dengan Arduino." },
|
|
];
|
|
const klass: Record<string, { id: string; totalMeetings: number; pricePerMeeting: number | null; packagePrice: number; teacherId: string | null; name: string }> = {};
|
|
for (const c of classesData) {
|
|
const created = await prisma.class.create({ data: { ...c, startDate: addWeeks(-10), status: "ACTIVE" } });
|
|
klass[c.code] = created;
|
|
}
|
|
|
|
// 3. SISWA
|
|
const studentsSeed = [
|
|
{ key: "andi", fullName: "Andi Pratama", phone: "0812-1111-0001", email: "andi@example.com", parentName: "Budi Pratama", parentPhone: "0812-1111-9001", leadSource: "REFERRAL", status: "STUDENT", joinedAt: addWeeks(-10) },
|
|
{ key: "citra", fullName: "Citra Lestari", phone: "0812-1111-0002", email: "citra@example.com", parentName: "Sari Lestari", parentPhone: "0812-1111-9002", leadSource: "WHATSAPP", status: "CALON_STUDENT", joinedAt: null as Date | null },
|
|
{ key: "dimas", fullName: "Dimas Anggara", phone: "0812-1111-0003", email: "dimas@example.com", parentName: "Hendra Anggara", parentPhone: "0812-1111-9003", leadSource: "BANNER", status: "STUDENT", joinedAt: addWeeks(-9) },
|
|
{ key: "elsa", fullName: "Elsa Maharani", phone: "0812-1111-0004", email: "elsa@example.com", parentName: "Rina Maharani", parentPhone: "0812-1111-9004", leadSource: "SOCIAL_MEDIA", status: "CALON_STUDENT", joinedAt: null as Date | null },
|
|
{ key: "fajar", fullName: "Fajar Nugroho", phone: "0812-1111-0005", email: "fajar@example.com", parentName: "Agus Nugroho", parentPhone: "0812-1111-9005", leadSource: "WHATSAPP", status: "STUDENT", joinedAt: addWeeks(-8) },
|
|
{ key: "gita", fullName: "Gita Permata", phone: "0812-1111-0006", email: "gita@example.com", parentName: "Dwi Permata", parentPhone: "0812-1111-9006", leadSource: "REFERRAL", status: "EX_STUDENT", joinedAt: addWeeks(-40) },
|
|
{ key: "hadi", fullName: "Hadi Susanto", phone: "0812-1111-0007", email: "hadi@example.com", parentName: "Tono Susanto", parentPhone: "0812-1111-9007", leadSource: "SOCIAL_MEDIA", status: "STUDENT", joinedAt: addWeeks(-6) },
|
|
{ key: "intan", fullName: "Intan Puspita", phone: "0812-1111-0008", email: "intan@example.com", parentName: "Maya Puspita", parentPhone: "0812-1111-9008", leadSource: "BANNER", status: "CALON_STUDENT", joinedAt: null as Date | null },
|
|
{ key: "joko", fullName: "Joko Wijaya", phone: "0812-1111-0009", email: "joko@example.com", parentName: "Slamet Wijaya", parentPhone: "0812-1111-9009", leadSource: "WHATSAPP", status: "STUDENT", joinedAt: addWeeks(-4) },
|
|
{ key: "kirana", fullName: "Kirana Dewi", phone: "0812-1111-0010", email: "kirana@example.com", parentName: "Ratna Dewi", parentPhone: "0812-1111-9010", leadSource: "REFERRAL", status: "EX_STUDENT", joinedAt: addWeeks(-36) },
|
|
];
|
|
const student: Record<string, { id: string; joinedAt: Date | null }> = {};
|
|
for (const s of studentsSeed) {
|
|
const created = await prisma.student.create({
|
|
data: {
|
|
fullName: s.fullName, phone: s.phone, email: s.email, photoUrl: avatar(s.key),
|
|
parentName: s.parentName, parentPhone: s.parentPhone, leadSource: s.leadSource,
|
|
status: s.status, joinedAt: s.joinedAt,
|
|
birthDate: new Date("2012-05-10T00:00:00.000Z"), gender: "MALE", address: "Jl. Merdeka No. 10, Bandung",
|
|
},
|
|
});
|
|
student[s.key] = created;
|
|
}
|
|
|
|
// 4. ENROLLMENT
|
|
const enrollments = [
|
|
{ key: "andi", code: "KOD-PYT-01", status: "ACTIVE" },
|
|
{ key: "dimas", code: "KOD-SCR-01", status: "ACTIVE" },
|
|
{ key: "fajar", code: "KOD-WEB-01", status: "ACTIVE" },
|
|
{ key: "gita", code: "KOD-JS-01", status: "COMPLETED" },
|
|
{ key: "hadi", code: "KOD-ARD-01", status: "ACTIVE" },
|
|
{ key: "joko", code: "KOD-PYT-01", status: "ACTIVE" },
|
|
{ key: "kirana", code: "KOD-SCR-01", status: "COMPLETED" },
|
|
];
|
|
for (const e of enrollments) {
|
|
await prisma.enrollment.create({
|
|
data: { studentId: student[e.key].id, classId: klass[e.code].id, status: e.status, joinedAt: student[e.key].joinedAt ?? BASE, leftAt: e.status === "COMPLETED" ? addWeeks(-2) : null },
|
|
});
|
|
}
|
|
|
|
// 5. SESSION
|
|
const session: Record<string, { id: string }[]> = {};
|
|
for (const code of Object.keys(klass)) {
|
|
const c = klass[code];
|
|
session[code] = [];
|
|
for (let i = 1; i <= c.totalMeetings; i++) {
|
|
const s = await prisma.session.create({
|
|
data: { classId: c.id, meetingNumber: i, date: addWeeks(-10 + i - 1), topic: `Pertemuan ${i}: ${c.name}`, teacherId: c.teacherId },
|
|
});
|
|
session[code].push({ id: s.id });
|
|
}
|
|
}
|
|
|
|
// 6. KEHADIRAN
|
|
const plan: Record<string, { code: string; statuses: Att[] }> = {
|
|
andi: { code: "KOD-PYT-01", statuses: ["PRESENT", "PRESENT", "LATE", "PRESENT", "PRESENT", "PRESENT"] },
|
|
dimas: { code: "KOD-SCR-01", statuses: ["PRESENT", "PRESENT", "ABSENT", "PRESENT"] },
|
|
fajar: { code: "KOD-WEB-01", statuses: ["PRESENT", "LATE", "PRESENT", "PRESENT", "EXCUSED", "PRESENT"] },
|
|
gita: { code: "KOD-JS-01", statuses: ["PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT", "PRESENT"] },
|
|
hadi: { code: "KOD-ARD-01", statuses: ["PRESENT", "PRESENT", "PRESENT", "PRESENT"] },
|
|
joko: { code: "KOD-PYT-01", statuses: ["PRESENT", "SICK", "PRESENT", "PRESENT", "PRESENT"] },
|
|
kirana: { code: "KOD-SCR-01", statuses: ["PRESENT", "PRESENT", "LATE", "PRESENT", "PRESENT", "PRESENT"] },
|
|
};
|
|
let attendanceCount = 0;
|
|
for (const [key, p] of Object.entries(plan)) {
|
|
for (let i = 0; i < p.statuses.length; i++) {
|
|
await prisma.attendance.create({
|
|
data: { sessionId: session[p.code][i].id, studentId: student[key].id, status: p.statuses[i], recordedById: klass[p.code].teacherId, note: p.statuses[i] === "SICK" ? "Demam" : null },
|
|
});
|
|
attendanceCount++;
|
|
}
|
|
}
|
|
|
|
// 7. PEMBAYARAN PRORATA
|
|
const paymentsSeed = [
|
|
{ key: "andi", code: "KOD-PYT-01", meetingsPaid: 10, method: "TRANSFER", status: "PAID", paidAt: addWeeks(-10) },
|
|
{ key: "dimas", code: "KOD-SCR-01", meetingsPaid: 8, method: "CASH", status: "PAID", paidAt: addWeeks(-9) },
|
|
{ key: "fajar", code: "KOD-WEB-01", meetingsPaid: 6, method: "QRIS", status: "PAID", paidAt: addWeeks(-8), note: "Bayar 6 pertemuan dulu" },
|
|
{ key: "gita", code: "KOD-JS-01", meetingsPaid: 10, method: "TRANSFER", status: "PAID", paidAt: addWeeks(-40) },
|
|
{ key: "hadi", code: "KOD-ARD-01", meetingsPaid: 8, method: "TRANSFER", status: "PAID", paidAt: addWeeks(-6) },
|
|
{ key: "joko", code: "KOD-PYT-01", meetingsPaid: 5, method: "QRIS", status: "PARTIAL", paidAt: addWeeks(-4), note: "Cicilan 1 dari 2" },
|
|
{ key: "kirana", code: "KOD-SCR-01", meetingsPaid: 8, method: "CASH", status: "PAID", paidAt: addWeeks(-36) },
|
|
];
|
|
let paySeq = 1;
|
|
const createdPayments: { key: string; code: string; meetingsPaid: number; ppm: number }[] = [];
|
|
for (const p of paymentsSeed) {
|
|
const c = klass[p.code];
|
|
const ppm = c.pricePerMeeting ?? Math.round(c.packagePrice / c.totalMeetings);
|
|
await prisma.payment.create({
|
|
data: {
|
|
invoiceNumber: `INV-2026-${String(paySeq++).padStart(4, "0")}`,
|
|
studentId: student[p.key].id, classId: c.id,
|
|
meetingsPaid: p.meetingsPaid, pricePerMeeting: ppm, amount: p.meetingsPaid * ppm,
|
|
method: p.method, status: p.status, paidAt: p.paidAt, note: p.note ?? null, createdById: sari.id,
|
|
},
|
|
});
|
|
createdPayments.push({ key: p.key, code: p.code, meetingsPaid: p.meetingsPaid, ppm });
|
|
}
|
|
|
|
// 8. JURNAL
|
|
const journalsSeed = [
|
|
{ code: "KOD-SCR-01", title: "Membuat Game Ular dengan Scratch", description: "Anak-anak belajar event, loop, dan variabel skor sambil membuat game ular versi mereka sendiri.", tags: ["game", "scratch", "logika"], date: addWeeks(-2), students: ["dimas", "kirana"], photos: ["ular-1", "ular-2", "ular-3"] },
|
|
{ code: "KOD-PYT-01", title: "Dasar Variabel & Tipe Data Python", description: "Sesi seru bereksperimen dengan string, integer, dan input dari pengguna.", tags: ["python", "variable", "basic"], date: addWeeks(-3), students: ["andi", "joko"], photos: ["python-1", "python-2"] },
|
|
{ code: "KOD-WEB-01", title: "Landing Page Pertama", description: "Setiap siswa mempublikasikan halaman profil pribadinya menggunakan HTML & CSS.", tags: ["html", "css", "project"], date: addWeeks(-1), students: ["fajar"], photos: ["web-1", "web-2", "web-3", "web-4"] },
|
|
{ code: "KOD-ARD-01", title: "Sensor Cahaya dengan Arduino", description: "Merakit lampu otomatis yang menyala saat ruangan gelap.", tags: ["arduino", "sensor", "robotik"], date: addDays(-5), students: ["hadi"], photos: ["arduino-1", "arduino-2"] },
|
|
];
|
|
for (const j of journalsSeed) {
|
|
const c = klass[j.code];
|
|
await prisma.journal.create({
|
|
data: {
|
|
classId: c.id, sessionId: session[j.code][0].id, title: j.title, description: j.description,
|
|
activityDate: j.date, tags: j.tags.join(","), authorId: c.teacherId!,
|
|
photos: { create: j.photos.map((s, i) => ({ url: photo(s), caption: `Dokumentasi ${i + 1}`, sortOrder: i })) },
|
|
students: { create: j.students.map((k) => ({ studentId: student[k].id })) },
|
|
},
|
|
});
|
|
}
|
|
|
|
// 9. RAPORT
|
|
const reportsSeed = [
|
|
{ key: "andi", code: "KOD-PYT-01", period: "2026-Term1", scores: [["Logika", 88], ["Computational Thinking", 85], ["Problem Solving", 82], ["Kreativitas", 90], ["Kolaborasi", 86]], notes: "Andi sangat aktif bertanya dan cepat memahami konsep loop." },
|
|
{ key: "dimas", code: "KOD-SCR-01", period: "2026-Term1", scores: [["Logika", 80], ["Computational Thinking", 78], ["Problem Solving", 75], ["Kreativitas", 92], ["Kolaborasi", 88]], notes: "Kreativitas Dimas luar biasa, perlu latihan ketelitian." },
|
|
{ key: "fajar", code: "KOD-WEB-01", period: "2026-Term1", scores: [["Logika", 84], ["Computational Thinking", 82], ["Problem Solving", 86], ["Kreativitas", 85], ["Kolaborasi", 80]], notes: "Progres pesat di CSS layout." },
|
|
] as { key: string; code: string; period: string; scores: [string, number][]; notes: string }[];
|
|
for (const r of reportsSeed) {
|
|
const avg = Math.round(r.scores.reduce((a, [, v]) => a + v, 0) / r.scores.length);
|
|
const grade = avg >= 90 ? "A" : avg >= 80 ? "B" : avg >= 70 ? "C" : "D";
|
|
await prisma.reportCard.create({
|
|
data: {
|
|
studentId: student[r.key].id, classId: klass[r.code].id, period: r.period,
|
|
teacherId: klass[r.code].teacherId!, finalScore: avg, grade, teacherNotes: r.notes,
|
|
attendanceSummary: "Hadir 6 dari 6 pertemuan", publishedAt: addDays(-3),
|
|
scores: { create: r.scores.map(([aspect, score]) => ({ aspect, score })) },
|
|
},
|
|
});
|
|
}
|
|
|
|
// 10. PLACEMENT TEST
|
|
const placementsSeed = [
|
|
{ key: "elsa", level: "INTERMEDIATE" as Level, rec: "KOD-JS-01", scores: [["Logika", 82], ["Dasar Coding", 76], ["Problem Solving", 80], ["Kreativitas", 74]] },
|
|
{ key: "intan", level: "BEGINNER" as Level, rec: "KOD-SCR-01", scores: [["Logika", 62], ["Dasar Coding", 48], ["Problem Solving", 55], ["Kreativitas", 70]] },
|
|
{ key: "citra", level: "BEGINNER" as Level, rec: "KOD-PYT-01", scores: [["Logika", 50], ["Dasar Coding", 40], ["Problem Solving", 45], ["Kreativitas", 60]] },
|
|
{ key: "andi", level: "INTERMEDIATE" as Level, rec: "KOD-JS-01", scores: [["Logika", 80], ["Dasar Coding", 78], ["Problem Solving", 72], ["Kreativitas", 68]] },
|
|
] as { key: string; level: Level; rec: string; scores: [string, number][] }[];
|
|
for (const p of placementsSeed) {
|
|
const total = Math.round(p.scores.reduce((a, [, v]) => a + v, 0) / p.scores.length);
|
|
await prisma.placementTest.create({
|
|
data: {
|
|
studentId: student[p.key].id, testDate: addDays(-12), levelResult: p.level,
|
|
recommendedClassId: klass[p.rec].id, totalScore: total, examinerId: rina.id,
|
|
notes: `Direkomendasikan masuk kelas ${klass[p.rec].name}.`,
|
|
scores: { create: p.scores.map(([category, score]) => ({ category, score })) },
|
|
},
|
|
});
|
|
}
|
|
|
|
// 11. FOLLOW UP
|
|
const followUpsSeed = [
|
|
{ key: "citra", status: "CONTACTED", picId: sari.id, next: addDays(-1), last: addDays(-3), channel: "WHATSAPP", activities: [["wa", "Kirim brosur Python Dasar, ortu minta dihubungi lagi."]] },
|
|
{ key: "elsa", status: "NEW", picId: budi.id, next: addDays(0), last: null as Date | null, channel: "SOCIAL_MEDIA", activities: [["wa", "Lead dari Instagram, tanya jadwal kelas."]] },
|
|
{ key: "intan", status: "TRIAL", picId: rina.id, next: addDays(2), last: addDays(-1), channel: "BANNER", activities: [["call", "Sudah trial Scratch, tinggal konfirmasi pembayaran."]] },
|
|
] as { key: string; status: string; picId: string; next: Date; last: Date | null; channel: string; activities: [string, string][] }[];
|
|
for (const f of followUpsSeed) {
|
|
await prisma.followUp.create({
|
|
data: {
|
|
studentId: student[f.key].id, status: f.status, picId: f.picId,
|
|
nextFollowUpAt: f.next, lastContactedAt: f.last, channel: f.channel,
|
|
activities: { create: f.activities.map(([type, note]) => ({ type, note, authorId: f.picId })) },
|
|
},
|
|
});
|
|
}
|
|
|
|
// 12. SETTINGS
|
|
await prisma.setting.createMany({
|
|
data: [
|
|
{ key: "academy", value: JSON.stringify({ name: "Kodeva Academy", address: "Jl. Coding Raya No. 1, Bandung", phone: "0821-1000-2000", email: "halo@kodeva.id" }) },
|
|
{ key: "leadSources", value: JSON.stringify([
|
|
{ value: "WHATSAPP", label: "WhatsApp", color: "#22C55E", active: true },
|
|
{ value: "REFERRAL", label: "Referral", color: "#7C5CFC", active: true },
|
|
{ value: "BANNER", label: "Banner", color: "#FFB020", active: true },
|
|
{ value: "SOCIAL_MEDIA", label: "Social Media", color: "#2AA9FF", active: true },
|
|
]) },
|
|
{ key: "currency", value: JSON.stringify({ code: "IDR", locale: "id-ID", timezone: "Asia/Jakarta" }) },
|
|
],
|
|
});
|
|
|
|
const [activeStudents, exStudents, followUpDue] = await Promise.all([
|
|
prisma.student.count({ where: { status: "STUDENT" } }),
|
|
prisma.student.count({ where: { status: "EX_STUDENT" } }),
|
|
prisma.followUp.count({ where: { status: { notIn: ["ENROLLED", "LOST"] }, nextFollowUpAt: { lte: addDays(1) } } }),
|
|
]);
|
|
|
|
console.log("✅ Seed selesai.");
|
|
console.log(` 👥 Siswa aktif : ${activeStudents}`);
|
|
console.log(` 🎓 Ex student : ${exStudents}`);
|
|
console.log(` 🔔 Perlu follow up : ${followUpDue}`);
|
|
console.log(` 📚 Kelas : ${Object.keys(klass).length}`);
|
|
console.log(` 🗓️ Kehadiran : ${attendanceCount} record`);
|
|
console.log("\n Login: admin@kodeva.id / password123");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => { console.error("❌ Seed gagal:", e); process.exit(1); })
|
|
.finally(async () => { await prisma.$disconnect(); });
|