auth, trips and join trips

This commit is contained in:
2026-04-16 14:51:54 +07:00
parent de0d1c5413
commit 237caad488
49 changed files with 11343 additions and 334 deletions
@@ -0,0 +1,63 @@
-- CreateEnum
CREATE TYPE "TripStatus" AS ENUM ('OPEN', 'FULL', 'CLOSED', 'COMPLETED');
-- CreateEnum
CREATE TYPE "ParticipantStatus" AS ENUM ('PENDING', 'CONFIRMED', 'CANCELLED');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"image" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Trip" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"mountain" TEXT NOT NULL,
"location" TEXT NOT NULL,
"date" TIMESTAMP(3) NOT NULL,
"maxParticipants" INTEGER NOT NULL,
"price" INTEGER NOT NULL,
"image" TEXT,
"status" "TripStatus" NOT NULL DEFAULT 'OPEN',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"organizerId" TEXT NOT NULL,
CONSTRAINT "Trip_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TripParticipant" (
"id" TEXT NOT NULL,
"status" "ParticipantStatus" NOT NULL DEFAULT 'PENDING',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"tripId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "TripParticipant_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "TripParticipant_tripId_userId_key" ON "TripParticipant"("tripId", "userId");
-- AddForeignKey
ALTER TABLE "Trip" ADD CONSTRAINT "Trip_organizerId_fkey" FOREIGN KEY ("organizerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TripParticipant" ADD CONSTRAINT "TripParticipant_tripId_fkey" FOREIGN KEY ("tripId") REFERENCES "Trip"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TripParticipant" ADD CONSTRAINT "TripParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
+68
View File
@@ -0,0 +1,68 @@
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model User {
id String @id @default(cuid())
name String
email String @unique
password String
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
trips Trip[]
participations TripParticipant[]
}
model Trip {
id String @id @default(cuid())
title String
description String?
mountain String
location String
date DateTime
maxParticipants Int
price Int
image String?
status TripStatus @default(OPEN)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizerId String
organizer User @relation(fields: [organizerId], references: [id])
participants TripParticipant[]
}
model TripParticipant {
id String @id @default(cuid())
status ParticipantStatus @default(PENDING)
createdAt DateTime @default(now())
tripId String
trip Trip @relation(fields: [tripId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
@@unique([tripId, userId])
}
enum TripStatus {
OPEN
FULL
CLOSED
COMPLETED
}
enum ParticipantStatus {
PENDING
CONFIRMED
CANCELLED
}
+282
View File
@@ -0,0 +1,282 @@
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");
// ==================== USERS ====================
const password = await bcrypt.hash("password123", 12);
// Organizer
const organizer1 = await prisma.user.upsert({
where: { email: "andi@setrip.id" },
update: {},
create: {
name: "Andi Pendaki",
email: "andi@setrip.id",
password,
},
});
const organizer2 = await prisma.user.upsert({
where: { email: "rina@setrip.id" },
update: {},
create: {
name: "Rina Explorer",
email: "rina@setrip.id",
password,
},
});
// User biasa (join trip)
const user1 = await prisma.user.upsert({
where: { email: "budi@gmail.com" },
update: {},
create: {
name: "Budi Santoso",
email: "budi@gmail.com",
password,
},
});
const user2 = await prisma.user.upsert({
where: { email: "sari@gmail.com" },
update: {},
create: {
name: "Sari Dewi",
email: "sari@gmail.com",
password,
},
});
const user3 = await prisma.user.upsert({
where: { email: "doni@gmail.com" },
update: {},
create: {
name: "Doni Prasetyo",
email: "doni@gmail.com",
password,
},
});
const user4 = await prisma.user.upsert({
where: { email: "maya@gmail.com" },
update: {},
create: {
name: "Maya Putri",
email: "maya@gmail.com",
password,
},
});
console.log("✅ Users created");
console.log(" Organizer: andi@setrip.id, rina@setrip.id");
console.log(" Users: budi@gmail.com, sari@gmail.com, doni@gmail.com, maya@gmail.com");
console.log(" Password semua: password123\n");
// ==================== TRIPS ====================
const now = new Date();
const trip1 = await prisma.trip.create({
data: {
title: "Open Trip Papandayan Weekend",
description: `Pendakian santai ke Gunung Papandayan, cocok untuk pemula!
📍 Meeting Point: Alun-alun Garut, 05:00 WIB
🎒 Fasilitas: Transport PP, guide, tenda, makan 3x
⚠️ Bawa: Sleeping bag, jaket, headlamp, air 2L
Itinerary:
- Sabtu: Berangkat → Basecamp → Summit → Camp
- Minggu: Sunrise → Turun → Pulang`,
mountain: "Gunung Papandayan",
location: "Garut, Jawa Barat",
date: new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000), // 3 hari lagi
maxParticipants: 10,
price: 250000,
status: "OPEN",
organizerId: organizer1.id,
},
});
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: new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000), // 5 hari lagi
maxParticipants: 8,
price: 350000,
status: "OPEN",
organizerId: organizer1.id,
},
});
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: new Date(now.getTime() + 6 * 24 * 60 * 60 * 1000), // 6 hari lagi
maxParticipants: 12,
price: 280000,
status: "OPEN",
organizerId: organizer2.id,
},
});
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: new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000), // 2 hari lagi
maxParticipants: 15,
price: 120000,
status: "OPEN",
organizerId: organizer2.id,
},
});
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: new Date(now.getTime() + 4 * 24 * 60 * 60 * 1000), // 4 hari lagi
maxParticipants: 10,
price: 150000,
status: "OPEN",
organizerId: organizer1.id,
},
});
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: new Date(now.getTime() + 10 * 24 * 60 * 60 * 1000), // 10 hari lagi
maxParticipants: 8,
price: 300000,
status: "OPEN",
organizerId: organizer2.id,
},
});
console.log("✅ 6 Trips created\n");
// ==================== PARTICIPANTS ====================
// Trip 1 (Papandayan) — 3 peserta
await prisma.tripParticipant.createMany({
data: [
{ tripId: trip1.id, userId: user1.id, status: "CONFIRMED" },
{ tripId: trip1.id, userId: user2.id, status: "CONFIRMED" },
{ tripId: trip1.id, userId: user3.id, status: "CONFIRMED" },
],
});
// Trip 2 (Ciremai) — 2 peserta
await prisma.tripParticipant.createMany({
data: [
{ tripId: trip2.id, userId: user1.id, status: "CONFIRMED" },
{ tripId: trip2.id, userId: user4.id, status: "CONFIRMED" },
],
});
// Trip 3 (Gede) — 4 peserta
await prisma.tripParticipant.createMany({
data: [
{ tripId: trip3.id, userId: user1.id, status: "CONFIRMED" },
{ tripId: trip3.id, userId: user2.id, status: "CONFIRMED" },
{ tripId: trip3.id, userId: user3.id, status: "CONFIRMED" },
{ tripId: trip3.id, userId: user4.id, status: "CONFIRMED" },
],
});
// Trip 4 (Tangkuban Parahu) — 5 peserta
await prisma.tripParticipant.createMany({
data: [
{ tripId: trip4.id, userId: user1.id, status: "CONFIRMED" },
{ tripId: trip4.id, userId: user2.id, status: "CONFIRMED" },
{ tripId: trip4.id, userId: user3.id, status: "CONFIRMED" },
{ tripId: trip4.id, userId: user4.id, status: "CONFIRMED" },
{ tripId: trip4.id, userId: organizer1.id, status: "CONFIRMED" },
],
});
// Trip 5 (Malabar) — 1 peserta
await prisma.tripParticipant.createMany({
data: [
{ tripId: trip5.id, userId: user2.id, status: "CONFIRMED" },
],
});
// Trip 6 (Guntur) — belum ada peserta
console.log("✅ Participants joined trips");
console.log(" Papandayan: 3/10 peserta");
console.log(" Ciremai: 2/8 peserta");
console.log(" Gede: 4/12 peserta");
console.log(" Tangkuban Parahu: 5/15 peserta");
console.log(" Malabar: 1/10 peserta");
console.log(" Guntur: 0/8 peserta\n");
console.log("🎉 Seed complete!");
}
main()
.catch((e) => {
console.error("❌ Seed failed:", e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});