Files
setrip/prisma/seed.ts
T

546 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "dotenv/config";
import { PrismaClient } from "../app/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import bcrypt from "bcryptjs";
import { encryptString, hmacHex } from "../lib/crypto";
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);
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 },
});
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();
const dedeNik = "3201010101010001";
const panjiNik = "3201010101010002";
await prisma.organizerVerification.createMany({
data: [
{
userId: dede.id,
fullName: "Dede Inoen",
nikEncrypted: encryptString(dedeNik),
nikHash: hmacHex(dedeNik),
birthDate: new Date(Date.UTC(1990, 0, 1)),
address: "Jl. Pendaki No. 1, Garut, Jawa Barat",
ktpImageKey: "ktp/seed-dede.jpg",
livenessKey: "liveness/seed-dede.jpg",
bankName: "BCA",
bankAccountNumber: "1234567890",
bankAccountName: "Dede Inoen",
status: "APPROVED",
reviewedAt: verifiedAt,
verifiedAt,
},
{
userId: panji.id,
fullName: "Panji Petualang",
nikEncrypted: encryptString(panjiNik),
nikHash: hmacHex(panjiNik),
birthDate: new Date(Date.UTC(1985, 5, 15)),
address: "Jl. Adventure No. 7, Kuningan, Jawa Barat",
ktpImageKey: "ktp/seed-panji.jpg",
livenessKey: "liveness/seed-panji.jpg",
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. Multi hari isi `endDate` (hari terakhir
* trip). Trip 1 hari / night-event: `endDate` null.
*/
const utc = (y: number, m0: number, d: number, h = 12, min = 0) =>
new Date(Date.UTC(y, m0, d, h, min, 0, 0));
// Unsplash CDN photo helper. Slug ID di komentar = id di unsplash.com/photos/{slug}.
const img = (id: string) =>
`https://images.unsplash.com/photo-${id}?w=1200&q=80&auto=format&fit=crop`;
// --- HIKING: Papandayan ---
const tripHiking = await prisma.trip.create({
data: {
category: "HIKING",
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, Sabtu 05:00 WIB — detail grup WA.",
itinerary: `Sabtu
• 05:00 Meeting & briefing
• 07:00 Berangkat menuju basecamp
• 15:00 Camp area Pondok Salada
Minggu
• 04:00 Summit attack
• 11:00 Turun
• 16:00 Estimasi kembali ke Garut`,
whatsIncluded: `• Transport PP Garutbasecamp
• Guide lokal
• Tenda tim
• Konsumsi 3x + snack`,
whatsExcluded: `• Tiket masuk TN
• Sleeping bag pribadi
• Asuransi perjalanan`,
destination: "Gunung Papandayan",
location: "Garut, Jawa Barat",
date: utc(2026, 5, 13, 22, 0),
endDate: utc(2026, 5, 14, 18, 0),
maxParticipants: 10,
price: 250000,
status: "OPEN",
organizerId: dede.id,
images: {
create: [
{ url: img("1554629947-334ff61d85dc"), caption: "Kawah Papandayan", order: 0 },
{ url: img("1464822759023-fed622ff2c3b"), caption: "Track menuju puncak", order: 1 },
],
},
},
});
// --- HIKING: Ciremai ---
const tripHikingCiremai = await prisma.trip.create({
data: {
category: "HIKING",
title: "Pendakian Ciremai via Apuy",
description: `Trip ke puncak tertinggi Jawa Barat! 🏔️
📍 Meeting Point: Stasiun Kuningan, 04:00 WIB
⚠️ Level: Menengah — perlu stamina baik`,
destination: "Gunung Ciremai",
location: "Kuningan, Jawa Barat",
date: utc(2026, 5, 23, 4, 0),
endDate: utc(2026, 5, 24, 18, 0),
maxParticipants: 8,
price: 350000,
status: "OPEN",
organizerId: panji.id,
images: {
create: [
{ url: img("1480497490787-505ec076689f"), caption: "Puncak Ciremai 3.078 mdpl", order: 0 },
{ url: img("1502085671122-2d218cd434e6"), caption: "Sunrise dari puncak", order: 1 },
],
},
},
});
// --- CAMPING: Ranca Upas ---
const tripCamping = await prisma.trip.create({
data: {
category: "CAMPING",
title: "Camping Santai di Ranca Upas",
description: `Camping bareng di tengah hutan pinus dengan ditemani rusa-rusa! Cocok buat first-timer.
📍 Meeting Point: Lembang, Sabtu 13:00 WIB
🎒 Fasilitas: Tenda dome, sleeping bag, BBQ, api unggun
🔥 Bonus: Live music akustik malam hari`,
meetingPoint: "Pertigaan Pasar Lembang, Sabtu 13:00 WIB",
whatsIncluded: `• Tenda + sleeping bag + matras
• Logistik camp
• Makan malam BBQ + sarapan
• Tiket masuk lokasi`,
whatsExcluded: `• Transport pribadi
• Snack tambahan`,
destination: "Ranca Upas",
location: "Bandung Selatan, Jawa Barat",
date: utc(2026, 5, 16, 6, 0),
endDate: utc(2026, 5, 17, 12, 0),
maxParticipants: 12,
price: 220000,
status: "OPEN",
organizerId: dede.id,
images: {
create: [
{ url: img("1504280390367-361c6d9f38f4"), caption: "Tenda di hutan pinus", order: 0 },
{ url: img("1517824806704-9040b037703b"), caption: "Sunset di camp", order: 1 },
],
},
},
});
// --- SNORKELING: Pulau Pahawang ---
const tripSnorkel = await prisma.trip.create({
data: {
category: "SNORKELING",
title: "Snorkeling Pulau Pahawang",
description: `Air kristal-jernih, ikan warna-warni, dan pasir putih lembut. Spot terumbu karang Pahawang Kecil masuk daftar must-visit di Lampung.
🤿 Pemula friendly — guide profesional
📷 Underwater photo session included`,
meetingPoint: "Dermaga Ketapang, Lampung Selatan, 07:00 WIB",
whatsIncluded: `• Boat PP
• Alat snorkel (masker, fin, life vest)
• Guide & pemandu underwater
• Konsumsi 2x`,
whatsExcluded: `• Transport ke Lampung
• Penginapan Sabtu malam`,
destination: "Pulau Pahawang Kecil",
location: "Lampung Selatan, Lampung",
date: utc(2026, 5, 30, 0, 0),
endDate: null,
maxParticipants: 10,
price: 380000,
status: "OPEN",
organizerId: panji.id,
images: {
create: [
{ url: img("1583212292454-1fe6229603b7"), caption: "Snorkeling di Pahawang", order: 0 },
{ url: img("1559825481-12a05cc00344"), caption: "Pasir putih Pahawang Kecil", order: 1 },
],
},
},
});
// --- DIVING: Tulamben ---
const tripDiving = await prisma.trip.create({
data: {
category: "DIVING",
title: "Fun Dive Tulamben — USS Liberty Wreck",
description: `Dive trip ke USS Liberty Wreck, salah satu wreck dive paling ikonik di dunia. Visibility tinggi, kedalaman ramah open water diver.
⚠️ Sertifikasi minimal: Open Water (PADI/SSI)`,
meetingPoint: "Dive shop Tulamben, 06:30 WITA",
whatsIncluded: `• 2x dive guided
• Full gear rental
• Tank & weight
• Konsumsi siang`,
whatsExcluded: `• Transport ke Bali
• Penginapan
• Sertifikasi (cek validitas)`,
destination: "USS Liberty Wreck",
location: "Tulamben, Karangasem, Bali",
date: utc(2026, 6, 4, 0, 0),
endDate: utc(2026, 6, 5, 12, 0),
maxParticipants: 6,
price: 1850000,
status: "OPEN",
organizerId: fiersa.id,
images: {
create: [
{ url: img("1544551763-46a013bb70d5"), caption: "Wreck dive Tulamben", order: 0 },
{ url: img("1566024287286-457247b70310"), caption: "Reef Tulamben", order: 1 },
],
},
},
});
// --- ISLAND HOPPING: Karimun Jawa ---
const tripIslandHop = await prisma.trip.create({
data: {
category: "ISLAND_HOPPING",
title: "Karimun Jawa Island Hopping 3D2N",
description: `Hopping 5 pulau favorit di Karimun Jawa: Menjangan Kecil, Menjangan Besar, Cemara, Cilik, Geleang. Snorkeling, sunset, dan barbeque tepi pantai.
🏝️ Cocok untuk solo traveler & couple`,
meetingPoint: "Pelabuhan Kartini Jepara, Jumat 07:00 WIB",
whatsIncluded: `• Tiket kapal feri PP JeparaKarimun
• Homestay 2 malam (twin sharing)
• Boat hopping 2 hari
• Alat snorkel
• Makan 6x`,
whatsExcluded: `• Transport ke Jepara
• Tiket pesawat`,
destination: "Kepulauan Karimun Jawa",
location: "Jepara, Jawa Tengah",
date: utc(2026, 6, 12, 0, 0),
endDate: utc(2026, 6, 14, 18, 0),
maxParticipants: 12,
price: 1450000,
status: "OPEN",
organizerId: panji.id,
images: {
create: [
{ url: img("1507525428034-b723cf961d3e"), caption: "Pantai Karimun", order: 0 },
{ url: img("1519046904884-53103b34b206"), caption: "Boat hopping", order: 1 },
],
},
},
});
// --- CITY TRIP: Yogyakarta ---
const tripCityTrip = await prisma.trip.create({
data: {
category: "CITY_TRIP",
title: "City Trip Jogja Hidden Gems",
description: `Bukan Malioboro lagi. Trip jelajah Jogja sisi 'lokal' — Kotagede, Tamansari, Kalibiru, sampai angkringan andalan warga.
🚐 Mobil grup, bukan tour bus`,
meetingPoint: "Stasiun Tugu Yogyakarta, Sabtu 08:00 WIB",
whatsIncluded: `• Transport mobil grup 2 hari
• Tour leader lokal
• Makan 3x (kuliner lokal)
• Tiket masuk semua spot`,
whatsExcluded: `• Transport ke Jogja
• Penginapan (rekomendasi disediakan)`,
destination: "Yogyakarta",
location: "Yogyakarta",
date: utc(2026, 5, 22, 0, 0),
endDate: utc(2026, 5, 23, 20, 0),
maxParticipants: 8,
price: 650000,
status: "OPEN",
organizerId: fiersa.id,
images: {
create: [
{ url: img("1596402184320-417e7178b2cd"), caption: "Tamansari", order: 0 },
{ url: img("1583309217394-d191d747bc66"), caption: "Sudut Jogja", order: 1 },
],
},
},
});
// --- CULINARY: Bandung Street Food ---
const tripCulinary = await prisma.trip.create({
data: {
category: "CULINARY",
title: "Kulineran Street Food Bandung",
description: `Hopping 8 spot kuliner legend Bandung dalam satu hari: Surabi Enhaii, Mie Kocok Mang Dadeng, Lotek Kalipah Apo, sampai Es Cendol Elizabeth.
🍜 Cocok buat foodie & first-timer`,
meetingPoint: "Stasiun Bandung pintu utara, 09:00 WIB",
whatsIncluded: `• Transport angkot/grup
• Tour leader food explorer
• Sample setiap spot (8 tempat)`,
whatsExcluded: `• Pembelian extra di luar sample`,
destination: "Street Food Tour Bandung",
location: "Bandung, Jawa Barat",
date: utc(2026, 5, 17, 0, 0),
endDate: null,
maxParticipants: 8,
price: 175000,
status: "OPEN",
organizerId: dede.id,
images: {
create: [
{ url: img("1565299624946-b28f40a0ae38"), caption: "Street food", order: 0 },
],
},
},
});
// --- CONCERT: Coldplay Jakarta ---
const tripConcert = await prisma.trip.create({
data: {
category: "CONCERT",
title: "Nonton Coldplay Bareng — Music of the Spheres Jakarta",
description: `Cari teman buat nonton Coldplay tapi gak mau nonton sendirian? Gabung grup ini.
🎤 Tiket BUKAN termasuk — peserta bawa tiket masing-masing
🤝 Grup hanya untuk koordinasi meet-up & after-party`,
meetingPoint: "Plaza GBK, depan loket Cat 1, 17:00 WIB",
whatsIncluded: `• Koordinasi grup
• Foto bareng pre-show
• After-party dinner di Senayan`,
whatsExcluded: `• Tiket konser (bawa sendiri!)
• Transport ke GBK`,
destination: "Coldplay — Music of the Spheres",
location: "Stadion Utama GBK, Jakarta",
date: utc(2026, 6, 20, 10, 0),
endDate: null,
maxParticipants: 6,
price: 0,
status: "OPEN",
organizerId: fiersa.id,
images: {
create: [
{ url: img("1470229722913-7c0e2dbbafd3"), caption: "Konser malam", order: 0 },
],
},
},
});
// --- WORKSHOP: Fotografi Lanskap ---
const tripWorkshop = await prisma.trip.create({
data: {
category: "WORKSHOP",
title: "Workshop Fotografi Lanskap — Pangalengan",
description: `Belajar fotografi lanskap langsung di lapangan. Sunrise di kebun teh, golden hour di danau, milky way di malam hari (cuaca permitting).
📷 Bawa kamera DSLR/mirrorless + tripod
👨‍🏫 Mentor: fotografer pro (10+ tahun pengalaman)`,
meetingPoint: "Alun-alun Pangalengan, Sabtu 04:00 WIB",
whatsIncluded: `• Materi workshop (briefing + on-field)
• Tour leader & mentor
• Penginapan villa 1 malam
• Konsumsi 3x`,
whatsExcluded: `• Kamera & tripod (bawa sendiri)
• Transport ke Pangalengan`,
destination: "Fotografi Lanskap",
location: "Pangalengan, Bandung Selatan",
date: utc(2026, 6, 6, 0, 0),
endDate: utc(2026, 6, 7, 18, 0),
maxParticipants: 6,
price: 850000,
status: "OPEN",
organizerId: panji.id,
images: {
create: [
{ url: img("1452587925148-ce544e77e70d"), caption: "Sunrise kebun teh", order: 0 },
{ url: img("1444080748397-f442aa95c3e5"), caption: "Workshop on-field", order: 1 },
],
},
},
});
// --- RETREAT: Mindfulness Ubud ---
const tripRetreat = await prisma.trip.create({
data: {
category: "RETREAT",
title: "Mindfulness Retreat 3D — Ubud",
description: `Retreat 3 hari di tengah sawah Ubud. Yoga pagi, meditasi guided, journaling, dan sound healing.
🧘 Untuk yang lagi burnout & butuh reset
👥 Grup kecil (max 8) — pengalaman akrab`,
meetingPoint: "Villa Sawah Ubud (alamat dikirim H-3 via WA)",
whatsIncluded: `• Penginapan villa 2 malam
• Yoga 4 sesi + meditasi 6 sesi
• Sound healing (1 sesi)
• Konsumsi vegan 6x
• Welcome kit (jurnal, herbal tea)`,
whatsExcluded: `• Transport ke Ubud
• Treatment spa opsional`,
destination: "Mindfulness Retreat Ubud",
location: "Ubud, Gianyar, Bali",
date: utc(2026, 6, 26, 0, 0),
endDate: utc(2026, 6, 28, 14, 0),
maxParticipants: 8,
price: 2400000,
status: "OPEN",
organizerId: fiersa.id,
images: {
create: [
{ url: img("1545389336-cf090694435e"), caption: "Yoga di sawah Ubud", order: 0 },
{ url: img("1518609878373-06d740f60d8b"), caption: "Villa retreat", order: 1 },
],
},
},
});
console.log("✅ 11 Trips dibuat (10 kategori, semua tanggal masa depan)\n");
// ==================== PARTICIPANTS ====================
await prisma.tripParticipant.createMany({
data: [
// Hiking Papandayan — 4 peserta
{ tripId: tripHiking.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripHiking.id, userId: sari.id, status: "CONFIRMED" },
{ tripId: tripHiking.id, userId: doni.id, status: "CONFIRMED" },
{ tripId: tripHiking.id, userId: raka.id, status: "CONFIRMED" },
// Hiking Ciremai — 2 peserta
{ tripId: tripHikingCiremai.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripHikingCiremai.id, userId: maya.id, status: "CONFIRMED" },
// Camping — 5 peserta
{ tripId: tripCamping.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripCamping.id, userId: sari.id, status: "CONFIRMED" },
{ tripId: tripCamping.id, userId: doni.id, status: "CONFIRMED" },
{ tripId: tripCamping.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripCamping.id, userId: raka.id, status: "CONFIRMED" },
// Snorkeling — 3 peserta
{ tripId: tripSnorkel.id, userId: sari.id, status: "CONFIRMED" },
{ tripId: tripSnorkel.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripSnorkel.id, userId: raka.id, status: "PENDING" },
// Diving — 1 peserta (paid trip, mahal)
{ tripId: tripDiving.id, userId: doni.id, status: "PENDING" },
// Island hopping — 4 peserta
{ tripId: tripIslandHop.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripIslandHop.id, userId: sari.id, status: "CONFIRMED" },
{ tripId: tripIslandHop.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripIslandHop.id, userId: raka.id, status: "PENDING" },
// City trip — 3 peserta
{ tripId: tripCityTrip.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripCityTrip.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripCityTrip.id, userId: sari.id, status: "PENDING" },
// Culinary — 5 peserta
{ tripId: tripCulinary.id, userId: budi.id, status: "CONFIRMED" },
{ tripId: tripCulinary.id, userId: sari.id, status: "CONFIRMED" },
{ tripId: tripCulinary.id, userId: doni.id, status: "CONFIRMED" },
{ tripId: tripCulinary.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripCulinary.id, userId: raka.id, status: "CONFIRMED" },
// Concert — 2 peserta
{ tripId: tripConcert.id, userId: maya.id, status: "CONFIRMED" },
{ tripId: tripConcert.id, userId: raka.id, status: "CONFIRMED" },
// Workshop — 2 peserta
{ tripId: tripWorkshop.id, userId: doni.id, status: "CONFIRMED" },
{ tripId: tripWorkshop.id, userId: sari.id, status: "CONFIRMED" },
// Retreat — 1 peserta (mahal, niche)
{ tripId: tripRetreat.id, userId: maya.id, status: "PENDING" },
],
});
console.log("✅ Participants joined (mix CONFIRMED & PENDING per trip)\n");
console.log("🎉 Seed complete!");
}
main()
.catch((e) => {
console.error("❌ Seed failed:", e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});