421 lines
14 KiB
TypeScript
421 lines
14 KiB
TypeScript
import "dotenv/config";
|
||
import { PrismaClient } from "../app/generated/prisma/client";
|
||
import { PrismaPg } from "@prisma/adapter-pg";
|
||
import bcrypt from "bcryptjs";
|
||
|
||
const adapter = new PrismaPg({
|
||
connectionString: process.env.DATABASE_URL!,
|
||
});
|
||
const prisma = new PrismaClient({ adapter });
|
||
|
||
async function main() {
|
||
console.log("🌱 Seeding database...\n");
|
||
|
||
// Clean existing data (order matters for FK)
|
||
await prisma.tripReview.deleteMany();
|
||
await prisma.tripParticipant.deleteMany();
|
||
await prisma.tripImage.deleteMany();
|
||
await prisma.trip.deleteMany();
|
||
await prisma.organizerVerification.deleteMany();
|
||
await prisma.user.deleteMany();
|
||
|
||
// ==================== USERS ====================
|
||
|
||
const password = await bcrypt.hash("password123", 12);
|
||
|
||
// Organizer
|
||
const dede = await prisma.user.create({
|
||
data: {
|
||
name: "Dede Inoen",
|
||
email: "dede.inoen@setrip.id",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const panji = await prisma.user.create({
|
||
data: {
|
||
name: "Panji Petualang",
|
||
email: "panji@setrip.id",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const fiersa = await prisma.user.create({
|
||
data: {
|
||
name: "Fiersa Besari",
|
||
email: "fiersa@setrip.id",
|
||
password,
|
||
},
|
||
});
|
||
|
||
// User biasa (peserta)
|
||
const budi = await prisma.user.create({
|
||
data: {
|
||
name: "Budi Santoso",
|
||
email: "budi@gmail.com",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const sari = await prisma.user.create({
|
||
data: {
|
||
name: "Sari Dewi",
|
||
email: "sari@gmail.com",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const doni = await prisma.user.create({
|
||
data: {
|
||
name: "Doni Prasetyo",
|
||
email: "doni@gmail.com",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const maya = await prisma.user.create({
|
||
data: {
|
||
name: "Maya Putri",
|
||
email: "maya@gmail.com",
|
||
password,
|
||
},
|
||
});
|
||
|
||
const raka = await prisma.user.create({
|
||
data: {
|
||
name: "Raka Aditya",
|
||
email: "raka@gmail.com",
|
||
password,
|
||
},
|
||
});
|
||
|
||
console.log("✅ Users created");
|
||
console.log(" Organizer: dede.inoen@setrip.id, panji@setrip.id, fiersa@setrip.id");
|
||
console.log(" Peserta: budi, sari, doni, maya, raka @gmail.com");
|
||
console.log(" Password semua: password123\n");
|
||
|
||
// ==================== ORGANIZER VERIFICATIONS ====================
|
||
|
||
const verifiedAt = new Date();
|
||
await prisma.organizerVerification.createMany({
|
||
data: [
|
||
{
|
||
userId: dede.id,
|
||
fullName: "Dede Inoen",
|
||
nik: "3201010101010001",
|
||
birthDate: new Date(Date.UTC(1990, 0, 1)),
|
||
address: "Jl. Pendaki No. 1, Garut, Jawa Barat",
|
||
ktpImageUrl: "https://placehold.co/600x400/png?text=KTP+Dede",
|
||
selfieUrl: "https://placehold.co/600x400/png?text=Selfie+Dede",
|
||
bankName: "BCA",
|
||
bankAccountNumber: "1234567890",
|
||
bankAccountName: "Dede Inoen",
|
||
status: "APPROVED",
|
||
reviewedAt: verifiedAt,
|
||
verifiedAt,
|
||
},
|
||
{
|
||
userId: panji.id,
|
||
fullName: "Panji Petualang",
|
||
nik: "3201010101010002",
|
||
birthDate: new Date(Date.UTC(1985, 5, 15)),
|
||
address: "Jl. Adventure No. 7, Kuningan, Jawa Barat",
|
||
ktpImageUrl: "https://placehold.co/600x400/png?text=KTP+Panji",
|
||
selfieUrl: "https://placehold.co/600x400/png?text=Selfie+Panji",
|
||
bankName: "Mandiri",
|
||
bankAccountNumber: "9876543210",
|
||
bankAccountName: "Panji Petualang",
|
||
status: "APPROVED",
|
||
reviewedAt: verifiedAt,
|
||
verifiedAt,
|
||
},
|
||
],
|
||
});
|
||
console.log("✅ OrganizerVerification (APPROVED) untuk Dede & Panji\n");
|
||
|
||
// ==================== TRIPS + IMAGES ====================
|
||
/**
|
||
* Tanggal disimpan eksplisit di UTC agar filter `from`/`to` (YYYY-MM-DD UTC)
|
||
* cocok dengan yang tampil di daftar.
|
||
*
|
||
* - Multi hari: isi `endDate` = hari terakhir trip (UTC).
|
||
* - Satu hari / night hike satu malam: `endDate` null — filter memakai instan `date`
|
||
* dalam rentang hari UTC yang sama (jam tetap masuk hari itu).
|
||
*/
|
||
const utc = (y: number, m0: number, d: number, h = 12, min = 0) =>
|
||
new Date(Date.UTC(y, m0, d, h, min, 0, 0));
|
||
|
||
// Unsplash mountain photos (URL CDN publik, gratis, stabil).
|
||
// Slug ID di komentar = id di unsplash.com/photos/{slug} buat ditelusuri ulang.
|
||
const img = (id: string) =>
|
||
`https://images.unsplash.com/photo-${id}?w=1200&q=80&auto=format&fit=crop`;
|
||
const MOUNTAIN_PHOTOS = {
|
||
papandayan1: img("1554629947-334ff61d85dc"), // xfngap_DToE
|
||
papandayan2: img("1464822759023-fed622ff2c3b"), // Bkci_8qcdvQ
|
||
papandayan3: img("1454496522488-7a8e488e8606"), // 9wg5jCEPBsw
|
||
ciremai1: img("1480497490787-505ec076689f"), // 6bKxagnIDtk
|
||
ciremai2: img("1483728642387-6c3bdd6c93e5"), // YFFGkE3y4F8
|
||
ciremai3: img("1502085671122-2d218cd434e6"), // NNmiv6zcFvk
|
||
gede1: img("1478059299873-f047d8c5fe1a"), // DXQB5D1njMY
|
||
gede2: img("1519681393784-d120267933ba"), // z8ct_Q3oCqM
|
||
gede3: img("1501785888041-af3ef285b470"), // T7K4aEPoGGk
|
||
gede4: img("1540979388789-6cee28a1cdc9"), // eUFfY6cwjSU
|
||
tangkuban1: img("1506905925346-21bda4d32df4"), // 1527pjeb6jg
|
||
tangkuban2: img("1490682143684-14369e18dce8"), // 8c6eS43iq1o
|
||
malabar1: img("1494548162494-384bba4ab999"), // xP_AGmeEa6s
|
||
malabar2: img("1500964757637-c85e8a162699"), // twukN12EN7c
|
||
malabar3: img("1549880181-56a44cf4a9a5"), // ePpaQC2c1xA
|
||
guntur1: img("1558883493-8b86ff880fec"), // vaG8rOJLDHo
|
||
guntur2: img("1554629947-334ff61d85dc"), // reuse — xfngap_DToE
|
||
guntur3: img("1464822759023-fed622ff2c3b"), // reuse — Bkci_8qcdvQ
|
||
} as const;
|
||
|
||
// --- Trip 1: Papandayan (by Dede Inoen) — 2 hari ---
|
||
const trip1 = await prisma.trip.create({
|
||
data: {
|
||
title: "Open Trip Papandayan Weekend",
|
||
description: `Pendakian santai ke Gunung Papandayan, cocok untuk pemula!
|
||
|
||
⚠️ Bawa: Sleeping bag, jaket, headlamp, air 2L`,
|
||
meetingPoint:
|
||
"Alun-alun Garut (depan pendopo), Sabtu 05:00 WIB — detail grup WA.",
|
||
itinerary: `Sabtu
|
||
• 05:00 Meeting & briefing
|
||
• 07:00 Berangkat menuju basecamp
|
||
• 12:00 Makan siang trail
|
||
• 15:00 Camp area Pondok Salada
|
||
|
||
Minggu
|
||
• 04:00 Summit attack
|
||
• 08:00 Sarap & packing
|
||
• 11:00 Turun
|
||
• 16:00 Estimasi kembali ke Garut`,
|
||
whatsIncluded: `• Transport PP Garut–basecamp
|
||
• Guide lokal
|
||
• Tenda tim (kapasitas sesuai muatan)
|
||
• Konsumsi: makan 3x + snack`,
|
||
whatsExcluded: `• Tiket masuk TNGGP
|
||
• Sleeping bag & matras pribadi
|
||
• Asuransi perjalanan`,
|
||
mountain: "Gunung Papandayan",
|
||
location: "Garut, Jawa Barat",
|
||
date: utc(2026, 3, 23, 8, 0),
|
||
endDate: utc(2026, 3, 24, 18, 0),
|
||
maxParticipants: 10,
|
||
price: 250000,
|
||
status: "OPEN",
|
||
organizerId: dede.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.papandayan1, caption: "Kawah Papandayan", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.papandayan2, caption: "Track menuju puncak", order: 1 },
|
||
{ url: MOUNTAIN_PHOTOS.papandayan3, caption: "Camping ground Pondok Salada", order: 2 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
// --- Trip 2: Ciremai (by Panji Petualang) — 2 hari ---
|
||
const trip2 = await prisma.trip.create({
|
||
data: {
|
||
title: "Pendakian Ciremai via Apuy",
|
||
description: `Trip ke puncak tertinggi Jawa Barat! 🏔️
|
||
|
||
📍 Meeting Point: Stasiun Kuningan, 04:00 WIB
|
||
🎒 Fasilitas: Transport lokal, guide, logistik
|
||
⚠️ Level: Menengah — perlu stamina baik
|
||
|
||
Itinerary:
|
||
- Hari 1: Basecamp → Pos 3 → Camp
|
||
- Hari 2: Summit attack → Turun → Pulang`,
|
||
mountain: "Gunung Ciremai",
|
||
location: "Kuningan, Jawa Barat",
|
||
date: utc(2026, 3, 25, 4, 0),
|
||
endDate: utc(2026, 3, 26, 18, 0),
|
||
maxParticipants: 8,
|
||
price: 350000,
|
||
status: "OPEN",
|
||
organizerId: panji.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.ciremai1, caption: "Puncak Ciremai 3.078 mdpl", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.ciremai2, caption: "Jalur pendakian via Apuy", order: 1 },
|
||
{ url: MOUNTAIN_PHOTOS.ciremai3, caption: "Sunrise dari puncak", order: 2 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
// --- Trip 3: Gede-Pangrango (by Fiersa Besari) — 2 hari ---
|
||
const trip3 = await prisma.trip.create({
|
||
data: {
|
||
title: "Sunrise Trip Gede-Pangrango",
|
||
description: `Combo 2 puncak sekaligus! Gede + Pangrango.
|
||
|
||
📍 Meeting Point: Cibodas, 22:00 WIB (malam)
|
||
🎒 Fasilitas: Guide, tenda, makan
|
||
⚠️ Level: Advance — night hike
|
||
|
||
Start malam, summit saat sunrise. View epic dijamin!`,
|
||
mountain: "Gunung Gede",
|
||
location: "Bogor/Cianjur, Jawa Barat",
|
||
date: utc(2026, 3, 27, 22, 0),
|
||
endDate: utc(2026, 3, 28, 16, 0),
|
||
maxParticipants: 12,
|
||
price: 280000,
|
||
status: "OPEN",
|
||
organizerId: fiersa.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.gede1, caption: "Puncak Gunung Gede", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.gede2, caption: "Surya Kencana padang edelweis", order: 1 },
|
||
{ url: MOUNTAIN_PHOTOS.gede3, caption: "Blue lake / Danau Biru", order: 2 },
|
||
{ url: MOUNTAIN_PHOTOS.gede4, caption: "Night hike track Cibodas", order: 3 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
// --- Trip 4: Tangkuban Parahu (by Dede Inoen) — 1 hari ---
|
||
const trip4 = await prisma.trip.create({
|
||
data: {
|
||
title: "Trip Hemat Tangkuban Parahu",
|
||
description: `Trip santai ke kawah Tangkuban Parahu. Cocok buat first-timer!
|
||
|
||
📍 Meeting Point: Lembang, 07:00 WIB
|
||
🎒 Fasilitas: Transport, snack, guide
|
||
⚠️ Level: Easy — bisa pakai sandal gunung
|
||
|
||
Explore Kawah Ratu, Kawah Domas, foto-foto, terus makan sate maranggi!`,
|
||
mountain: "Gunung Tangkuban Parahu",
|
||
location: "Bandung, Jawa Barat",
|
||
date: utc(2026, 3, 22, 0, 0),
|
||
endDate: null,
|
||
maxParticipants: 15,
|
||
price: 120000,
|
||
status: "OPEN",
|
||
organizerId: dede.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.tangkuban1, caption: "Kawah Ratu", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.tangkuban2, caption: "Kawah Domas", order: 1 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
// --- Trip 5: Malabar (by Fiersa Besari) — 1 hari (night hike, `endDate` null) ---
|
||
const trip5 = await prisma.trip.create({
|
||
data: {
|
||
title: "Malabar Night Hike",
|
||
description: `Night hike ke Gunung Malabar — view kota Bandung dari atas!
|
||
|
||
📍 Meeting Point: Pangalengan, 20:00 WIB
|
||
🎒 Fasilitas: Guide, teh hangat di puncak
|
||
⚠️ Bawa: Headlamp WAJIB, jaket tebal
|
||
|
||
Trip ringan, 3-4 jam naik. Cocok buat yang mau healing malam-malam.`,
|
||
mountain: "Gunung Malabar",
|
||
location: "Bandung, Jawa Barat",
|
||
date: utc(2026, 3, 20, 14, 0),
|
||
endDate: null,
|
||
maxParticipants: 10,
|
||
price: 150000,
|
||
status: "OPEN",
|
||
organizerId: fiersa.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.malabar1, caption: "Puncak Malabar malam hari", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.malabar2, caption: "View Bandung dari atas", order: 1 },
|
||
{ url: MOUNTAIN_PHOTOS.malabar3, caption: "Track pendakian", order: 2 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
// --- Trip 6: Guntur (by Panji Petualang) — 2 hari ---
|
||
const trip6 = await prisma.trip.create({
|
||
data: {
|
||
title: "Guntur Challenge Trip",
|
||
description: `Trip ke Gunung Guntur — jalur menantang tapi worth it!
|
||
|
||
📍 Meeting Point: Alun-alun Garut, 04:30 WIB
|
||
🎒 Fasilitas: Transport, guide, logistik
|
||
⚠️ Level: Hard — medan berbatu & terjal
|
||
|
||
Buat yang suka challenge. Pemandangan kawah aktif dari dekat!`,
|
||
mountain: "Gunung Guntur",
|
||
location: "Garut, Jawa Barat",
|
||
date: utc(2026, 3, 30, 4, 0),
|
||
endDate: utc(2026, 4, 1, 18, 0),
|
||
maxParticipants: 8,
|
||
price: 300000,
|
||
status: "OPEN",
|
||
organizerId: panji.id,
|
||
images: {
|
||
create: [
|
||
{ url: MOUNTAIN_PHOTOS.guntur1, caption: "Kawah aktif Gunung Guntur", order: 0 },
|
||
{ url: MOUNTAIN_PHOTOS.guntur2, caption: "Jalur berbatu menuju puncak", order: 1 },
|
||
{ url: MOUNTAIN_PHOTOS.guntur3, caption: "View dari puncak", order: 2 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
|
||
console.log("✅ 6 Trips + images created (tanggal UTC + endDate untuk trip multi hari)\n");
|
||
console.log(
|
||
` Guntur: ${trip6.title} ${trip6.date.toISOString().slice(0, 10)} → ${trip6.endDate?.toISOString().slice(0, 10) ?? "-"}\n`
|
||
);
|
||
|
||
// ==================== PARTICIPANTS ====================
|
||
|
||
await prisma.tripParticipant.createMany({
|
||
data: [
|
||
// Papandayan — 4 peserta
|
||
{ tripId: trip1.id, userId: budi.id, status: "CONFIRMED" },
|
||
{ tripId: trip1.id, userId: sari.id, status: "CONFIRMED" },
|
||
{ tripId: trip1.id, userId: doni.id, status: "CONFIRMED" },
|
||
{ tripId: trip1.id, userId: raka.id, status: "CONFIRMED" },
|
||
|
||
// Ciremai — 2 peserta
|
||
{ tripId: trip2.id, userId: budi.id, status: "CONFIRMED" },
|
||
{ tripId: trip2.id, userId: maya.id, status: "CONFIRMED" },
|
||
|
||
// Gede — 5 peserta
|
||
{ tripId: trip3.id, userId: budi.id, status: "CONFIRMED" },
|
||
{ tripId: trip3.id, userId: sari.id, status: "CONFIRMED" },
|
||
{ tripId: trip3.id, userId: doni.id, status: "CONFIRMED" },
|
||
{ tripId: trip3.id, userId: maya.id, status: "CONFIRMED" },
|
||
{ tripId: trip3.id, userId: raka.id, status: "CONFIRMED" },
|
||
|
||
// Tangkuban Parahu — 5 peserta
|
||
{ tripId: trip4.id, userId: budi.id, status: "CONFIRMED" },
|
||
{ tripId: trip4.id, userId: sari.id, status: "CONFIRMED" },
|
||
{ tripId: trip4.id, userId: doni.id, status: "CONFIRMED" },
|
||
{ tripId: trip4.id, userId: maya.id, status: "CONFIRMED" },
|
||
{ tripId: trip4.id, userId: raka.id, status: "CONFIRMED" },
|
||
|
||
// Malabar — 2 peserta
|
||
{ tripId: trip5.id, userId: sari.id, status: "CONFIRMED" },
|
||
{ tripId: trip5.id, userId: maya.id, status: "CONFIRMED" },
|
||
|
||
// Guntur — 0 peserta
|
||
],
|
||
});
|
||
|
||
console.log("✅ Participants joined");
|
||
console.log(" Papandayan: 4/10 | Ciremai: 2/8 | Gede: 5/12");
|
||
console.log(" Tangkuban Parahu: 5/15 | Malabar: 2/10 | Guntur: 0/8\n");
|
||
|
||
console.log("🎉 Seed complete!");
|
||
}
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error("❌ Seed failed:", e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|