kyc user and upload partial update encrypt nik and picture
This commit is contained in:
+8
-1
@@ -230,10 +230,17 @@ Alur data mengikuti pola yang sama: **UI (`app/`) → server actions (`features/
|
|||||||
|
|
||||||
### Trust & organizer (`server/services/trust.service.ts`)
|
### Trust & organizer (`server/services/trust.service.ts`)
|
||||||
|
|
||||||
- **Verified:** kolom `User.isVerified` (default false; set manual / seed / admin ke depan).
|
- **Verified Organizer:** dihitung dari `OrganizerVerification.status === "APPROVED"` (lihat `lib/trust.ts → isVerifiedOrganizer()`). Tidak lagi pakai `User.isVerified`.
|
||||||
- **Trip leader:** heuristik `jumlah trip dibuat ≥ TRIP_LEADER_MIN_TRIPS` (`lib/trust.ts`).
|
- **Trip leader:** heuristik `jumlah trip dibuat ≥ TRIP_LEADER_MIN_TRIPS` (`lib/trust.ts`).
|
||||||
- **Jumlah trip dibuat & rating organizer:** dihitung agregat dari DB (rating = rata-rata `TripReview` pada semua trip sang organizer).
|
- **Jumlah trip dibuat & rating organizer:** dihitung agregat dari DB (rating = rata-rata `TripReview` pada semua trip sang organizer).
|
||||||
|
|
||||||
|
### Verifikasi organizer (KYC ringan)
|
||||||
|
|
||||||
|
- Model `OrganizerVerification` (1-1 ke `User`) menyimpan KTP (nama, NIK unik, tanggal lahir, alamat), URL foto KTP & selfie, data rekening bank, dan status `PENDING` / `APPROVED` / `REJECTED` + audit reviewer.
|
||||||
|
- Alur: user submit di `/verify` (`features/organizer/`) → admin review di `/admin/verifications` → setujui/tolak.
|
||||||
|
- **Gate trip berbayar:** `createTripAction` menolak `price > 0` jika user belum `APPROVED` (`organizerService.isApproved`).
|
||||||
|
- **Akses admin:** `lib/admin.ts → isAdminEmail()` membaca `ADMIN_EMAILS` (env, comma-separated).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 🧠 Final Principle
|
# 🧠 Final Principle
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
# 🔒 Privacy Policy (Kebijakan Privasi) SeTrip
|
||||||
|
|
||||||
|
**Terakhir diperbarui: 2026-04-27**
|
||||||
|
|
||||||
|
SeTrip menghargai privasi Anda. Kebijakan Privasi ini menjelaskan bagaimana kami mengumpulkan, menggunakan, dan melindungi informasi Anda saat menggunakan platform SeTrip.
|
||||||
|
|
||||||
|
Dengan menggunakan SeTrip, Anda menyetujui praktik yang dijelaskan dalam Kebijakan Privasi ini.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Informasi yang Kami Kumpulkan
|
||||||
|
|
||||||
|
Kami dapat mengumpulkan informasi berikut:
|
||||||
|
|
||||||
|
## a. Informasi Akun
|
||||||
|
|
||||||
|
- Nama
|
||||||
|
- Email
|
||||||
|
- Nomor telepon
|
||||||
|
- Password (disimpan dalam bentuk terenkripsi)
|
||||||
|
|
||||||
|
## b. Informasi Profil
|
||||||
|
|
||||||
|
- Foto profil
|
||||||
|
- Deskripsi diri
|
||||||
|
- Riwayat trip
|
||||||
|
|
||||||
|
## c. Informasi Transaksi
|
||||||
|
|
||||||
|
- Data booking trip
|
||||||
|
- Status pembayaran
|
||||||
|
- Riwayat aktivitas
|
||||||
|
|
||||||
|
## d. Informasi Teknis
|
||||||
|
|
||||||
|
- Alamat IP
|
||||||
|
- Browser
|
||||||
|
- Perangkat yang digunakan
|
||||||
|
- Log aktivitas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Cara Kami Menggunakan Informasi
|
||||||
|
|
||||||
|
Kami menggunakan informasi Anda untuk:
|
||||||
|
|
||||||
|
- Membuat dan mengelola akun
|
||||||
|
- Menghubungkan pengguna dengan organizer
|
||||||
|
- Memproses booking dan aktivitas trip
|
||||||
|
- Meningkatkan layanan dan pengalaman pengguna
|
||||||
|
- Mengirim notifikasi terkait aktivitas
|
||||||
|
- Mencegah penipuan dan penyalahgunaan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Pembagian Informasi
|
||||||
|
|
||||||
|
Kami tidak menjual data pribadi Anda.
|
||||||
|
|
||||||
|
Namun, kami dapat membagikan informasi dalam kondisi berikut:
|
||||||
|
|
||||||
|
## a. Dengan Organizer
|
||||||
|
|
||||||
|
- Informasi dasar seperti nama dan kontak dapat dibagikan kepada organizer untuk keperluan trip
|
||||||
|
|
||||||
|
## b. Dengan Penyedia Layanan
|
||||||
|
|
||||||
|
- Untuk kebutuhan teknis (hosting, analytics, dll)
|
||||||
|
|
||||||
|
## c. Kewajiban Hukum
|
||||||
|
|
||||||
|
- Jika diminta oleh hukum atau otoritas berwenang
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Keamanan Data
|
||||||
|
|
||||||
|
Kami berusaha melindungi data Anda dengan:
|
||||||
|
|
||||||
|
- Enkripsi password
|
||||||
|
- Pembatasan akses data
|
||||||
|
- Sistem keamanan standar industri
|
||||||
|
|
||||||
|
Namun, tidak ada sistem yang 100% aman.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Penyimpanan Data
|
||||||
|
|
||||||
|
Kami menyimpan data Anda selama:
|
||||||
|
|
||||||
|
- Akun Anda aktif
|
||||||
|
- Dibutuhkan untuk keperluan layanan
|
||||||
|
|
||||||
|
Data dapat dihapus atas permintaan pengguna, kecuali diwajibkan oleh hukum untuk disimpan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Hak Pengguna
|
||||||
|
|
||||||
|
Anda memiliki hak untuk:
|
||||||
|
|
||||||
|
- Mengakses data pribadi Anda
|
||||||
|
- Memperbarui informasi
|
||||||
|
- Menghapus akun
|
||||||
|
- Menarik persetujuan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Cookie & Tracking
|
||||||
|
|
||||||
|
SeTrip dapat menggunakan:
|
||||||
|
|
||||||
|
- Cookie
|
||||||
|
- Teknologi pelacakan sederhana
|
||||||
|
|
||||||
|
Untuk:
|
||||||
|
|
||||||
|
- Menyimpan sesi login
|
||||||
|
- Meningkatkan pengalaman pengguna
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Layanan Pihak Ketiga
|
||||||
|
|
||||||
|
SeTrip dapat menggunakan layanan pihak ketiga seperti:
|
||||||
|
|
||||||
|
- Hosting
|
||||||
|
- Analytics
|
||||||
|
- Payment gateway (di masa depan)
|
||||||
|
|
||||||
|
Kami tidak bertanggung jawab atas kebijakan privasi pihak ketiga tersebut.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Perlindungan terhadap Penipuan
|
||||||
|
|
||||||
|
Kami dapat menggunakan data untuk:
|
||||||
|
|
||||||
|
- Mendeteksi aktivitas mencurigakan
|
||||||
|
- Mencegah penipuan
|
||||||
|
- Melindungi pengguna lain
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Perubahan Kebijakan Privasi
|
||||||
|
|
||||||
|
SeTrip dapat memperbarui Kebijakan Privasi ini sewaktu-waktu.
|
||||||
|
|
||||||
|
Pengguna disarankan untuk:
|
||||||
|
|
||||||
|
- Membaca secara berkala
|
||||||
|
- Memahami perubahan yang berlaku
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Kontak
|
||||||
|
|
||||||
|
Jika Anda memiliki pertanyaan mengenai Kebijakan Privasi ini, silakan hubungi:
|
||||||
|
|
||||||
|
Email: [support@setrip.com](mailto:support@setrip.com)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ✅ Persetujuan
|
||||||
|
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
|
||||||
|
- Telah membaca
|
||||||
|
- Memahami
|
||||||
|
- Menyetujui Kebijakan Privasi ini
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**End of Document**
|
||||||
@@ -23,6 +23,8 @@ Tanpa login, pengguna tetap bisa melihat daftar trip dan detail trip, tetapi tid
|
|||||||
|
|
||||||
Organizer **tidak** bisa join trip sendiri; di detail trip ditampilkan bahwa dia adalah organizer trip ini.
|
Organizer **tidak** bisa join trip sendiri; di detail trip ditampilkan bahwa dia adalah organizer trip ini.
|
||||||
|
|
||||||
|
**Verifikasi organizer (untuk trip berbayar).** Trip dengan harga > 0 hanya bisa dibuat oleh user yang sudah mengirim KTP, selfie, dan data rekening di `/verify` lalu disetujui admin di `/admin/verifications`. Trip gratis tidak butuh verifikasi. Organizer yang sudah disetujui tampil dengan badge **✅ Verified Organizer** di halaman detail trip.
|
||||||
|
|
||||||
### 3. Peserta: mencari trip
|
### 3. Peserta: mencari trip
|
||||||
|
|
||||||
1. **Beranda** (`/`) dan **Open Trip** (`/trips`) menampilkan trip **OPEN** dengan tanggal berangkat yang masih relevan (`tripService.getOpenTrips`).
|
1. **Beranda** (`/`) dan **Open Trip** (`/trips`) menampilkan trip **OPEN** dengan tanggal berangkat yang masih relevan (`tripService.getOpenTrips`).
|
||||||
@@ -95,7 +97,8 @@ Alur ini menggambarkan satu peserta dari pertama kali mendaftar sampai pembayara
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| Trip | `Trip`: judul, gunung, lokasi, tanggal, kuota, harga, status trip (`OPEN` / `FULL` / …), meeting point, itinerary, termasuk/tidak termasuk, relasi ke organizer |
|
| Trip | `Trip`: judul, gunung, lokasi, tanggal, kuota, harga, status trip (`OPEN` / `FULL` / …), meeting point, itinerary, termasuk/tidak termasuk, relasi ke organizer |
|
||||||
| Peserta | `TripParticipant` unik per `(tripId, userId)`: status **`PENDING`** / **`CONFIRMED`** / **`CANCELLED`**, serta **`markedPaidAt`** & **`paymentConfirmedAt`** untuk alur bayar manual |
|
| Peserta | `TripParticipant` unik per `(tripId, userId)`: status **`PENDING`** / **`CONFIRMED`** / **`CANCELLED`**, serta **`markedPaidAt`** & **`paymentConfirmedAt`** untuk alur bayar manual |
|
||||||
| Organizer (kepercayaan) | `User.isVerified`; agregat rating & jumlah trip dibuat dihitung dari data ulasan & trip |
|
| Organizer (kepercayaan) | `OrganizerVerification` (1-1 ke `User`) berisi KTP, selfie, rekening, dan status (`PENDING` / `APPROVED` / `REJECTED`); badge **Verified Organizer** muncul ketika `status === "APPROVED"` (helper `lib/trust.ts → isVerifiedOrganizer`). Agregat rating & jumlah trip dihitung dari ulasan & trip. |
|
||||||
|
| Persetujuan T&C / Privasi | `User.acceptedTermsAndPrivacy` + `User.acceptedAt`, dicentang saat registrasi (link ke `/terms` & `/privacy`). |
|
||||||
|
|
||||||
## Menjalankan secara lokal
|
## Menjalankan secara lokal
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# 📜 Terms & Conditions (Syarat & Ketentuan) SeTrip
|
||||||
|
|
||||||
|
**Terakhir diperbarui: 2026-04-27**
|
||||||
|
|
||||||
|
Selamat datang di SeTrip. Dengan mengakses atau menggunakan platform SeTrip, Anda menyetujui untuk terikat oleh Syarat & Ketentuan berikut.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Definisi
|
||||||
|
|
||||||
|
Dalam dokumen ini:
|
||||||
|
|
||||||
|
- **SeTrip**: Platform yang menghubungkan pengguna dengan penyelenggara trip.
|
||||||
|
- **Pengguna (User)**: Individu yang menggunakan aplikasi SeTrip.
|
||||||
|
- **Organizer (Penyelenggara)**: Pengguna yang membuat dan mengelola trip.
|
||||||
|
- **Trip**: Kegiatan perjalanan yang dibuat oleh organizer.
|
||||||
|
- **Platform**: Website atau aplikasi SeTrip.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Peran SeTrip
|
||||||
|
|
||||||
|
SeTrip bertindak sebagai **platform perantara** yang menghubungkan pengguna dan organizer.
|
||||||
|
|
||||||
|
SeTrip:
|
||||||
|
|
||||||
|
- **Bukan penyelenggara trip**
|
||||||
|
- **Tidak terlibat langsung dalam pelaksanaan perjalanan**
|
||||||
|
- **Tidak bertanggung jawab atas kegiatan selama trip berlangsung**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Penggunaan Platform
|
||||||
|
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
|
||||||
|
- Berusia minimal 18 tahun atau memiliki izin dari wali
|
||||||
|
- Memberikan informasi yang benar dan akurat
|
||||||
|
- Tidak menggunakan platform untuk:
|
||||||
|
- Penipuan
|
||||||
|
- Aktivitas ilegal
|
||||||
|
- Penyebaran informasi palsu
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Akun Pengguna
|
||||||
|
|
||||||
|
- Pengguna bertanggung jawab atas keamanan akun masing-masing
|
||||||
|
- Dilarang membagikan akun kepada pihak lain
|
||||||
|
- SeTrip berhak menangguhkan atau menghapus akun jika terjadi pelanggaran
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Trip & Booking
|
||||||
|
|
||||||
|
- Organizer bertanggung jawab atas seluruh informasi trip
|
||||||
|
- Pengguna wajib membaca detail trip sebelum melakukan join
|
||||||
|
- Dengan melakukan join trip, pengguna menyetujui seluruh ketentuan trip yang dibuat oleh organizer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Pembayaran
|
||||||
|
|
||||||
|
- Pembayaran dilakukan sesuai metode yang tersedia di platform
|
||||||
|
- Dalam fase awal, pembayaran dapat dilakukan langsung kepada organizer
|
||||||
|
- SeTrip tidak menjamin keamanan transaksi yang dilakukan di luar platform
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Pembatalan & Refund
|
||||||
|
|
||||||
|
- Kebijakan pembatalan ditentukan oleh organizer
|
||||||
|
- SeTrip tidak bertanggung jawab atas refund yang tidak diberikan oleh organizer
|
||||||
|
- Pengguna disarankan untuk memahami kebijakan sebelum melakukan pembayaran
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Tanggung Jawab Organizer
|
||||||
|
|
||||||
|
Organizer wajib:
|
||||||
|
|
||||||
|
- Memberikan informasi trip yang jelas dan akurat
|
||||||
|
- Menjalankan trip sesuai deskripsi
|
||||||
|
- Bertanggung jawab atas keselamatan peserta selama trip
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Risiko Perjalanan
|
||||||
|
|
||||||
|
Pengguna memahami bahwa aktivitas perjalanan, terutama kegiatan outdoor, memiliki risiko termasuk namun tidak terbatas pada:
|
||||||
|
|
||||||
|
- Cedera
|
||||||
|
- Kecelakaan
|
||||||
|
- Cuaca ekstrem
|
||||||
|
- Kondisi tak terduga lainnya
|
||||||
|
|
||||||
|
Dengan mengikuti trip, pengguna menyatakan:
|
||||||
|
|
||||||
|
> Mengikuti kegiatan secara sadar dan bertanggung jawab atas risiko pribadi
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Batasan Tanggung Jawab
|
||||||
|
|
||||||
|
SeTrip tidak bertanggung jawab atas:
|
||||||
|
|
||||||
|
- Kerugian finansial
|
||||||
|
- Cedera atau kecelakaan
|
||||||
|
- Kegagalan pelaksanaan trip
|
||||||
|
- Tindakan organizer atau pengguna lain
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Larangan Transaksi di Luar Platform
|
||||||
|
|
||||||
|
Pengguna disarankan untuk tidak melakukan transaksi di luar platform.
|
||||||
|
|
||||||
|
SeTrip tidak bertanggung jawab atas:
|
||||||
|
|
||||||
|
- Penipuan
|
||||||
|
- Kerugian
|
||||||
|
- Masalah lain yang terjadi akibat transaksi di luar sistem SeTrip
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. Sistem Review
|
||||||
|
|
||||||
|
- Pengguna dapat memberikan review setelah trip
|
||||||
|
- Review harus jujur dan tidak mengandung unsur fitnah
|
||||||
|
- SeTrip berhak menghapus review yang melanggar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 13. Penangguhan & Penghentian Akun
|
||||||
|
|
||||||
|
SeTrip berhak untuk:
|
||||||
|
|
||||||
|
- Menangguhkan akun
|
||||||
|
- Menghapus akun
|
||||||
|
- Membatasi akses
|
||||||
|
|
||||||
|
Jika pengguna:
|
||||||
|
|
||||||
|
- Melanggar ketentuan
|
||||||
|
- Terindikasi melakukan penipuan
|
||||||
|
- Menyalahgunakan platform
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 14. Perubahan Layanan
|
||||||
|
|
||||||
|
SeTrip dapat:
|
||||||
|
|
||||||
|
- Mengubah fitur
|
||||||
|
- Menghentikan layanan
|
||||||
|
- Menambahkan kebijakan baru
|
||||||
|
|
||||||
|
Tanpa pemberitahuan sebelumnya
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 15. Perubahan Syarat & Ketentuan
|
||||||
|
|
||||||
|
SeTrip dapat memperbarui Syarat & Ketentuan ini kapan saja.
|
||||||
|
|
||||||
|
Pengguna disarankan untuk:
|
||||||
|
|
||||||
|
- Membaca secara berkala
|
||||||
|
- Memahami perubahan yang berlaku
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16. Hukum yang Berlaku
|
||||||
|
|
||||||
|
Syarat & Ketentuan ini diatur oleh hukum yang berlaku di Republik Indonesia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 17. Kontak
|
||||||
|
|
||||||
|
Jika Anda memiliki pertanyaan, silakan hubungi:
|
||||||
|
|
||||||
|
Email: [support@setrip.com](mailto:support@setrip.com)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ✅ Persetujuan
|
||||||
|
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
|
||||||
|
- Telah membaca
|
||||||
|
- Memahami
|
||||||
|
- Menyetujui seluruh isi Syarat & Ketentuan ini
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**End of Document**
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Admin · Review Verifikasi Organizer",
|
||||||
|
description: "Halaman admin untuk meninjau pengajuan verifikasi organizer.",
|
||||||
|
alternates: { canonical: "/admin/verifications" },
|
||||||
|
robots: { index: false, follow: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AdminVerificationsLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { getServerSession } from "next-auth";
|
||||||
|
import { authOptions } from "@/lib/auth";
|
||||||
|
import { isAdminEmail } from "@/lib/admin";
|
||||||
|
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||||
|
import { ReviewCard } from "@/features/organizer/components/review-card";
|
||||||
|
|
||||||
|
type Tab = "PENDING" | "APPROVED" | "REJECTED";
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
searchParams: Promise<{ tab?: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function AdminVerificationsPage({ searchParams }: PageProps) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user) redirect("/login?callbackUrl=/admin/verifications");
|
||||||
|
if (!isAdminEmail(session.user.email)) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl px-4 py-12 text-center">
|
||||||
|
<p className="text-sm text-neutral-600">
|
||||||
|
Halaman ini hanya untuk admin SeTrip.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = await searchParams;
|
||||||
|
const tab: Tab =
|
||||||
|
params.tab === "APPROVED" || params.tab === "REJECTED" ? params.tab : "PENDING";
|
||||||
|
|
||||||
|
const items = await organizerRepo.listByStatus(tab);
|
||||||
|
|
||||||
|
const tabs: { key: Tab; label: string }[] = [
|
||||||
|
{ key: "PENDING", label: "Pending" },
|
||||||
|
{ key: "APPROVED", label: "Disetujui" },
|
||||||
|
{ key: "REJECTED", label: "Ditolak" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-4xl px-4 py-8 sm:py-12">
|
||||||
|
<header className="mb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||||
|
Review Verifikasi Organizer
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-sm text-neutral-500">
|
||||||
|
Periksa data KTP, selfie, dan rekening sebelum menyetujui.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="mb-6 flex gap-2">
|
||||||
|
{tabs.map((t) => (
|
||||||
|
<a
|
||||||
|
key={t.key}
|
||||||
|
href={`/admin/verifications?tab=${t.key}`}
|
||||||
|
className={`rounded-full px-4 py-1.5 text-sm font-semibold transition-colors ${
|
||||||
|
tab === t.key
|
||||||
|
? "bg-primary-600 text-white"
|
||||||
|
: "bg-neutral-100 text-neutral-600 hover:bg-neutral-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{items.length === 0 ? (
|
||||||
|
<div className="rounded-2xl border border-dashed border-neutral-300 bg-white p-10 text-center">
|
||||||
|
<p className="text-sm text-neutral-500">Tidak ada data.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{items.map((v) => (
|
||||||
|
<ReviewCard key={v.id} verification={v} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+54
-337
@@ -1,44 +1,11 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useSession } from "next-auth/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import DatePicker from "react-datepicker";
|
import { getServerSession } from "next-auth";
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
import { authOptions } from "@/lib/auth";
|
||||||
import { createTripAction } from "@/features/trip/actions";
|
import { organizerService } from "@/server/services/organizer.service";
|
||||||
import { ImageUrlInput } from "@/features/trip/components/image-url-input";
|
import { CreateTripForm } from "@/features/trip/components/create-trip-form";
|
||||||
import { formatLocalCalendarYmd } from "@/lib/trip-dates";
|
|
||||||
|
|
||||||
const SAMPLE_MOUNTAINS = [
|
export default async function CreateTripPage() {
|
||||||
{ name: "Gunung Papandayan", location: "Garut, Jawa Barat" },
|
const session = await getServerSession(authOptions);
|
||||||
{ name: "Gunung Ciremai", location: "Kuningan, Jawa Barat" },
|
|
||||||
{ name: "Gunung Pangrango", location: "Bogor/Cianjur, Jawa Barat" },
|
|
||||||
{ name: "Gunung Gede", location: "Bogor/Cianjur, Jawa Barat" },
|
|
||||||
{ name: "Gunung Tangkuban Parahu", location: "Bandung, Jawa Barat" },
|
|
||||||
{ name: "Gunung Bukit Tunggul", location: "Bandung, Jawa Barat" },
|
|
||||||
{ name: "Gunung Malabar", location: "Bandung, Jawa Barat" },
|
|
||||||
{ name: "Gunung Guntur", location: "Garut, Jawa Barat" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function formatRupiahInput(value: string): string {
|
|
||||||
const num = value.replace(/\D/g, "");
|
|
||||||
return num.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseRupiahInput(value: string): string {
|
|
||||||
return value.replace(/\./g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CreateTripPage() {
|
|
||||||
const { data: session } = useSession();
|
|
||||||
const router = useRouter();
|
|
||||||
const [error, setError] = useState("");
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState<Date | null>(null);
|
|
||||||
const [endDate, setEndDate] = useState<Date | null>(null);
|
|
||||||
const [priceDisplay, setPriceDisplay] = useState("");
|
|
||||||
|
|
||||||
if (!session?.user) {
|
if (!session?.user) {
|
||||||
return (
|
return (
|
||||||
@@ -51,7 +18,7 @@ export default function CreateTripPage() {
|
|||||||
Kamu harus login untuk membuat trip.
|
Kamu harus login untuk membuat trip.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
href="/login?callbackUrl=/create-trip"
|
||||||
className="inline-block rounded-xl bg-primary-600 px-6 py-2.5 text-sm font-semibold text-white hover:bg-primary-700"
|
className="inline-block rounded-xl bg-primary-600 px-6 py-2.5 text-sm font-semibold text-white hover:bg-primary-700"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
@@ -61,69 +28,8 @@ export default function CreateTripPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
const verification = await organizerService.getStatusForUser(session.user.id);
|
||||||
e.preventDefault();
|
const isVerifiedOrganizer = verification?.status === "APPROVED";
|
||||||
setError("");
|
|
||||||
|
|
||||||
if (!startDate) {
|
|
||||||
setError("Tanggal berangkat harus diisi");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
const formData = new FormData(e.currentTarget);
|
|
||||||
// Tanggal dari picker → string tanggal untuk server action
|
|
||||||
formData.set("date", formatLocalCalendarYmd(startDate));
|
|
||||||
if (endDate) {
|
|
||||||
const startYmd = formatLocalCalendarYmd(startDate);
|
|
||||||
const endYmd = formatLocalCalendarYmd(endDate);
|
|
||||||
// Satu hari: tanggal pulang sama dengan berangkat → jangan kirim endDate (trip 1 hari)
|
|
||||||
if (endYmd !== startYmd) {
|
|
||||||
formData.set("endDate", endYmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Set raw price number
|
|
||||||
formData.set("price", parseRupiahInput(priceDisplay));
|
|
||||||
|
|
||||||
const result = await createTripAction(formData);
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
setError(result.error);
|
|
||||||
} else if (result.tripId) {
|
|
||||||
router.push(`/trips/${result.tripId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMountainSelect(e: React.ChangeEvent<HTMLSelectElement>) {
|
|
||||||
const selected = SAMPLE_MOUNTAINS.find((m) => m.name === e.target.value);
|
|
||||||
if (selected) {
|
|
||||||
const form = e.target.form;
|
|
||||||
if (form) {
|
|
||||||
const mountainInput = form.elements.namedItem(
|
|
||||||
"mountain"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
const locationInput = form.elements.namedItem(
|
|
||||||
"location"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
mountainInput.value = selected.name;
|
|
||||||
locationInput.value = selected.location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDateChange(dates: [Date | null, Date | null]) {
|
|
||||||
const [start, end] = dates;
|
|
||||||
setStartDate(start);
|
|
||||||
setEndDate(end);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePriceChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const raw = e.target.value.replace(/\D/g, "");
|
|
||||||
setPriceDisplay(raw ? formatRupiahInput(raw) : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-2xl px-4 py-6 sm:py-8">
|
<div className="mx-auto max-w-2xl px-4 py-6 sm:py-8">
|
||||||
@@ -134,243 +40,54 @@ export default function CreateTripPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm">
|
{!isVerifiedOrganizer && (
|
||||||
{error && (
|
<VerificationBanner status={verification?.status ?? null} />
|
||||||
<div className="mb-4 rounded-xl bg-red-50 px-4 py-3 text-sm font-medium text-red-600">
|
)}
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<CreateTripForm isVerifiedOrganizer={isVerifiedOrganizer} />
|
||||||
{/* Mountain Quick Picker */}
|
</div>
|
||||||
<div className="rounded-xl bg-primary-50 p-4">
|
);
|
||||||
<label className="mb-2 flex items-center gap-1.5 text-sm font-bold text-primary-800">
|
}
|
||||||
<span>🏔️</span> Pilih Gunung Jawa Barat
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
onChange={handleMountainSelect}
|
|
||||||
className="w-full rounded-lg border border-primary-200 bg-white px-4 py-2.5 text-sm text-neutral-800"
|
|
||||||
defaultValue=""
|
|
||||||
>
|
|
||||||
<option value="" disabled>
|
|
||||||
Pilih gunung...
|
|
||||||
</option>
|
|
||||||
{SAMPLE_MOUNTAINS.map((m) => (
|
|
||||||
<option key={m.name} value={m.name}>
|
|
||||||
{m.name} — {m.location}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
function VerificationBanner({
|
||||||
<label htmlFor="title" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
status,
|
||||||
Judul Trip
|
}: {
|
||||||
</label>
|
status: "PENDING" | "APPROVED" | "REJECTED" | null;
|
||||||
<input
|
}) {
|
||||||
id="title"
|
if (status === "PENDING") {
|
||||||
name="title"
|
return (
|
||||||
type="text"
|
<div className="mb-5 rounded-2xl border border-amber-200 bg-amber-50 p-4 sm:p-5">
|
||||||
required
|
<p className="text-sm font-bold text-amber-800">
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
⏳ Verifikasi sedang diproses
|
||||||
placeholder="contoh: Open Trip Papandayan Weekend"
|
</p>
|
||||||
/>
|
<p className="mt-1 text-sm text-neutral-700">
|
||||||
</div>
|
Pengajuan verifikasi-mu masih ditinjau admin. Sementara menunggu, kamu
|
||||||
|
masih bisa membuat <strong>trip gratis</strong> (harga 0).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
const isRejected = status === "REJECTED";
|
||||||
<div>
|
return (
|
||||||
<label htmlFor="mountain" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
<div className="mb-5 rounded-2xl border border-amber-200 bg-amber-50 p-4 sm:p-5">
|
||||||
Nama Gunung
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
</label>
|
<div className="flex-1">
|
||||||
<input
|
<p className="text-sm font-bold text-amber-800">
|
||||||
id="mountain"
|
⚠️ {isRejected ? "Verifikasi ditolak" : "Belum terverifikasi"}
|
||||||
name="mountain"
|
</p>
|
||||||
type="text"
|
<p className="mt-1 text-sm text-neutral-700">
|
||||||
required
|
{isRejected
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
? "Pengajuan sebelumnya ditolak. Untuk membuat trip berbayar, perbaiki data dan ajukan ulang."
|
||||||
placeholder="Gunung Papandayan"
|
: "Untuk membuat trip berbayar, akun kamu perlu diverifikasi (KTP, selfie, & rekening). Trip gratis tidak butuh verifikasi."}
|
||||||
/>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<Link
|
||||||
<label htmlFor="location" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
href="/verify"
|
||||||
Lokasi
|
className="inline-flex shrink-0 items-center justify-center rounded-xl bg-amber-600 px-4 py-2 text-sm font-bold text-white shadow-sm transition-colors hover:bg-amber-700 sm:px-5"
|
||||||
</label>
|
>
|
||||||
<input
|
{isRejected ? "Ajukan Ulang" : "Verifikasi Sekarang"}
|
||||||
id="location"
|
</Link>
|
||||||
name="location"
|
|
||||||
type="text"
|
|
||||||
required
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="Garut, Jawa Barat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="description" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Deskripsi
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
rows={4}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="Ringkasan trip, vibe, level kesulitan..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="meetingPoint" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Meeting point
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="meetingPoint"
|
|
||||||
name="meetingPoint"
|
|
||||||
type="text"
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="contoh: Alfamart Cicaheum, 05:00 WIB"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="itinerary" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Itinerary
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="itinerary"
|
|
||||||
name="itinerary"
|
|
||||||
rows={5}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder={"Hari 1: …\nHari 2: …"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="whatsIncluded" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Termasuk
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="whatsIncluded"
|
|
||||||
name="whatsIncluded"
|
|
||||||
rows={4}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="Transport, konsumsi, tenda, …"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="whatsExcluded" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Tidak termasuk
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="whatsExcluded"
|
|
||||||
name="whatsExcluded"
|
|
||||||
rows={4}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="Tiket masuk TN, sleeping bag, …"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ImageUrlInput />
|
|
||||||
|
|
||||||
{/* Date Range & Participants & Price */}
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
|
||||||
{/* Date Range Picker */}
|
|
||||||
<div>
|
|
||||||
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Tanggal berangkat — pulang
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute left-3 top-1/2 z-10 -translate-y-1/2 text-neutral-400">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
className="h-4 w-4"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<DatePicker
|
|
||||||
selectsRange
|
|
||||||
startDate={startDate}
|
|
||||||
endDate={endDate}
|
|
||||||
onChange={handleDateChange}
|
|
||||||
minDate={new Date()}
|
|
||||||
placeholderText="Pilih tanggal..."
|
|
||||||
dateFormat="dd MMM yyyy"
|
|
||||||
isClearable
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-9 pr-3 text-sm text-neutral-800 placeholder:text-neutral-400 focus:border-primary-500 focus:bg-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Max Participants */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="maxParticipants" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Maks Peserta
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-neutral-400">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
className="h-4 w-4"
|
|
||||||
>
|
|
||||||
<path d="M7 8a3 3 0 100-6 3 3 0 000 6zM14.5 9a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM1.615 16.428a1.224 1.224 0 01-.569-1.175 6.002 6.002 0 0111.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 017 18a9.953 9.953 0 01-5.385-1.572zM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 00-1.588-3.755 4.502 4.502 0 015.874 2.636.818.818 0 01-.36.98A7.465 7.465 0 0114.5 16z" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
id="maxParticipants"
|
|
||||||
name="maxParticipants"
|
|
||||||
type="number"
|
|
||||||
required
|
|
||||||
min={1}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-9 pr-4 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="10"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Price with Rp format */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="priceDisplay" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
|
||||||
Harga per Orang
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-sm font-semibold text-neutral-500">
|
|
||||||
Rp
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
id="priceDisplay"
|
|
||||||
type="text"
|
|
||||||
inputMode="numeric"
|
|
||||||
required
|
|
||||||
value={priceDisplay}
|
|
||||||
onChange={handlePriceChange}
|
|
||||||
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-10 pr-4 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
|
||||||
placeholder="150.000"
|
|
||||||
/>
|
|
||||||
{/* Hidden input for form submission */}
|
|
||||||
<input type="hidden" name="price" value={parseRupiahInput(priceDisplay)} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={loading}
|
|
||||||
className="w-full rounded-xl bg-primary-600 py-3 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700 disabled:opacity-50"
|
|
||||||
>
|
|
||||||
{loading ? "Membuat Trip..." : "Buat Trip"}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ export * from './enums';
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model OrganizerVerification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type OrganizerVerification = Prisma.OrganizerVerificationModel
|
||||||
/**
|
/**
|
||||||
* Model Trip
|
* Model Trip
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ export { Prisma }
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model OrganizerVerification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type OrganizerVerification = Prisma.OrganizerVerificationModel
|
||||||
/**
|
/**
|
||||||
* Model Trip
|
* Model Trip
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -49,6 +49,17 @@ export type BoolFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type DateTimeFilter<$PrismaModel = never> = {
|
export type DateTimeFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
@@ -109,6 +120,20 @@ export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
@@ -123,15 +148,21 @@ export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
export type EnumVerificationStatusFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
notIn?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel> | $Enums.VerificationStatus
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
}
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
export type EnumVerificationStatusWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedEnumVerificationStatusWithAggregatesFilter<$PrismaModel> | $Enums.VerificationStatus
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IntFilter<$PrismaModel = never> = {
|
export type IntFilter<$PrismaModel = never> = {
|
||||||
@@ -152,20 +183,6 @@ export type EnumTripStatusFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedEnumTripStatusFilter<$PrismaModel> | $Enums.TripStatus
|
not?: Prisma.NestedEnumTripStatusFilter<$PrismaModel> | $Enums.TripStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
@@ -242,6 +259,17 @@ export type NestedBoolFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
@@ -317,6 +345,20 @@ export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
@@ -331,15 +373,21 @@ export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
export type NestedEnumVerificationStatusFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
notIn?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel> | $Enums.VerificationStatus
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
}
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
export type NestedEnumVerificationStatusWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedEnumVerificationStatusWithAggregatesFilter<$PrismaModel> | $Enums.VerificationStatus
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumVerificationStatusFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedEnumTripStatusFilter<$PrismaModel = never> = {
|
export type NestedEnumTripStatusFilter<$PrismaModel = never> = {
|
||||||
@@ -349,20 +397,6 @@ export type NestedEnumTripStatusFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedEnumTripStatusFilter<$PrismaModel> | $Enums.TripStatus
|
not?: Prisma.NestedEnumTripStatusFilter<$PrismaModel> | $Enums.TripStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export const VerificationStatus = {
|
||||||
|
PENDING: 'PENDING',
|
||||||
|
APPROVED: 'APPROVED',
|
||||||
|
REJECTED: 'REJECTED'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type VerificationStatus = (typeof VerificationStatus)[keyof typeof VerificationStatus]
|
||||||
|
|
||||||
|
|
||||||
export const TripStatus = {
|
export const TripStatus = {
|
||||||
OPEN: 'OPEN',
|
OPEN: 'OPEN',
|
||||||
FULL: 'FULL',
|
FULL: 'FULL',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -385,6 +385,7 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
|
|||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
User: 'User',
|
User: 'User',
|
||||||
|
OrganizerVerification: 'OrganizerVerification',
|
||||||
Trip: 'Trip',
|
Trip: 'Trip',
|
||||||
TripReview: 'TripReview',
|
TripReview: 'TripReview',
|
||||||
TripImage: 'TripImage',
|
TripImage: 'TripImage',
|
||||||
@@ -404,7 +405,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
omit: GlobalOmitOptions
|
omit: GlobalOmitOptions
|
||||||
}
|
}
|
||||||
meta: {
|
meta: {
|
||||||
modelProps: "user" | "trip" | "tripReview" | "tripImage" | "tripParticipant"
|
modelProps: "user" | "organizerVerification" | "trip" | "tripReview" | "tripImage" | "tripParticipant"
|
||||||
txIsolationLevel: TransactionIsolationLevel
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
}
|
}
|
||||||
model: {
|
model: {
|
||||||
@@ -482,6 +483,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OrganizerVerification: {
|
||||||
|
payload: Prisma.$OrganizerVerificationPayload<ExtArgs>
|
||||||
|
fields: Prisma.OrganizerVerificationFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.OrganizerVerificationFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.OrganizerVerificationFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.OrganizerVerificationFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.OrganizerVerificationFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.OrganizerVerificationFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.OrganizerVerificationCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.OrganizerVerificationCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.OrganizerVerificationCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.OrganizerVerificationDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.OrganizerVerificationUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.OrganizerVerificationDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.OrganizerVerificationUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.OrganizerVerificationUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.OrganizerVerificationUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$OrganizerVerificationPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.OrganizerVerificationAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateOrganizerVerification>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.OrganizerVerificationGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.OrganizerVerificationGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.OrganizerVerificationCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.OrganizerVerificationCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Trip: {
|
Trip: {
|
||||||
payload: Prisma.$TripPayload<ExtArgs>
|
payload: Prisma.$TripPayload<ExtArgs>
|
||||||
fields: Prisma.TripFieldRefs
|
fields: Prisma.TripFieldRefs
|
||||||
@@ -823,7 +898,8 @@ export const UserScalarFieldEnum = {
|
|||||||
email: 'email',
|
email: 'email',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
image: 'image',
|
image: 'image',
|
||||||
isVerified: 'isVerified',
|
acceptedTermsAndPrivacy: 'acceptedTermsAndPrivacy',
|
||||||
|
acceptedAt: 'acceptedAt',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -831,6 +907,30 @@ export const UserScalarFieldEnum = {
|
|||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const OrganizerVerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
fullName: 'fullName',
|
||||||
|
nik: 'nik',
|
||||||
|
birthDate: 'birthDate',
|
||||||
|
address: 'address',
|
||||||
|
ktpImageUrl: 'ktpImageUrl',
|
||||||
|
selfieUrl: 'selfieUrl',
|
||||||
|
bankName: 'bankName',
|
||||||
|
bankAccountNumber: 'bankAccountNumber',
|
||||||
|
bankAccountName: 'bankAccountName',
|
||||||
|
status: 'status',
|
||||||
|
rejectionReason: 'rejectionReason',
|
||||||
|
reviewedAt: 'reviewedAt',
|
||||||
|
reviewedById: 'reviewedById',
|
||||||
|
verifiedAt: 'verifiedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type OrganizerVerificationScalarFieldEnum = (typeof OrganizerVerificationScalarFieldEnum)[keyof typeof OrganizerVerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const TripScalarFieldEnum = {
|
export const TripScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
@@ -956,6 +1056,20 @@ export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaM
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'VerificationStatus'
|
||||||
|
*/
|
||||||
|
export type EnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'VerificationStatus[]'
|
||||||
|
*/
|
||||||
|
export type ListEnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus[]'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'Int'
|
* Reference to a field of type 'Int'
|
||||||
*/
|
*/
|
||||||
@@ -1107,6 +1221,7 @@ export type PrismaClientOptions = ({
|
|||||||
}
|
}
|
||||||
export type GlobalOmitConfig = {
|
export type GlobalOmitConfig = {
|
||||||
user?: Prisma.UserOmit
|
user?: Prisma.UserOmit
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationOmit
|
||||||
trip?: Prisma.TripOmit
|
trip?: Prisma.TripOmit
|
||||||
tripReview?: Prisma.TripReviewOmit
|
tripReview?: Prisma.TripReviewOmit
|
||||||
tripImage?: Prisma.TripImageOmit
|
tripImage?: Prisma.TripImageOmit
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export const AnyNull = runtime.AnyNull
|
|||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
User: 'User',
|
User: 'User',
|
||||||
|
OrganizerVerification: 'OrganizerVerification',
|
||||||
Trip: 'Trip',
|
Trip: 'Trip',
|
||||||
TripReview: 'TripReview',
|
TripReview: 'TripReview',
|
||||||
TripImage: 'TripImage',
|
TripImage: 'TripImage',
|
||||||
@@ -80,7 +81,8 @@ export const UserScalarFieldEnum = {
|
|||||||
email: 'email',
|
email: 'email',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
image: 'image',
|
image: 'image',
|
||||||
isVerified: 'isVerified',
|
acceptedTermsAndPrivacy: 'acceptedTermsAndPrivacy',
|
||||||
|
acceptedAt: 'acceptedAt',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -88,6 +90,30 @@ export const UserScalarFieldEnum = {
|
|||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const OrganizerVerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
fullName: 'fullName',
|
||||||
|
nik: 'nik',
|
||||||
|
birthDate: 'birthDate',
|
||||||
|
address: 'address',
|
||||||
|
ktpImageUrl: 'ktpImageUrl',
|
||||||
|
selfieUrl: 'selfieUrl',
|
||||||
|
bankName: 'bankName',
|
||||||
|
bankAccountNumber: 'bankAccountNumber',
|
||||||
|
bankAccountName: 'bankAccountName',
|
||||||
|
status: 'status',
|
||||||
|
rejectionReason: 'rejectionReason',
|
||||||
|
reviewedAt: 'reviewedAt',
|
||||||
|
reviewedById: 'reviewedById',
|
||||||
|
verifiedAt: 'verifiedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type OrganizerVerificationScalarFieldEnum = (typeof OrganizerVerificationScalarFieldEnum)[keyof typeof OrganizerVerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const TripScalarFieldEnum = {
|
export const TripScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
export type * from './models/User'
|
export type * from './models/User'
|
||||||
|
export type * from './models/OrganizerVerification'
|
||||||
export type * from './models/Trip'
|
export type * from './models/Trip'
|
||||||
export type * from './models/TripReview'
|
export type * from './models/TripReview'
|
||||||
export type * from './models/TripImage'
|
export type * from './models/TripImage'
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -699,10 +699,6 @@ export type TripUncheckedUpdateManyWithoutOrganizerNestedInput = {
|
|||||||
deleteMany?: Prisma.TripScalarWhereInput | Prisma.TripScalarWhereInput[]
|
deleteMany?: Prisma.TripScalarWhereInput | Prisma.TripScalarWhereInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NullableDateTimeFieldUpdateOperationsInput = {
|
|
||||||
set?: Date | string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntFieldUpdateOperationsInput = {
|
export type IntFieldUpdateOperationsInput = {
|
||||||
set?: number
|
set?: number
|
||||||
increment?: number
|
increment?: number
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export type UserMinAggregateOutputType = {
|
|||||||
email: string | null
|
email: string | null
|
||||||
password: string | null
|
password: string | null
|
||||||
image: string | null
|
image: string | null
|
||||||
isVerified: boolean | null
|
acceptedTermsAndPrivacy: boolean | null
|
||||||
|
acceptedAt: Date | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -41,7 +42,8 @@ export type UserMaxAggregateOutputType = {
|
|||||||
email: string | null
|
email: string | null
|
||||||
password: string | null
|
password: string | null
|
||||||
image: string | null
|
image: string | null
|
||||||
isVerified: boolean | null
|
acceptedTermsAndPrivacy: boolean | null
|
||||||
|
acceptedAt: Date | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,8 @@ export type UserCountAggregateOutputType = {
|
|||||||
email: number
|
email: number
|
||||||
password: number
|
password: number
|
||||||
image: number
|
image: number
|
||||||
isVerified: number
|
acceptedTermsAndPrivacy: number
|
||||||
|
acceptedAt: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
_all: number
|
_all: number
|
||||||
@@ -65,7 +68,8 @@ export type UserMinAggregateInputType = {
|
|||||||
email?: true
|
email?: true
|
||||||
password?: true
|
password?: true
|
||||||
image?: true
|
image?: true
|
||||||
isVerified?: true
|
acceptedTermsAndPrivacy?: true
|
||||||
|
acceptedAt?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -76,7 +80,8 @@ export type UserMaxAggregateInputType = {
|
|||||||
email?: true
|
email?: true
|
||||||
password?: true
|
password?: true
|
||||||
image?: true
|
image?: true
|
||||||
isVerified?: true
|
acceptedTermsAndPrivacy?: true
|
||||||
|
acceptedAt?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -87,7 +92,8 @@ export type UserCountAggregateInputType = {
|
|||||||
email?: true
|
email?: true
|
||||||
password?: true
|
password?: true
|
||||||
image?: true
|
image?: true
|
||||||
isVerified?: true
|
acceptedTermsAndPrivacy?: true
|
||||||
|
acceptedAt?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
@@ -171,7 +177,8 @@ export type UserGroupByOutputType = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image: string | null
|
image: string | null
|
||||||
isVerified: boolean
|
acceptedTermsAndPrivacy: boolean
|
||||||
|
acceptedAt: Date | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
_count: UserCountAggregateOutputType | null
|
_count: UserCountAggregateOutputType | null
|
||||||
@@ -203,12 +210,15 @@ export type UserWhereInput = {
|
|||||||
email?: Prisma.StringFilter<"User"> | string
|
email?: Prisma.StringFilter<"User"> | string
|
||||||
password?: Prisma.StringFilter<"User"> | string
|
password?: Prisma.StringFilter<"User"> | string
|
||||||
image?: Prisma.StringNullableFilter<"User"> | string | null
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
isVerified?: Prisma.BoolFilter<"User"> | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
acceptedAt?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
trips?: Prisma.TripListRelationFilter
|
trips?: Prisma.TripListRelationFilter
|
||||||
participations?: Prisma.TripParticipantListRelationFilter
|
participations?: Prisma.TripParticipantListRelationFilter
|
||||||
tripReviews?: Prisma.TripReviewListRelationFilter
|
tripReviews?: Prisma.TripReviewListRelationFilter
|
||||||
|
organizerVerification?: Prisma.XOR<Prisma.OrganizerVerificationNullableScalarRelationFilter, Prisma.OrganizerVerificationWhereInput> | null
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationListRelationFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOrderByWithRelationInput = {
|
export type UserOrderByWithRelationInput = {
|
||||||
@@ -217,12 +227,15 @@ export type UserOrderByWithRelationInput = {
|
|||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
password?: Prisma.SortOrder
|
password?: Prisma.SortOrder
|
||||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
isVerified?: Prisma.SortOrder
|
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||||
|
acceptedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
trips?: Prisma.TripOrderByRelationAggregateInput
|
trips?: Prisma.TripOrderByRelationAggregateInput
|
||||||
participations?: Prisma.TripParticipantOrderByRelationAggregateInput
|
participations?: Prisma.TripParticipantOrderByRelationAggregateInput
|
||||||
tripReviews?: Prisma.TripReviewOrderByRelationAggregateInput
|
tripReviews?: Prisma.TripReviewOrderByRelationAggregateInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationOrderByWithRelationInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationOrderByRelationAggregateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserWhereUniqueInput = Prisma.AtLeast<{
|
export type UserWhereUniqueInput = Prisma.AtLeast<{
|
||||||
@@ -234,12 +247,15 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
name?: Prisma.StringFilter<"User"> | string
|
name?: Prisma.StringFilter<"User"> | string
|
||||||
password?: Prisma.StringFilter<"User"> | string
|
password?: Prisma.StringFilter<"User"> | string
|
||||||
image?: Prisma.StringNullableFilter<"User"> | string | null
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
isVerified?: Prisma.BoolFilter<"User"> | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
acceptedAt?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
trips?: Prisma.TripListRelationFilter
|
trips?: Prisma.TripListRelationFilter
|
||||||
participations?: Prisma.TripParticipantListRelationFilter
|
participations?: Prisma.TripParticipantListRelationFilter
|
||||||
tripReviews?: Prisma.TripReviewListRelationFilter
|
tripReviews?: Prisma.TripReviewListRelationFilter
|
||||||
|
organizerVerification?: Prisma.XOR<Prisma.OrganizerVerificationNullableScalarRelationFilter, Prisma.OrganizerVerificationWhereInput> | null
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationListRelationFilter
|
||||||
}, "id" | "email">
|
}, "id" | "email">
|
||||||
|
|
||||||
export type UserOrderByWithAggregationInput = {
|
export type UserOrderByWithAggregationInput = {
|
||||||
@@ -248,7 +264,8 @@ export type UserOrderByWithAggregationInput = {
|
|||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
password?: Prisma.SortOrder
|
password?: Prisma.SortOrder
|
||||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
isVerified?: Prisma.SortOrder
|
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||||
|
acceptedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
_count?: Prisma.UserCountOrderByAggregateInput
|
_count?: Prisma.UserCountOrderByAggregateInput
|
||||||
@@ -265,7 +282,8 @@ export type UserScalarWhereWithAggregatesInput = {
|
|||||||
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
password?: Prisma.StringWithAggregatesFilter<"User"> | string
|
password?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
isVerified?: Prisma.BoolWithAggregatesFilter<"User"> | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolWithAggregatesFilter<"User"> | boolean
|
||||||
|
acceptedAt?: Prisma.DateTimeNullableWithAggregatesFilter<"User"> | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -276,12 +294,15 @@ export type UserCreateInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateInput = {
|
export type UserUncheckedCreateInput = {
|
||||||
@@ -290,12 +311,15 @@ export type UserUncheckedCreateInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateInput = {
|
export type UserUpdateInput = {
|
||||||
@@ -304,12 +328,15 @@ export type UserUpdateInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateInput = {
|
export type UserUncheckedUpdateInput = {
|
||||||
@@ -318,12 +345,15 @@ export type UserUncheckedUpdateInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateManyInput = {
|
export type UserCreateManyInput = {
|
||||||
@@ -332,7 +362,8 @@ export type UserCreateManyInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -343,7 +374,8 @@ export type UserUpdateManyMutationInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -354,7 +386,8 @@ export type UserUncheckedUpdateManyInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -365,7 +398,8 @@ export type UserCountOrderByAggregateInput = {
|
|||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
password?: Prisma.SortOrder
|
password?: Prisma.SortOrder
|
||||||
image?: Prisma.SortOrder
|
image?: Prisma.SortOrder
|
||||||
isVerified?: Prisma.SortOrder
|
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||||
|
acceptedAt?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -376,7 +410,8 @@ export type UserMaxOrderByAggregateInput = {
|
|||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
password?: Prisma.SortOrder
|
password?: Prisma.SortOrder
|
||||||
image?: Prisma.SortOrder
|
image?: Prisma.SortOrder
|
||||||
isVerified?: Prisma.SortOrder
|
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||||
|
acceptedAt?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -387,7 +422,8 @@ export type UserMinOrderByAggregateInput = {
|
|||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
password?: Prisma.SortOrder
|
password?: Prisma.SortOrder
|
||||||
image?: Prisma.SortOrder
|
image?: Prisma.SortOrder
|
||||||
isVerified?: Prisma.SortOrder
|
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||||
|
acceptedAt?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -397,6 +433,11 @@ export type UserScalarRelationFilter = {
|
|||||||
isNot?: Prisma.UserWhereInput
|
isNot?: Prisma.UserWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserNullableScalarRelationFilter = {
|
||||||
|
is?: Prisma.UserWhereInput | null
|
||||||
|
isNot?: Prisma.UserWhereInput | null
|
||||||
|
}
|
||||||
|
|
||||||
export type StringFieldUpdateOperationsInput = {
|
export type StringFieldUpdateOperationsInput = {
|
||||||
set?: string
|
set?: string
|
||||||
}
|
}
|
||||||
@@ -409,10 +450,44 @@ export type BoolFieldUpdateOperationsInput = {
|
|||||||
set?: boolean
|
set?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NullableDateTimeFieldUpdateOperationsInput = {
|
||||||
|
set?: Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type DateTimeFieldUpdateOperationsInput = {
|
export type DateTimeFieldUpdateOperationsInput = {
|
||||||
set?: Date | string
|
set?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutOrganizerVerificationInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutOrganizerVerificationInput, Prisma.UserUncheckedCreateWithoutOrganizerVerificationInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutOrganizerVerificationInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutReviewedVerificationsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutReviewedVerificationsInput, Prisma.UserUncheckedCreateWithoutReviewedVerificationsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutReviewedVerificationsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneRequiredWithoutOrganizerVerificationNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutOrganizerVerificationInput, Prisma.UserUncheckedCreateWithoutOrganizerVerificationInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutOrganizerVerificationInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutOrganizerVerificationInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutOrganizerVerificationInput, Prisma.UserUpdateWithoutOrganizerVerificationInput>, Prisma.UserUncheckedUpdateWithoutOrganizerVerificationInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneWithoutReviewedVerificationsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutReviewedVerificationsInput, Prisma.UserUncheckedCreateWithoutReviewedVerificationsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutReviewedVerificationsInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutReviewedVerificationsInput
|
||||||
|
disconnect?: Prisma.UserWhereInput | boolean
|
||||||
|
delete?: Prisma.UserWhereInput | boolean
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutReviewedVerificationsInput, Prisma.UserUpdateWithoutReviewedVerificationsInput>, Prisma.UserUncheckedUpdateWithoutReviewedVerificationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
export type UserCreateNestedOneWithoutTripsInput = {
|
export type UserCreateNestedOneWithoutTripsInput = {
|
||||||
create?: Prisma.XOR<Prisma.UserCreateWithoutTripsInput, Prisma.UserUncheckedCreateWithoutTripsInput>
|
create?: Prisma.XOR<Prisma.UserCreateWithoutTripsInput, Prisma.UserUncheckedCreateWithoutTripsInput>
|
||||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutTripsInput
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutTripsInput
|
||||||
@@ -455,17 +530,180 @@ export type UserUpdateOneRequiredWithoutParticipationsNestedInput = {
|
|||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutParticipationsInput, Prisma.UserUpdateWithoutParticipationsInput>, Prisma.UserUncheckedUpdateWithoutParticipationsInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutParticipationsInput, Prisma.UserUpdateWithoutParticipationsInput>, Prisma.UserUncheckedUpdateWithoutParticipationsInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutOrganizerVerificationInput = {
|
||||||
|
id?: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
image?: string | null
|
||||||
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||||
|
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||||
|
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutOrganizerVerificationInput = {
|
||||||
|
id?: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
image?: string | null
|
||||||
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||||
|
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutOrganizerVerificationInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutOrganizerVerificationInput, Prisma.UserUncheckedCreateWithoutOrganizerVerificationInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutReviewedVerificationsInput = {
|
||||||
|
id?: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
image?: string | null
|
||||||
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||||
|
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||||
|
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutReviewedVerificationsInput = {
|
||||||
|
id?: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
image?: string | null
|
||||||
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||||
|
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutReviewedVerificationsInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutReviewedVerificationsInput, Prisma.UserUncheckedCreateWithoutReviewedVerificationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutOrganizerVerificationInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutOrganizerVerificationInput, Prisma.UserUncheckedUpdateWithoutOrganizerVerificationInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutOrganizerVerificationInput, Prisma.UserUncheckedCreateWithoutOrganizerVerificationInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutOrganizerVerificationInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutOrganizerVerificationInput, Prisma.UserUncheckedUpdateWithoutOrganizerVerificationInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutOrganizerVerificationInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||||
|
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||||
|
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutOrganizerVerificationInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||||
|
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutReviewedVerificationsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutReviewedVerificationsInput, Prisma.UserUncheckedUpdateWithoutReviewedVerificationsInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutReviewedVerificationsInput, Prisma.UserUncheckedCreateWithoutReviewedVerificationsInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutReviewedVerificationsInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutReviewedVerificationsInput, Prisma.UserUncheckedUpdateWithoutReviewedVerificationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutReviewedVerificationsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||||
|
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||||
|
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutReviewedVerificationsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||||
|
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutTripsInput = {
|
export type UserCreateWithoutTripsInput = {
|
||||||
id?: string
|
id?: string
|
||||||
name: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutTripsInput = {
|
export type UserUncheckedCreateWithoutTripsInput = {
|
||||||
@@ -474,11 +712,14 @@ export type UserUncheckedCreateWithoutTripsInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutTripsInput = {
|
export type UserCreateOrConnectWithoutTripsInput = {
|
||||||
@@ -503,11 +744,14 @@ export type UserUpdateWithoutTripsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutTripsInput = {
|
export type UserUncheckedUpdateWithoutTripsInput = {
|
||||||
@@ -516,11 +760,14 @@ export type UserUncheckedUpdateWithoutTripsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutTripReviewsInput = {
|
export type UserCreateWithoutTripReviewsInput = {
|
||||||
@@ -529,11 +776,14 @@ export type UserCreateWithoutTripReviewsInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutTripReviewsInput = {
|
export type UserUncheckedCreateWithoutTripReviewsInput = {
|
||||||
@@ -542,11 +792,14 @@ export type UserUncheckedCreateWithoutTripReviewsInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutTripReviewsInput = {
|
export type UserCreateOrConnectWithoutTripReviewsInput = {
|
||||||
@@ -571,11 +824,14 @@ export type UserUpdateWithoutTripReviewsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutTripReviewsInput = {
|
export type UserUncheckedUpdateWithoutTripReviewsInput = {
|
||||||
@@ -584,11 +840,14 @@ export type UserUncheckedUpdateWithoutTripReviewsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutParticipationsInput = {
|
export type UserCreateWithoutParticipationsInput = {
|
||||||
@@ -597,11 +856,14 @@ export type UserCreateWithoutParticipationsInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutParticipationsInput = {
|
export type UserUncheckedCreateWithoutParticipationsInput = {
|
||||||
@@ -610,11 +872,14 @@ export type UserUncheckedCreateWithoutParticipationsInput = {
|
|||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
image?: string | null
|
image?: string | null
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: Date | string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutParticipationsInput = {
|
export type UserCreateOrConnectWithoutParticipationsInput = {
|
||||||
@@ -639,11 +904,14 @@ export type UserUpdateWithoutParticipationsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutParticipationsInput = {
|
export type UserUncheckedUpdateWithoutParticipationsInput = {
|
||||||
@@ -652,11 +920,14 @@ export type UserUncheckedUpdateWithoutParticipationsInput = {
|
|||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
isVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||||
|
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -668,12 +939,14 @@ export type UserCountOutputType = {
|
|||||||
trips: number
|
trips: number
|
||||||
participations: number
|
participations: number
|
||||||
tripReviews: number
|
tripReviews: number
|
||||||
|
reviewedVerifications: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
trips?: boolean | UserCountOutputTypeCountTripsArgs
|
trips?: boolean | UserCountOutputTypeCountTripsArgs
|
||||||
participations?: boolean | UserCountOutputTypeCountParticipationsArgs
|
participations?: boolean | UserCountOutputTypeCountParticipationsArgs
|
||||||
tripReviews?: boolean | UserCountOutputTypeCountTripReviewsArgs
|
tripReviews?: boolean | UserCountOutputTypeCountTripReviewsArgs
|
||||||
|
reviewedVerifications?: boolean | UserCountOutputTypeCountReviewedVerificationsArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -707,6 +980,13 @@ export type UserCountOutputTypeCountTripReviewsArgs<ExtArgs extends runtime.Type
|
|||||||
where?: Prisma.TripReviewWhereInput
|
where?: Prisma.TripReviewWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountReviewedVerificationsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.OrganizerVerificationWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
@@ -714,12 +994,15 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
|||||||
email?: boolean
|
email?: boolean
|
||||||
password?: boolean
|
password?: boolean
|
||||||
image?: boolean
|
image?: boolean
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
||||||
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
||||||
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
||||||
|
organizerVerification?: boolean | Prisma.User$organizerVerificationArgs<ExtArgs>
|
||||||
|
reviewedVerifications?: boolean | Prisma.User$reviewedVerificationsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
|
|
||||||
@@ -729,7 +1012,8 @@ export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
|||||||
email?: boolean
|
email?: boolean
|
||||||
password?: boolean
|
password?: boolean
|
||||||
image?: boolean
|
image?: boolean
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
@@ -740,7 +1024,8 @@ export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
|||||||
email?: boolean
|
email?: boolean
|
||||||
password?: boolean
|
password?: boolean
|
||||||
image?: boolean
|
image?: boolean
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
@@ -751,16 +1036,19 @@ export type UserSelectScalar = {
|
|||||||
email?: boolean
|
email?: boolean
|
||||||
password?: boolean
|
password?: boolean
|
||||||
image?: boolean
|
image?: boolean
|
||||||
isVerified?: boolean
|
acceptedTermsAndPrivacy?: boolean
|
||||||
|
acceptedAt?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "email" | "password" | "image" | "isVerified" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "email" | "password" | "image" | "acceptedTermsAndPrivacy" | "acceptedAt" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
||||||
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
||||||
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
||||||
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
||||||
|
organizerVerification?: boolean | Prisma.User$organizerVerificationArgs<ExtArgs>
|
||||||
|
reviewedVerifications?: boolean | Prisma.User$reviewedVerificationsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type UserIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
export type UserIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
||||||
@@ -772,6 +1060,8 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|||||||
trips: Prisma.$TripPayload<ExtArgs>[]
|
trips: Prisma.$TripPayload<ExtArgs>[]
|
||||||
participations: Prisma.$TripParticipantPayload<ExtArgs>[]
|
participations: Prisma.$TripParticipantPayload<ExtArgs>[]
|
||||||
tripReviews: Prisma.$TripReviewPayload<ExtArgs>[]
|
tripReviews: Prisma.$TripReviewPayload<ExtArgs>[]
|
||||||
|
organizerVerification: Prisma.$OrganizerVerificationPayload<ExtArgs> | null
|
||||||
|
reviewedVerifications: Prisma.$OrganizerVerificationPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
@@ -780,9 +1070,13 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|||||||
password: string
|
password: string
|
||||||
image: string | null
|
image: string | null
|
||||||
/**
|
/**
|
||||||
* Akun diverifikasi tim SeTrip (manual / admin) — tampil sebagai badge kepercayaan
|
* Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||||
*/
|
*/
|
||||||
isVerified: boolean
|
acceptedTermsAndPrivacy: boolean
|
||||||
|
/**
|
||||||
|
* Waktu user menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||||
|
*/
|
||||||
|
acceptedAt: Date | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
@@ -1182,6 +1476,8 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
|
|||||||
trips<T extends Prisma.User$tripsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
trips<T extends Prisma.User$tripsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
participations<T extends Prisma.User$participationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$participationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripParticipantPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
participations<T extends Prisma.User$participationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$participationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripParticipantPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
tripReviews<T extends Prisma.User$tripReviewsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripReviewsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripReviewPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
tripReviews<T extends Prisma.User$tripReviewsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripReviewsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripReviewPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
|
organizerVerification<T extends Prisma.User$organizerVerificationArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$organizerVerificationArgs<ExtArgs>>): Prisma.Prisma__OrganizerVerificationClient<runtime.Types.Result.GetResult<Prisma.$OrganizerVerificationPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||||
|
reviewedVerifications<T extends Prisma.User$reviewedVerificationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$reviewedVerificationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$OrganizerVerificationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||||
@@ -1216,7 +1512,8 @@ export interface UserFieldRefs {
|
|||||||
readonly email: Prisma.FieldRef<"User", 'String'>
|
readonly email: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly password: Prisma.FieldRef<"User", 'String'>
|
readonly password: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly image: Prisma.FieldRef<"User", 'String'>
|
readonly image: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly isVerified: Prisma.FieldRef<"User", 'Boolean'>
|
readonly acceptedTermsAndPrivacy: Prisma.FieldRef<"User", 'Boolean'>
|
||||||
|
readonly acceptedAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
}
|
}
|
||||||
@@ -1683,6 +1980,49 @@ export type User$tripReviewsArgs<ExtArgs extends runtime.Types.Extensions.Intern
|
|||||||
distinct?: Prisma.TripReviewScalarFieldEnum | Prisma.TripReviewScalarFieldEnum[]
|
distinct?: Prisma.TripReviewScalarFieldEnum | Prisma.TripReviewScalarFieldEnum[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.organizerVerification
|
||||||
|
*/
|
||||||
|
export type User$organizerVerificationArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the OrganizerVerification
|
||||||
|
*/
|
||||||
|
select?: Prisma.OrganizerVerificationSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the OrganizerVerification
|
||||||
|
*/
|
||||||
|
omit?: Prisma.OrganizerVerificationOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.OrganizerVerificationInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.OrganizerVerificationWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.reviewedVerifications
|
||||||
|
*/
|
||||||
|
export type User$reviewedVerificationsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the OrganizerVerification
|
||||||
|
*/
|
||||||
|
select?: Prisma.OrganizerVerificationSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the OrganizerVerification
|
||||||
|
*/
|
||||||
|
omit?: Prisma.OrganizerVerificationOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.OrganizerVerificationInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.OrganizerVerificationWhereInput
|
||||||
|
orderBy?: Prisma.OrganizerVerificationOrderByWithRelationInput | Prisma.OrganizerVerificationOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.OrganizerVerificationWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.OrganizerVerificationScalarFieldEnum | Prisma.OrganizerVerificationScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User without action
|
* User without action
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Kebijakan Privasi",
|
||||||
|
description:
|
||||||
|
"Kebijakan Privasi SeTrip. Pelajari bagaimana kami mengumpulkan, menggunakan, dan melindungi data Anda.",
|
||||||
|
alternates: { canonical: "/privacy" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PrivacyLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function PrivacyPage() {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-3xl px-4 py-8 sm:py-12">
|
||||||
|
<article className="rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm sm:p-10">
|
||||||
|
<header className="mb-8 border-b border-neutral-200 pb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||||
|
🔒 Kebijakan Privasi SeTrip
|
||||||
|
</h1>
|
||||||
|
<p className="mt-2 text-sm text-neutral-500">
|
||||||
|
Terakhir diperbarui: 2026-04-27
|
||||||
|
</p>
|
||||||
|
<p className="mt-4 text-sm leading-relaxed text-neutral-700">
|
||||||
|
SeTrip menghargai privasi Anda. Kebijakan Privasi ini menjelaskan
|
||||||
|
bagaimana kami mengumpulkan, menggunakan, dan melindungi informasi
|
||||||
|
Anda saat menggunakan platform SeTrip. Dengan menggunakan SeTrip,
|
||||||
|
Anda menyetujui praktik yang dijelaskan dalam Kebijakan Privasi ini.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="space-y-8 text-sm leading-relaxed text-neutral-700">
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
1. Informasi yang Kami Kumpulkan
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">a. Informasi Akun</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Nama</li>
|
||||||
|
<li>Email</li>
|
||||||
|
<li>Nomor telepon</li>
|
||||||
|
<li>Password (disimpan dalam bentuk terenkripsi)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">b. Informasi Profil</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Foto profil</li>
|
||||||
|
<li>Deskripsi diri</li>
|
||||||
|
<li>Riwayat trip</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">c. Informasi Transaksi</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Data booking trip</li>
|
||||||
|
<li>Status pembayaran</li>
|
||||||
|
<li>Riwayat aktivitas</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">d. Informasi Teknis</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Alamat IP</li>
|
||||||
|
<li>Browser</li>
|
||||||
|
<li>Perangkat yang digunakan</li>
|
||||||
|
<li>Log aktivitas</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
2. Cara Kami Menggunakan Informasi
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">Kami menggunakan informasi Anda untuk:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Membuat dan mengelola akun</li>
|
||||||
|
<li>Menghubungkan pengguna dengan organizer</li>
|
||||||
|
<li>Memproses booking dan aktivitas trip</li>
|
||||||
|
<li>Meningkatkan layanan dan pengalaman pengguna</li>
|
||||||
|
<li>Mengirim notifikasi terkait aktivitas</li>
|
||||||
|
<li>Mencegah penipuan dan penyalahgunaan</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">3. Pembagian Informasi</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
Kami tidak menjual data pribadi Anda. Namun, kami dapat membagikan
|
||||||
|
informasi dalam kondisi berikut:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">a. Dengan Organizer</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>
|
||||||
|
Informasi dasar seperti nama dan kontak dapat dibagikan kepada
|
||||||
|
organizer untuk keperluan trip
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">
|
||||||
|
b. Dengan Penyedia Layanan
|
||||||
|
</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Untuk kebutuhan teknis (hosting, analytics, dll)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="mb-2 mt-4 font-semibold text-neutral-800">c. Kewajiban Hukum</h3>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Jika diminta oleh hukum atau otoritas berwenang</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">4. Keamanan Data</h2>
|
||||||
|
<p className="mb-3">Kami berusaha melindungi data Anda dengan:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Enkripsi password</li>
|
||||||
|
<li>Pembatasan akses data</li>
|
||||||
|
<li>Sistem keamanan standar industri</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3">Namun, tidak ada sistem yang 100% aman.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">5. Penyimpanan Data</h2>
|
||||||
|
<p className="mb-3">Kami menyimpan data Anda selama:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Akun Anda aktif</li>
|
||||||
|
<li>Dibutuhkan untuk keperluan layanan</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3">
|
||||||
|
Data dapat dihapus atas permintaan pengguna, kecuali diwajibkan
|
||||||
|
oleh hukum untuk disimpan.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">6. Hak Pengguna</h2>
|
||||||
|
<p className="mb-3">Anda memiliki hak untuk:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Mengakses data pribadi Anda</li>
|
||||||
|
<li>Memperbarui informasi</li>
|
||||||
|
<li>Menghapus akun</li>
|
||||||
|
<li>Menarik persetujuan</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">7. Cookie & Tracking</h2>
|
||||||
|
<p className="mb-3">SeTrip dapat menggunakan:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Cookie</li>
|
||||||
|
<li>Teknologi pelacakan sederhana</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3 mb-2">Untuk:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Menyimpan sesi login</li>
|
||||||
|
<li>Meningkatkan pengalaman pengguna</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
8. Layanan Pihak Ketiga
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">SeTrip dapat menggunakan layanan pihak ketiga seperti:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Hosting</li>
|
||||||
|
<li>Analytics</li>
|
||||||
|
<li>Payment gateway (di masa depan)</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3">
|
||||||
|
Kami tidak bertanggung jawab atas kebijakan privasi pihak ketiga
|
||||||
|
tersebut.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
9. Perlindungan terhadap Penipuan
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">Kami dapat menggunakan data untuk:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Mendeteksi aktivitas mencurigakan</li>
|
||||||
|
<li>Mencegah penipuan</li>
|
||||||
|
<li>Melindungi pengguna lain</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
10. Perubahan Kebijakan Privasi
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
SeTrip dapat memperbarui Kebijakan Privasi ini sewaktu-waktu.
|
||||||
|
Pengguna disarankan untuk:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Membaca secara berkala</li>
|
||||||
|
<li>Memahami perubahan yang berlaku</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">11. Kontak</h2>
|
||||||
|
<p>
|
||||||
|
Jika Anda memiliki pertanyaan mengenai Kebijakan Privasi ini,
|
||||||
|
silakan hubungi:{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:support@setrip.com"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
support@setrip.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-xl bg-neutral-50 p-5">
|
||||||
|
<h2 className="mb-2 text-lg font-bold text-neutral-900">✅ Persetujuan</h2>
|
||||||
|
<p className="mb-2">
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Telah membaca</li>
|
||||||
|
<li>Memahami</li>
|
||||||
|
<li>Menyetujui Kebijakan Privasi ini</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer className="mt-10 border-t border-neutral-200 pt-6 text-sm">
|
||||||
|
<p className="text-neutral-500">
|
||||||
|
Lihat juga{" "}
|
||||||
|
<Link
|
||||||
|
href="/terms"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
Syarat & Ketentuan
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -140,6 +140,37 @@ export default function RegisterPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label className="flex items-start gap-2.5 text-sm text-neutral-700">
|
||||||
|
<input
|
||||||
|
id="acceptedTermsAndPrivacy"
|
||||||
|
name="acceptedTermsAndPrivacy"
|
||||||
|
type="checkbox"
|
||||||
|
required
|
||||||
|
className="mt-0.5 h-4 w-4 shrink-0 rounded border-neutral-300 text-primary-600 focus:ring-primary-500"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
Saya menyetujui{" "}
|
||||||
|
<Link
|
||||||
|
href="/terms"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
Syarat & Ketentuan
|
||||||
|
</Link>{" "}
|
||||||
|
dan{" "}
|
||||||
|
<Link
|
||||||
|
href="/privacy"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
Kebijakan Privasi
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
|||||||
@@ -30,6 +30,18 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|||||||
changeFrequency: "yearly",
|
changeFrequency: "yearly",
|
||||||
priority: 0.3,
|
priority: 0.3,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: absoluteUrl("/terms"),
|
||||||
|
lastModified: now,
|
||||||
|
changeFrequency: "yearly",
|
||||||
|
priority: 0.2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: absoluteUrl("/privacy"),
|
||||||
|
lastModified: now,
|
||||||
|
changeFrequency: "yearly",
|
||||||
|
priority: 0.2,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const tripEntries: MetadataRoute.Sitemap = trips.map((t) => ({
|
const tripEntries: MetadataRoute.Sitemap = trips.map((t) => ({
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Syarat & Ketentuan",
|
||||||
|
description:
|
||||||
|
"Syarat & Ketentuan penggunaan platform SeTrip. Baca sebelum menggunakan layanan SeTrip.",
|
||||||
|
alternates: { canonical: "/terms" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TermsLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function TermsPage() {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-3xl px-4 py-8 sm:py-12">
|
||||||
|
<article className="rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm sm:p-10">
|
||||||
|
<header className="mb-8 border-b border-neutral-200 pb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||||
|
📜 Syarat & Ketentuan SeTrip
|
||||||
|
</h1>
|
||||||
|
<p className="mt-2 text-sm text-neutral-500">
|
||||||
|
Terakhir diperbarui: 2026-04-27
|
||||||
|
</p>
|
||||||
|
<p className="mt-4 text-sm leading-relaxed text-neutral-700">
|
||||||
|
Selamat datang di SeTrip. Dengan mengakses atau menggunakan platform
|
||||||
|
SeTrip, Anda menyetujui untuk terikat oleh Syarat & Ketentuan
|
||||||
|
berikut.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="space-y-8 text-sm leading-relaxed text-neutral-700">
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">1. Definisi</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>
|
||||||
|
<strong>SeTrip</strong>: Platform yang menghubungkan pengguna
|
||||||
|
dengan penyelenggara trip.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Pengguna (User)</strong>: Individu yang menggunakan
|
||||||
|
aplikasi SeTrip.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Organizer (Penyelenggara)</strong>: Pengguna yang membuat
|
||||||
|
dan mengelola trip.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Trip</strong>: Kegiatan perjalanan yang dibuat oleh
|
||||||
|
organizer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Platform</strong>: Website atau aplikasi SeTrip.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">2. Peran SeTrip</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
SeTrip bertindak sebagai <strong>platform perantara</strong> yang
|
||||||
|
menghubungkan pengguna dan organizer. SeTrip:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Bukan penyelenggara trip</li>
|
||||||
|
<li>Tidak terlibat langsung dalam pelaksanaan perjalanan</li>
|
||||||
|
<li>Tidak bertanggung jawab atas kegiatan selama trip berlangsung</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
3. Penggunaan Platform
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Berusia minimal 18 tahun atau memiliki izin dari wali</li>
|
||||||
|
<li>Memberikan informasi yang benar dan akurat</li>
|
||||||
|
<li>
|
||||||
|
Tidak menggunakan platform untuk: penipuan, aktivitas ilegal,
|
||||||
|
atau penyebaran informasi palsu
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">4. Akun Pengguna</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Pengguna bertanggung jawab atas keamanan akun masing-masing</li>
|
||||||
|
<li>Dilarang membagikan akun kepada pihak lain</li>
|
||||||
|
<li>
|
||||||
|
SeTrip berhak menangguhkan atau menghapus akun jika terjadi
|
||||||
|
pelanggaran
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">5. Trip & Booking</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Organizer bertanggung jawab atas seluruh informasi trip</li>
|
||||||
|
<li>Pengguna wajib membaca detail trip sebelum melakukan join</li>
|
||||||
|
<li>
|
||||||
|
Dengan melakukan join trip, pengguna menyetujui seluruh ketentuan
|
||||||
|
trip yang dibuat oleh organizer
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">6. Pembayaran</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Pembayaran dilakukan sesuai metode yang tersedia di platform</li>
|
||||||
|
<li>
|
||||||
|
Dalam fase awal, pembayaran dapat dilakukan langsung kepada
|
||||||
|
organizer
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
SeTrip tidak menjamin keamanan transaksi yang dilakukan di luar
|
||||||
|
platform
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
7. Pembatalan & Refund
|
||||||
|
</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Kebijakan pembatalan ditentukan oleh organizer</li>
|
||||||
|
<li>
|
||||||
|
SeTrip tidak bertanggung jawab atas refund yang tidak diberikan
|
||||||
|
oleh organizer
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Pengguna disarankan untuk memahami kebijakan sebelum melakukan
|
||||||
|
pembayaran
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
8. Tanggung Jawab Organizer
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">Organizer wajib:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Memberikan informasi trip yang jelas dan akurat</li>
|
||||||
|
<li>Menjalankan trip sesuai deskripsi</li>
|
||||||
|
<li>Bertanggung jawab atas keselamatan peserta selama trip</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">9. Risiko Perjalanan</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
Pengguna memahami bahwa aktivitas perjalanan, terutama kegiatan
|
||||||
|
outdoor, memiliki risiko termasuk namun tidak terbatas pada:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Cedera</li>
|
||||||
|
<li>Kecelakaan</li>
|
||||||
|
<li>Cuaca ekstrem</li>
|
||||||
|
<li>Kondisi tak terduga lainnya</li>
|
||||||
|
</ul>
|
||||||
|
<blockquote className="mt-3 border-l-4 border-primary-500 bg-primary-50 px-4 py-3 italic text-neutral-700">
|
||||||
|
Mengikuti kegiatan secara sadar dan bertanggung jawab atas risiko
|
||||||
|
pribadi
|
||||||
|
</blockquote>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
10. Batasan Tanggung Jawab
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">SeTrip tidak bertanggung jawab atas:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Kerugian finansial</li>
|
||||||
|
<li>Cedera atau kecelakaan</li>
|
||||||
|
<li>Kegagalan pelaksanaan trip</li>
|
||||||
|
<li>Tindakan organizer atau pengguna lain</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
11. Larangan Transaksi di Luar Platform
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
Pengguna disarankan untuk tidak melakukan transaksi di luar
|
||||||
|
platform. SeTrip tidak bertanggung jawab atas:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Penipuan</li>
|
||||||
|
<li>Kerugian</li>
|
||||||
|
<li>Masalah lain yang terjadi akibat transaksi di luar sistem SeTrip</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">12. Sistem Review</h2>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Pengguna dapat memberikan review setelah trip</li>
|
||||||
|
<li>Review harus jujur dan tidak mengandung unsur fitnah</li>
|
||||||
|
<li>SeTrip berhak menghapus review yang melanggar</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
13. Penangguhan & Penghentian Akun
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">SeTrip berhak untuk:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Menangguhkan akun</li>
|
||||||
|
<li>Menghapus akun</li>
|
||||||
|
<li>Membatasi akses</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3 mb-3">Jika pengguna:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Melanggar ketentuan</li>
|
||||||
|
<li>Terindikasi melakukan penipuan</li>
|
||||||
|
<li>Menyalahgunakan platform</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">14. Perubahan Layanan</h2>
|
||||||
|
<p className="mb-3">SeTrip dapat:</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Mengubah fitur</li>
|
||||||
|
<li>Menghentikan layanan</li>
|
||||||
|
<li>Menambahkan kebijakan baru</li>
|
||||||
|
</ul>
|
||||||
|
<p className="mt-3">Tanpa pemberitahuan sebelumnya.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">
|
||||||
|
15. Perubahan Syarat & Ketentuan
|
||||||
|
</h2>
|
||||||
|
<p className="mb-3">
|
||||||
|
SeTrip dapat memperbarui Syarat & Ketentuan ini kapan saja.
|
||||||
|
Pengguna disarankan untuk:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Membaca secara berkala</li>
|
||||||
|
<li>Memahami perubahan yang berlaku</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">16. Hukum yang Berlaku</h2>
|
||||||
|
<p>
|
||||||
|
Syarat & Ketentuan ini diatur oleh hukum yang berlaku di
|
||||||
|
Republik Indonesia.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-lg font-bold text-neutral-900">17. Kontak</h2>
|
||||||
|
<p>
|
||||||
|
Jika Anda memiliki pertanyaan, silakan hubungi:{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:support@setrip.com"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
support@setrip.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-xl bg-neutral-50 p-5">
|
||||||
|
<h2 className="mb-2 text-lg font-bold text-neutral-900">✅ Persetujuan</h2>
|
||||||
|
<p className="mb-2">
|
||||||
|
Dengan menggunakan SeTrip, Anda menyatakan bahwa:
|
||||||
|
</p>
|
||||||
|
<ul className="ml-5 list-disc space-y-1.5">
|
||||||
|
<li>Telah membaca</li>
|
||||||
|
<li>Memahami</li>
|
||||||
|
<li>Menyetujui seluruh isi Syarat & Ketentuan ini</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer className="mt-10 border-t border-neutral-200 pt-6 text-sm">
|
||||||
|
<p className="text-neutral-500">
|
||||||
|
Lihat juga{" "}
|
||||||
|
<Link
|
||||||
|
href="/privacy"
|
||||||
|
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||||
|
>
|
||||||
|
Kebijakan Privasi
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Verifikasi Organizer",
|
||||||
|
description:
|
||||||
|
"Ajukan verifikasi sebagai organizer SeTrip dengan KTP & data rekening untuk membuat trip berbayar.",
|
||||||
|
alternates: { canonical: "/verify" },
|
||||||
|
robots: { index: false, follow: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function VerifyLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { getServerSession } from "next-auth";
|
||||||
|
import { authOptions } from "@/lib/auth";
|
||||||
|
import { organizerService } from "@/server/services/organizer.service";
|
||||||
|
import { VerifyForm } from "@/features/organizer/components/verify-form";
|
||||||
|
import { VerifiedBadge } from "@/components/shared/verified-badge";
|
||||||
|
|
||||||
|
export default async function VerifyPage() {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user) {
|
||||||
|
redirect("/login?callbackUrl=/verify");
|
||||||
|
}
|
||||||
|
|
||||||
|
const verification = await organizerService.getStatusForUser(session.user.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl px-4 py-8 sm:py-12">
|
||||||
|
<div className="mb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||||
|
Verifikasi Organizer
|
||||||
|
</h1>
|
||||||
|
<p className="mt-2 text-sm text-neutral-600">
|
||||||
|
Lengkapi data berikut untuk mengaktifkan kemampuan membuat trip berbayar.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{verification?.status === "APPROVED" && (
|
||||||
|
<div className="mb-6 rounded-2xl border border-primary-200 bg-primary-50 p-5">
|
||||||
|
<div className="mb-2 flex items-center gap-2">
|
||||||
|
<VerifiedBadge size="md" />
|
||||||
|
<span className="text-sm font-semibold text-primary-800">
|
||||||
|
Akun terverifikasi
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-neutral-700">
|
||||||
|
Selamat! Kamu sudah bisa membuat trip berbayar.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verification?.status === "PENDING" && (
|
||||||
|
<div className="mb-6 rounded-2xl border border-amber-200 bg-amber-50 p-5">
|
||||||
|
<p className="mb-1 text-sm font-bold text-amber-800">
|
||||||
|
⏳ Menunggu review admin
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-neutral-700">
|
||||||
|
Pengajuanmu sedang diproses. Kami akan memberitahu via email setelah selesai.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verification?.status === "REJECTED" && (
|
||||||
|
<div className="mb-6 rounded-2xl border border-red-200 bg-red-50 p-5">
|
||||||
|
<p className="mb-1 text-sm font-bold text-red-800">❌ Pengajuan ditolak</p>
|
||||||
|
{verification.rejectionReason && (
|
||||||
|
<p className="text-sm text-neutral-700">
|
||||||
|
<span className="font-semibold">Alasan:</span>{" "}
|
||||||
|
{verification.rejectionReason}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p className="mt-2 text-sm text-neutral-700">
|
||||||
|
Kamu bisa memperbaiki data dan mengajukan ulang di bawah.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verification?.status !== "APPROVED" && verification?.status !== "PENDING" && (
|
||||||
|
<VerifyForm initial={verification ?? null} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="mt-6 text-center text-sm text-neutral-500">
|
||||||
|
<Link href="/profile" className="hover:text-primary-600">
|
||||||
|
← Kembali ke profil
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -50,6 +50,12 @@ export function Navbar() {
|
|||||||
>
|
>
|
||||||
Profil
|
Profil
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/verify"
|
||||||
|
className="rounded-lg px-3 py-1.5 text-sm font-medium text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-neutral-800"
|
||||||
|
>
|
||||||
|
Verifikasi
|
||||||
|
</Link>
|
||||||
<div className="ml-2 flex items-center gap-2">
|
<div className="ml-2 flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
href="/profile"
|
href="/profile"
|
||||||
@@ -136,6 +142,13 @@ export function Navbar() {
|
|||||||
>
|
>
|
||||||
Profil
|
Profil
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/verify"
|
||||||
|
onClick={() => setMenuOpen(false)}
|
||||||
|
className="rounded-lg px-3 py-2.5 text-sm font-medium text-neutral-700 transition-colors hover:bg-neutral-50"
|
||||||
|
>
|
||||||
|
Verifikasi
|
||||||
|
</Link>
|
||||||
<div className="mt-2 flex items-center justify-between rounded-lg bg-neutral-50 px-3 py-2.5">
|
<div className="mt-2 flex items-center justify-between rounded-lg bg-neutral-50 px-3 py-2.5">
|
||||||
<Link
|
<Link
|
||||||
href="/profile"
|
href="/profile"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
type Size = "sm" | "md";
|
||||||
|
|
||||||
|
export function VerifiedBadge({ size = "sm" }: { size?: Size }) {
|
||||||
|
const cls =
|
||||||
|
size === "md"
|
||||||
|
? "px-2.5 py-1 text-xs"
|
||||||
|
: "px-2 py-0.5 text-[10px]";
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center gap-1 rounded-full bg-primary-50 font-semibold text-primary-700 ring-1 ring-primary-200 ${cls}`}
|
||||||
|
title="Organizer terverifikasi SeTrip"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
className={size === "md" ? "h-3.5 w-3.5" : "h-3 w-3"}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M8 0l2.09 1.74L12.86 1.5l.64 2.78 2.5 1.5-1.5 2.5.5 2.86-2.78.64-1.5 2.5-2.72-.59L5.5 14.5 4 12 1.5 11.36 2 8.5.5 6 3 4.5l.64-2.78 2.77.24L8 0zm-1.07 9.4l4.6-4.6-1.06-1.06-3.54 3.54-1.41-1.42-1.06 1.06 2.47 2.48z" />
|
||||||
|
</svg>
|
||||||
|
Verified
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
DATABASE_URL="postgresql://setrip_user:setrip_password@localhost:5432/setrip_db"
|
||||||
|
NEXTAUTH_SECRET="3GaP/mqi1IYbafyLfyI54ouPRDE0IUK5vFqpKJQM5hg="
|
||||||
|
NEXTAUTH_URL="http://localhost:3000"
|
||||||
|
NEXT_PUBLIC_SITE_URL="https://arifal.imola.ai"
|
||||||
|
ADMIN_EMAILS=admin@setrip.id
|
||||||
@@ -9,6 +9,7 @@ export async function registerAction(formData: FormData) {
|
|||||||
email: formData.get("email") as string,
|
email: formData.get("email") as string,
|
||||||
password: formData.get("password") as string,
|
password: formData.get("password") as string,
|
||||||
confirmPassword: formData.get("confirmPassword") as string,
|
confirmPassword: formData.get("confirmPassword") as string,
|
||||||
|
acceptedTermsAndPrivacy: formData.get("acceptedTermsAndPrivacy") === "on",
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = registerSchema.safeParse(raw);
|
const result = registerSchema.safeParse(raw);
|
||||||
@@ -21,6 +22,7 @@ export async function registerAction(formData: FormData) {
|
|||||||
name: result.data.name,
|
name: result.data.name,
|
||||||
email: result.data.email,
|
email: result.data.email,
|
||||||
password: result.data.password,
|
password: result.data.password,
|
||||||
|
acceptedTermsAndPrivacy: result.data.acceptedTermsAndPrivacy,
|
||||||
});
|
});
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ export const registerSchema = z.object({
|
|||||||
.min(6, "Password minimal 6 karakter")
|
.min(6, "Password minimal 6 karakter")
|
||||||
.max(LIMITS.MAX_PASSWORD_LENGTH, "Password terlalu panjang (maks. 72 karakter)"),
|
.max(LIMITS.MAX_PASSWORD_LENGTH, "Password terlalu panjang (maks. 72 karakter)"),
|
||||||
confirmPassword: z.string(),
|
confirmPassword: z.string(),
|
||||||
|
acceptedTermsAndPrivacy: z.literal(true, {
|
||||||
|
error: "Kamu harus menyetujui Syarat & Ketentuan dan Kebijakan Privasi",
|
||||||
|
}),
|
||||||
}).refine((data) => data.password === data.confirmPassword, {
|
}).refine((data) => data.password === data.confirmPassword, {
|
||||||
message: "Password tidak cocok",
|
message: "Password tidak cocok",
|
||||||
path: ["confirmPassword"],
|
path: ["confirmPassword"],
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { getServerSession } from "next-auth";
|
||||||
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { authOptions } from "@/lib/auth";
|
||||||
|
import { isAdminEmail } from "@/lib/admin";
|
||||||
|
import { organizerService } from "@/server/services/organizer.service";
|
||||||
|
import { submitVerificationSchema, reviewVerificationSchema } from "./schemas";
|
||||||
|
|
||||||
|
export async function submitVerificationAction(formData: FormData) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user) {
|
||||||
|
return { error: "Kamu harus login terlebih dahulu" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = {
|
||||||
|
fullName: formData.get("fullName") as string,
|
||||||
|
nik: formData.get("nik") as string,
|
||||||
|
birthDate: formData.get("birthDate") as string,
|
||||||
|
address: formData.get("address") as string,
|
||||||
|
ktpImageUrl: formData.get("ktpImageUrl") as string,
|
||||||
|
selfieUrl: formData.get("selfieUrl") as string,
|
||||||
|
bankName: formData.get("bankName") as string,
|
||||||
|
bankAccountNumber: formData.get("bankAccountNumber") as string,
|
||||||
|
bankAccountName: formData.get("bankAccountName") as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = submitVerificationSchema.safeParse(raw);
|
||||||
|
if (!result.success) {
|
||||||
|
return { error: result.error.issues[0].message };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await organizerService.submitVerification(session.user.id, {
|
||||||
|
...result.data,
|
||||||
|
birthDate: new Date(result.data.birthDate),
|
||||||
|
});
|
||||||
|
revalidatePath("/verify");
|
||||||
|
revalidatePath("/profile");
|
||||||
|
revalidatePath("/admin/verifications");
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
return { error: (err as Error).message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reviewVerificationAction(formData: FormData) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
if (!session?.user || !isAdminEmail(session.user.email)) {
|
||||||
|
return { error: "Tidak memiliki akses admin" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = {
|
||||||
|
verificationId: formData.get("verificationId") as string,
|
||||||
|
decision: formData.get("decision") as string,
|
||||||
|
rejectionReason: (formData.get("rejectionReason") as string) || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = reviewVerificationSchema.safeParse(raw);
|
||||||
|
if (!result.success) {
|
||||||
|
return { error: result.error.issues[0].message };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await organizerService.reviewVerification({
|
||||||
|
verificationId: result.data.verificationId,
|
||||||
|
decision: result.data.decision,
|
||||||
|
rejectionReason: result.data.rejectionReason,
|
||||||
|
reviewerId: session.user.id,
|
||||||
|
});
|
||||||
|
revalidatePath("/admin/verifications");
|
||||||
|
revalidatePath("/verify");
|
||||||
|
revalidatePath("/profile");
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
return { error: (err as Error).message };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { reviewVerificationAction } from "@/features/organizer/actions";
|
||||||
|
|
||||||
|
type Verification = {
|
||||||
|
id: string;
|
||||||
|
fullName: string;
|
||||||
|
nik: string;
|
||||||
|
birthDate: Date;
|
||||||
|
address: string;
|
||||||
|
ktpImageUrl: string;
|
||||||
|
selfieUrl: string;
|
||||||
|
bankName: string;
|
||||||
|
bankAccountNumber: string;
|
||||||
|
bankAccountName: string;
|
||||||
|
status: "PENDING" | "APPROVED" | "REJECTED";
|
||||||
|
rejectionReason: string | null;
|
||||||
|
reviewedAt: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
user: { id: string; name: string; email: string };
|
||||||
|
reviewedBy: { id: string; name: string; email: string } | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatDate(d: Date): string {
|
||||||
|
return new Date(d).toLocaleString("id-ID", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReviewCard({ verification }: { verification: Verification }) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [showReject, setShowReject] = useState(false);
|
||||||
|
const [rejectionReason, setRejectionReason] = useState("");
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
async function decide(decision: "APPROVED" | "REJECTED") {
|
||||||
|
setError("");
|
||||||
|
setLoading(true);
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.set("verificationId", verification.id);
|
||||||
|
fd.set("decision", decision);
|
||||||
|
if (decision === "REJECTED") fd.set("rejectionReason", rejectionReason);
|
||||||
|
const result = await reviewVerificationAction(fd);
|
||||||
|
setLoading(false);
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setShowReject(false);
|
||||||
|
setRejectionReason("");
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="rounded-2xl border border-neutral-200 bg-white p-5 shadow-sm sm:p-6">
|
||||||
|
<header className="mb-4 flex flex-wrap items-start justify-between gap-3 border-b border-neutral-100 pb-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-base font-bold text-neutral-900">
|
||||||
|
{verification.user.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-neutral-500">
|
||||||
|
{verification.user.email} · diajukan {formatDate(verification.createdAt)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<StatusPill status={verification.status} />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
<Field label="Nama Lengkap" value={verification.fullName} />
|
||||||
|
<Field label="NIK" value={verification.nik} mono />
|
||||||
|
<Field
|
||||||
|
label="Tanggal Lahir"
|
||||||
|
value={formatDate(verification.birthDate)}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
label="Bank"
|
||||||
|
value={`${verification.bankName} · ${verification.bankAccountNumber}`}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
label="Pemilik Rekening"
|
||||||
|
value={verification.bankAccountName}
|
||||||
|
/>
|
||||||
|
<Field label="Alamat" value={verification.address} fullWidth />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid gap-4 sm:grid-cols-2">
|
||||||
|
<ImagePreview label="Foto KTP" url={verification.ktpImageUrl} />
|
||||||
|
<ImagePreview label="Selfie + KTP" url={verification.selfieUrl} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{verification.status === "REJECTED" && verification.rejectionReason && (
|
||||||
|
<div className="mt-4 rounded-xl bg-red-50 p-3 text-sm text-red-700">
|
||||||
|
<span className="font-semibold">Alasan ditolak:</span>{" "}
|
||||||
|
{verification.rejectionReason}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verification.reviewedBy && verification.reviewedAt && (
|
||||||
|
<p className="mt-4 text-xs text-neutral-500">
|
||||||
|
Diproses oleh {verification.reviewedBy.name} pada{" "}
|
||||||
|
{formatDate(verification.reviewedAt)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verification.status === "PENDING" && (
|
||||||
|
<div className="mt-5 border-t border-neutral-100 pt-4">
|
||||||
|
{error && (
|
||||||
|
<div className="mb-3 rounded-lg bg-red-50 px-3 py-2 text-xs text-red-600">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!showReject ? (
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => decide("APPROVED")}
|
||||||
|
disabled={loading}
|
||||||
|
className="rounded-xl bg-primary-600 px-4 py-2 text-sm font-bold text-white hover:bg-primary-700 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
✅ Setujui
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowReject(true)}
|
||||||
|
disabled={loading}
|
||||||
|
className="rounded-xl border border-red-200 bg-white px-4 py-2 text-sm font-bold text-red-600 hover:bg-red-50 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
❌ Tolak
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<textarea
|
||||||
|
value={rejectionReason}
|
||||||
|
onChange={(e) => setRejectionReason(e.target.value)}
|
||||||
|
rows={2}
|
||||||
|
placeholder="Alasan penolakan (akan dilihat user)"
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-3 py-2 text-sm focus:bg-white"
|
||||||
|
/>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => decide("REJECTED")}
|
||||||
|
disabled={loading || !rejectionReason.trim()}
|
||||||
|
className="rounded-xl bg-red-600 px-4 py-2 text-sm font-bold text-white hover:bg-red-700 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Konfirmasi Tolak
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setShowReject(false);
|
||||||
|
setRejectionReason("");
|
||||||
|
}}
|
||||||
|
className="rounded-xl border border-neutral-200 bg-white px-4 py-2 text-sm font-medium text-neutral-600 hover:bg-neutral-50"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Field({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
mono,
|
||||||
|
fullWidth,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
mono?: boolean;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={fullWidth ? "sm:col-span-2" : ""}>
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-wide text-neutral-500">
|
||||||
|
{label}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
className={`mt-0.5 text-sm text-neutral-800 ${mono ? "font-mono" : ""}`}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImagePreview({ label, url }: { label: string; url: string }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p className="mb-1.5 text-xs font-semibold uppercase tracking-wide text-neutral-500">
|
||||||
|
{label}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block overflow-hidden rounded-xl border border-neutral-200 bg-neutral-100"
|
||||||
|
>
|
||||||
|
<div className="relative aspect-[4/3] w-full">
|
||||||
|
<Image
|
||||||
|
src={url}
|
||||||
|
alt={label}
|
||||||
|
fill
|
||||||
|
unoptimized
|
||||||
|
className="object-cover"
|
||||||
|
sizes="(min-width: 640px) 50vw, 100vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusPill({ status }: { status: "PENDING" | "APPROVED" | "REJECTED" }) {
|
||||||
|
const cfg = {
|
||||||
|
PENDING: { label: "Pending", cls: "bg-amber-50 text-amber-700 ring-amber-200" },
|
||||||
|
APPROVED: { label: "Disetujui", cls: "bg-primary-50 text-primary-700 ring-primary-200" },
|
||||||
|
REJECTED: { label: "Ditolak", cls: "bg-red-50 text-red-700 ring-red-200" },
|
||||||
|
}[status];
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ring-1 ${cfg.cls}`}
|
||||||
|
>
|
||||||
|
{cfg.label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { submitVerificationAction } from "@/features/organizer/actions";
|
||||||
|
|
||||||
|
type Initial = {
|
||||||
|
fullName: string;
|
||||||
|
nik: string;
|
||||||
|
birthDate: Date;
|
||||||
|
address: string;
|
||||||
|
ktpImageUrl: string;
|
||||||
|
selfieUrl: string;
|
||||||
|
bankName: string;
|
||||||
|
bankAccountNumber: string;
|
||||||
|
bankAccountName: string;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
function toYmd(d: Date): string {
|
||||||
|
const y = d.getUTCFullYear();
|
||||||
|
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(d.getUTCDate()).padStart(2, "0");
|
||||||
|
return `${y}-${m}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VerifyForm({ initial }: { initial: Initial }) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
|
e.preventDefault();
|
||||||
|
setError("");
|
||||||
|
setLoading(true);
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
const result = await submitVerificationAction(formData);
|
||||||
|
setLoading(false);
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputCls =
|
||||||
|
"w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 transition-colors placeholder:text-neutral-400 focus:bg-white";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="space-y-5 rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm sm:p-8"
|
||||||
|
>
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-xl bg-red-50 px-4 py-3 text-sm font-medium text-red-600">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-base font-bold text-neutral-900">📇 Data KTP</h2>
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Nama Lengkap (sesuai KTP)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="fullName"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.fullName ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="Mis. Budi Santoso"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
NIK (16 digit)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="nik"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
pattern="\d{16}"
|
||||||
|
maxLength={16}
|
||||||
|
required
|
||||||
|
defaultValue={initial?.nik ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="3201xxxxxxxxxxxx"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Tanggal Lahir
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="birthDate"
|
||||||
|
type="date"
|
||||||
|
required
|
||||||
|
defaultValue={initial ? toYmd(new Date(initial.birthDate)) : ""}
|
||||||
|
className={inputCls}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Alamat (sesuai KTP)
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
name="address"
|
||||||
|
required
|
||||||
|
rows={3}
|
||||||
|
defaultValue={initial?.address ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="Jalan, RT/RW, kelurahan, kota, provinsi"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-base font-bold text-neutral-900">🖼️ Foto</h2>
|
||||||
|
<p className="mb-3 text-xs text-neutral-500">
|
||||||
|
Upload foto ke hosting (imgur, imgbb, dll) lalu paste URL-nya. Foto akan
|
||||||
|
dilihat tim admin saat review.
|
||||||
|
</p>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
URL Foto KTP
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="ktpImageUrl"
|
||||||
|
type="url"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.ktpImageUrl ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="https://i.imgur.com/xxxx.jpg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
URL Selfie dengan KTP
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="selfieUrl"
|
||||||
|
type="url"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.selfieUrl ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="https://i.imgur.com/yyyy.jpg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="mb-3 text-base font-bold text-neutral-900">🏦 Rekening Bank</h2>
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Nama Bank
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="bankName"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.bankName ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="Mis. BCA, Mandiri, BRI"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Nomor Rekening
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="bankAccountNumber"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.bankAccountNumber ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="1234567890"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="sm:col-span-2">
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Nama Pemilik Rekening
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="bankAccountName"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
defaultValue={initial?.bankAccountName ?? ""}
|
||||||
|
className={inputCls}
|
||||||
|
placeholder="Sesuai buku tabungan"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
className="w-full rounded-xl bg-primary-600 py-2.5 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? "Mengirim..." : "Ajukan Verifikasi"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-center text-xs text-neutral-500">
|
||||||
|
Data KTP & rekening hanya digunakan untuk verifikasi dan tidak akan
|
||||||
|
ditampilkan ke pengguna lain.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import { LIMITS } from "@/lib/limits";
|
||||||
|
|
||||||
|
const ymdRegex = /^\d{4}-\d{2}-\d{2}$/;
|
||||||
|
|
||||||
|
export const submitVerificationSchema = z.object({
|
||||||
|
fullName: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(2, "Nama lengkap minimal 2 karakter")
|
||||||
|
.max(LIMITS.MAX_NAME_LENGTH, `Nama maksimal ${LIMITS.MAX_NAME_LENGTH} karakter`),
|
||||||
|
nik: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.regex(/^\d{16}$/, "NIK harus 16 digit angka"),
|
||||||
|
birthDate: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.regex(ymdRegex, "Format tanggal lahir harus YYYY-MM-DD"),
|
||||||
|
address: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(5, "Alamat minimal 5 karakter")
|
||||||
|
.max(LIMITS.MAX_ADDRESS_LENGTH, `Alamat maksimal ${LIMITS.MAX_ADDRESS_LENGTH} karakter`),
|
||||||
|
ktpImageUrl: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Foto KTP wajib diisi")
|
||||||
|
.max(LIMITS.MAX_URL_LENGTH, "URL foto KTP terlalu panjang")
|
||||||
|
.pipe(z.url("URL foto KTP tidak valid")),
|
||||||
|
selfieUrl: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Foto selfie dengan KTP wajib diisi")
|
||||||
|
.max(LIMITS.MAX_URL_LENGTH, "URL foto selfie terlalu panjang")
|
||||||
|
.pipe(z.url("URL foto selfie tidak valid")),
|
||||||
|
bankName: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(2, "Nama bank minimal 2 karakter")
|
||||||
|
.max(LIMITS.MAX_BANK_NAME_LENGTH, `Nama bank maksimal ${LIMITS.MAX_BANK_NAME_LENGTH} karakter`),
|
||||||
|
bankAccountNumber: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.regex(/^[0-9-]+$/, "Nomor rekening hanya boleh angka")
|
||||||
|
.min(5, "Nomor rekening minimal 5 digit")
|
||||||
|
.max(LIMITS.MAX_BANK_ACCOUNT_NUMBER_LENGTH, "Nomor rekening terlalu panjang"),
|
||||||
|
bankAccountName: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(2, "Nama pemilik rekening minimal 2 karakter")
|
||||||
|
.max(LIMITS.MAX_NAME_LENGTH, `Nama pemilik rekening maksimal ${LIMITS.MAX_NAME_LENGTH} karakter`),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reviewVerificationSchema = z.object({
|
||||||
|
verificationId: z.string().min(1, "Verification ID wajib"),
|
||||||
|
decision: z.enum(["APPROVED", "REJECTED"], {
|
||||||
|
error: "Keputusan tidak valid",
|
||||||
|
}),
|
||||||
|
rejectionReason: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.max(LIMITS.MAX_REJECTION_REASON_LENGTH, "Alasan penolakan terlalu panjang")
|
||||||
|
.optional(),
|
||||||
|
}).refine(
|
||||||
|
(data) => data.decision !== "REJECTED" || (data.rejectionReason && data.rejectionReason.length > 0),
|
||||||
|
{ message: "Alasan penolakan wajib diisi jika menolak", path: ["rejectionReason"] }
|
||||||
|
);
|
||||||
|
|
||||||
|
export type SubmitVerificationInput = z.infer<typeof submitVerificationSchema>;
|
||||||
|
export type ReviewVerificationInput = z.infer<typeof reviewVerificationSchema>;
|
||||||
@@ -4,6 +4,7 @@ import { getServerSession } from "next-auth";
|
|||||||
import { authOptions } from "@/lib/auth";
|
import { authOptions } from "@/lib/auth";
|
||||||
import { createTripSchema, tripImageUrlsSchema } from "./schemas";
|
import { createTripSchema, tripImageUrlsSchema } from "./schemas";
|
||||||
import { tripService } from "@/server/services/trip.service";
|
import { tripService } from "@/server/services/trip.service";
|
||||||
|
import { organizerService } from "@/server/services/organizer.service";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
import { tripStoredInstantFromYmd } from "@/lib/trip-dates";
|
import { tripStoredInstantFromYmd } from "@/lib/trip-dates";
|
||||||
|
|
||||||
@@ -33,6 +34,16 @@ export async function createTripAction(formData: FormData) {
|
|||||||
return { error: result.error.issues[0].message };
|
return { error: result.error.issues[0].message };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.data.price > 0) {
|
||||||
|
const approved = await organizerService.isApproved(session.user.id);
|
||||||
|
if (!approved) {
|
||||||
|
return {
|
||||||
|
error:
|
||||||
|
"Untuk membuat trip berbayar, akun kamu perlu diverifikasi. Silakan lengkapi verifikasi di /verify.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const imageUrlsRaw = formData
|
const imageUrlsRaw = formData
|
||||||
.getAll("imageUrls")
|
.getAll("imageUrls")
|
||||||
.map((v) => (v as string).trim())
|
.map((v) => (v as string).trim())
|
||||||
|
|||||||
@@ -0,0 +1,362 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import DatePicker from "react-datepicker";
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
import { createTripAction } from "@/features/trip/actions";
|
||||||
|
import { ImageUrlInput } from "@/features/trip/components/image-url-input";
|
||||||
|
import { formatLocalCalendarYmd } from "@/lib/trip-dates";
|
||||||
|
|
||||||
|
const SAMPLE_MOUNTAINS = [
|
||||||
|
{ name: "Gunung Papandayan", location: "Garut, Jawa Barat" },
|
||||||
|
{ name: "Gunung Ciremai", location: "Kuningan, Jawa Barat" },
|
||||||
|
{ name: "Gunung Pangrango", location: "Bogor/Cianjur, Jawa Barat" },
|
||||||
|
{ name: "Gunung Gede", location: "Bogor/Cianjur, Jawa Barat" },
|
||||||
|
{ name: "Gunung Tangkuban Parahu", location: "Bandung, Jawa Barat" },
|
||||||
|
{ name: "Gunung Bukit Tunggul", location: "Bandung, Jawa Barat" },
|
||||||
|
{ name: "Gunung Malabar", location: "Bandung, Jawa Barat" },
|
||||||
|
{ name: "Gunung Guntur", location: "Garut, Jawa Barat" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatRupiahInput(value: string): string {
|
||||||
|
const num = value.replace(/\D/g, "");
|
||||||
|
return num.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRupiahInput(value: string): string {
|
||||||
|
return value.replace(/\./g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateTripFormProps {
|
||||||
|
isVerifiedOrganizer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreateTripForm({ isVerifiedOrganizer }: CreateTripFormProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const [startDate, setStartDate] = useState<Date | null>(null);
|
||||||
|
const [endDate, setEndDate] = useState<Date | null>(null);
|
||||||
|
const [priceDisplay, setPriceDisplay] = useState("");
|
||||||
|
|
||||||
|
const priceNumber = Number(parseRupiahInput(priceDisplay) || "0");
|
||||||
|
const isPaidTrip = priceNumber > 0;
|
||||||
|
const blockedByVerification = isPaidTrip && !isVerifiedOrganizer;
|
||||||
|
|
||||||
|
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
|
e.preventDefault();
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
if (!startDate) {
|
||||||
|
setError("Tanggal berangkat harus diisi");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
formData.set("date", formatLocalCalendarYmd(startDate));
|
||||||
|
if (endDate) {
|
||||||
|
const startYmd = formatLocalCalendarYmd(startDate);
|
||||||
|
const endYmd = formatLocalCalendarYmd(endDate);
|
||||||
|
if (endYmd !== startYmd) {
|
||||||
|
formData.set("endDate", endYmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formData.set("price", parseRupiahInput(priceDisplay));
|
||||||
|
|
||||||
|
const result = await createTripAction(formData);
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error);
|
||||||
|
} else if (result.tripId) {
|
||||||
|
router.push(`/trips/${result.tripId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMountainSelect(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
const selected = SAMPLE_MOUNTAINS.find((m) => m.name === e.target.value);
|
||||||
|
if (selected) {
|
||||||
|
const form = e.target.form;
|
||||||
|
if (form) {
|
||||||
|
const mountainInput = form.elements.namedItem(
|
||||||
|
"mountain"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const locationInput = form.elements.namedItem(
|
||||||
|
"location"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
mountainInput.value = selected.name;
|
||||||
|
locationInput.value = selected.location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDateChange(dates: [Date | null, Date | null]) {
|
||||||
|
const [start, end] = dates;
|
||||||
|
setStartDate(start);
|
||||||
|
setEndDate(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePriceChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const raw = e.target.value.replace(/\D/g, "");
|
||||||
|
setPriceDisplay(raw ? formatRupiahInput(raw) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm">
|
||||||
|
{error && (
|
||||||
|
<div className="mb-4 rounded-xl bg-red-50 px-4 py-3 text-sm font-medium text-red-600">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
|
{/* Mountain Quick Picker */}
|
||||||
|
<div className="rounded-xl bg-primary-50 p-4">
|
||||||
|
<label className="mb-2 flex items-center gap-1.5 text-sm font-bold text-primary-800">
|
||||||
|
<span>🏔️</span> Pilih Gunung Jawa Barat
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
onChange={handleMountainSelect}
|
||||||
|
className="w-full rounded-lg border border-primary-200 bg-white px-4 py-2.5 text-sm text-neutral-800"
|
||||||
|
defaultValue=""
|
||||||
|
>
|
||||||
|
<option value="" disabled>
|
||||||
|
Pilih gunung...
|
||||||
|
</option>
|
||||||
|
{SAMPLE_MOUNTAINS.map((m) => (
|
||||||
|
<option key={m.name} value={m.name}>
|
||||||
|
{m.name} — {m.location}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="title" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Judul Trip
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="title"
|
||||||
|
name="title"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="contoh: Open Trip Papandayan Weekend"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="mountain" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Nama Gunung
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="mountain"
|
||||||
|
name="mountain"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="Gunung Papandayan"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="location" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Lokasi
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="location"
|
||||||
|
name="location"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="Garut, Jawa Barat"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="description" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Deskripsi
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
|
rows={4}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="Ringkasan trip, vibe, level kesulitan..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="meetingPoint" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Meeting point
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="meetingPoint"
|
||||||
|
name="meetingPoint"
|
||||||
|
type="text"
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="contoh: Alfamart Cicaheum, 05:00 WIB"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="itinerary" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Itinerary
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="itinerary"
|
||||||
|
name="itinerary"
|
||||||
|
rows={5}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder={"Hari 1: …\nHari 2: …"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="whatsIncluded" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Termasuk
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="whatsIncluded"
|
||||||
|
name="whatsIncluded"
|
||||||
|
rows={4}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="Transport, konsumsi, tenda, …"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="whatsExcluded" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Tidak termasuk
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="whatsExcluded"
|
||||||
|
name="whatsExcluded"
|
||||||
|
rows={4}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="Tiket masuk TN, sleeping bag, …"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ImageUrlInput />
|
||||||
|
|
||||||
|
{/* Date Range & Participants & Price */}
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
{/* Date Range Picker */}
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Tanggal berangkat — pulang
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-1/2 z-10 -translate-y-1/2 text-neutral-400">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
className="h-4 w-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<DatePicker
|
||||||
|
selectsRange
|
||||||
|
startDate={startDate}
|
||||||
|
endDate={endDate}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
minDate={new Date()}
|
||||||
|
placeholderText="Pilih tanggal..."
|
||||||
|
dateFormat="dd MMM yyyy"
|
||||||
|
isClearable
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-9 pr-3 text-sm text-neutral-800 placeholder:text-neutral-400 focus:border-primary-500 focus:bg-white"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Max Participants */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="maxParticipants" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Maks Peserta
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-neutral-400">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
className="h-4 w-4"
|
||||||
|
>
|
||||||
|
<path d="M7 8a3 3 0 100-6 3 3 0 000 6zM14.5 9a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM1.615 16.428a1.224 1.224 0 01-.569-1.175 6.002 6.002 0 0111.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 017 18a9.953 9.953 0 01-5.385-1.572zM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 00-1.588-3.755 4.502 4.502 0 015.874 2.636.818.818 0 01-.36.98A7.465 7.465 0 0114.5 16z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
id="maxParticipants"
|
||||||
|
name="maxParticipants"
|
||||||
|
type="number"
|
||||||
|
required
|
||||||
|
min={1}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-9 pr-4 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="10"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Price with Rp format */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="priceDisplay" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||||
|
Harga per Orang
|
||||||
|
{!isVerifiedOrganizer && (
|
||||||
|
<span className="ml-2 text-xs font-normal text-neutral-500">
|
||||||
|
(isi 0 untuk trip gratis)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-sm font-semibold text-neutral-500">
|
||||||
|
Rp
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
id="priceDisplay"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
required
|
||||||
|
value={priceDisplay}
|
||||||
|
onChange={handlePriceChange}
|
||||||
|
className="w-full rounded-xl border border-neutral-200 bg-neutral-50 py-2.5 pl-10 pr-4 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||||
|
placeholder="150.000"
|
||||||
|
/>
|
||||||
|
<input type="hidden" name="price" value={parseRupiahInput(priceDisplay)} />
|
||||||
|
</div>
|
||||||
|
{blockedByVerification && (
|
||||||
|
<p className="mt-2 text-xs font-medium text-amber-700">
|
||||||
|
⚠️ Trip berbayar butuh verifikasi organizer terlebih dahulu.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading || blockedByVerification}
|
||||||
|
className="w-full rounded-xl bg-primary-600 py-3 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading
|
||||||
|
? "Membuat Trip..."
|
||||||
|
: blockedByVerification
|
||||||
|
? "Verifikasi diperlukan untuk trip berbayar"
|
||||||
|
: "Buat Trip"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -38,8 +38,11 @@ export function OrganizerTrustPanel({
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-1.5 flex flex-wrap gap-1.5">
|
<div className="mt-1.5 flex flex-wrap gap-1.5">
|
||||||
{trust.isVerified && (
|
{trust.isVerified && (
|
||||||
<span className="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-blue-800 sm:text-xs">
|
<span
|
||||||
Verified
|
className="inline-flex items-center gap-1 rounded-full bg-primary-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary-800 sm:text-xs"
|
||||||
|
title="Identitas organizer telah diverifikasi (KTP & rekening)"
|
||||||
|
>
|
||||||
|
✅ Verified Organizer
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{trust.isTripLeader && (
|
{trust.isTripLeader && (
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/** Daftar email admin (comma-separated, lowercase). */
|
||||||
|
function adminEmails(): string[] {
|
||||||
|
return (process.env.ADMIN_EMAILS ?? "")
|
||||||
|
.split(",")
|
||||||
|
.map((e) => e.trim().toLowerCase())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAdminEmail(email: string | null | undefined): boolean {
|
||||||
|
if (!email) return false;
|
||||||
|
return adminEmails().includes(email.toLowerCase());
|
||||||
|
}
|
||||||
@@ -18,4 +18,10 @@ export const LIMITS = {
|
|||||||
MAX_URL_LENGTH: 2048,
|
MAX_URL_LENGTH: 2048,
|
||||||
MAX_NAME_LENGTH: 80,
|
MAX_NAME_LENGTH: 80,
|
||||||
MAX_PASSWORD_LENGTH: 72,
|
MAX_PASSWORD_LENGTH: 72,
|
||||||
|
/** Verifikasi organizer (KTP + bank) */
|
||||||
|
MAX_ADDRESS_LENGTH: 500,
|
||||||
|
MAX_BANK_NAME_LENGTH: 60,
|
||||||
|
MAX_BANK_ACCOUNT_NUMBER_LENGTH: 32,
|
||||||
|
MAX_REJECTION_REASON_LENGTH: 500,
|
||||||
|
NIK_LENGTH: 16,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -1,2 +1,14 @@
|
|||||||
/** Minimal trip sebagai organizer untuk badge "Trip leader" (heuristik MVP). */
|
/** Minimal trip sebagai organizer untuk badge "Trip leader" (heuristik MVP). */
|
||||||
export const TRIP_LEADER_MIN_TRIPS = 2;
|
export const TRIP_LEADER_MIN_TRIPS = 2;
|
||||||
|
|
||||||
|
/** Bentuk data minimal untuk cek status verifikasi organizer. */
|
||||||
|
type WithOrganizerVerification = {
|
||||||
|
organizerVerification?: { status: "PENDING" | "APPROVED" | "REJECTED" } | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** True kalau user punya OrganizerVerification berstatus APPROVED. */
|
||||||
|
export function isVerifiedOrganizer(
|
||||||
|
user: WithOrganizerVerification | null | undefined
|
||||||
|
): boolean {
|
||||||
|
return user?.organizerVerification?.status === "APPROVED";
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" ADD COLUMN "acceptedAt" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "acceptedTermsAndPrivacy" BOOLEAN NOT NULL DEFAULT false;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "VerificationStatus" AS ENUM ('PENDING', 'APPROVED', 'REJECTED');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "OrganizerVerification" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"fullName" TEXT NOT NULL,
|
||||||
|
"nik" TEXT NOT NULL,
|
||||||
|
"birthDate" TIMESTAMP(3) NOT NULL,
|
||||||
|
"address" TEXT NOT NULL,
|
||||||
|
"ktpImageUrl" TEXT NOT NULL,
|
||||||
|
"selfieUrl" TEXT NOT NULL,
|
||||||
|
"bankName" TEXT NOT NULL,
|
||||||
|
"bankAccountNumber" TEXT NOT NULL,
|
||||||
|
"bankAccountName" TEXT NOT NULL,
|
||||||
|
"status" "VerificationStatus" NOT NULL DEFAULT 'PENDING',
|
||||||
|
"rejectionReason" TEXT,
|
||||||
|
"reviewedAt" TIMESTAMP(3),
|
||||||
|
"reviewedById" TEXT,
|
||||||
|
"verifiedAt" TIMESTAMP(3),
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "OrganizerVerification_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "OrganizerVerification_userId_key" ON "OrganizerVerification"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "OrganizerVerification_nik_key" ON "OrganizerVerification"("nik");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "OrganizerVerification" ADD CONSTRAINT "OrganizerVerification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "OrganizerVerification" ADD CONSTRAINT "OrganizerVerification_reviewedById_fkey" FOREIGN KEY ("reviewedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `isVerified` on the `User` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" DROP COLUMN "isVerified";
|
||||||
+45
-2
@@ -13,14 +13,57 @@ model User {
|
|||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
image String?
|
image String?
|
||||||
/// Akun diverifikasi tim SeTrip (manual / admin) — tampil sebagai badge kepercayaan
|
/// Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||||
isVerified Boolean @default(false)
|
acceptedTermsAndPrivacy Boolean @default(false)
|
||||||
|
/// Waktu user menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||||
|
acceptedAt DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
trips Trip[]
|
trips Trip[]
|
||||||
participations TripParticipant[]
|
participations TripParticipant[]
|
||||||
tripReviews TripReview[]
|
tripReviews TripReview[]
|
||||||
|
|
||||||
|
organizerVerification OrganizerVerification? @relation("OrganizerVerificationOwner")
|
||||||
|
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
|
||||||
|
}
|
||||||
|
|
||||||
|
model OrganizerVerification {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String @unique
|
||||||
|
user User @relation("OrganizerVerificationOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
/// Nama lengkap sesuai KTP
|
||||||
|
fullName String
|
||||||
|
/// Nomor Induk Kependudukan (PII — perlakukan sensitif)
|
||||||
|
nik String @unique
|
||||||
|
birthDate DateTime
|
||||||
|
address String
|
||||||
|
|
||||||
|
/// URL foto KTP (untuk MVP pakai hosting; pindah ke storage privat untuk produksi)
|
||||||
|
ktpImageUrl String
|
||||||
|
/// URL selfie memegang KTP
|
||||||
|
selfieUrl String
|
||||||
|
|
||||||
|
bankName String
|
||||||
|
bankAccountNumber String
|
||||||
|
bankAccountName String
|
||||||
|
|
||||||
|
status VerificationStatus @default(PENDING)
|
||||||
|
rejectionReason String?
|
||||||
|
reviewedAt DateTime?
|
||||||
|
reviewedById String?
|
||||||
|
reviewedBy User? @relation("OrganizerVerificationReviewer", fields: [reviewedById], references: [id])
|
||||||
|
verifiedAt DateTime?
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VerificationStatus {
|
||||||
|
PENDING
|
||||||
|
APPROVED
|
||||||
|
REJECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
model Trip {
|
model Trip {
|
||||||
|
|||||||
+40
-2
@@ -16,6 +16,7 @@ async function main() {
|
|||||||
await prisma.tripParticipant.deleteMany();
|
await prisma.tripParticipant.deleteMany();
|
||||||
await prisma.tripImage.deleteMany();
|
await prisma.tripImage.deleteMany();
|
||||||
await prisma.trip.deleteMany();
|
await prisma.trip.deleteMany();
|
||||||
|
await prisma.organizerVerification.deleteMany();
|
||||||
await prisma.user.deleteMany();
|
await prisma.user.deleteMany();
|
||||||
|
|
||||||
// ==================== USERS ====================
|
// ==================== USERS ====================
|
||||||
@@ -28,7 +29,6 @@ async function main() {
|
|||||||
name: "Dede Inoen",
|
name: "Dede Inoen",
|
||||||
email: "dede.inoen@setrip.id",
|
email: "dede.inoen@setrip.id",
|
||||||
password,
|
password,
|
||||||
isVerified: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ async function main() {
|
|||||||
name: "Panji Petualang",
|
name: "Panji Petualang",
|
||||||
email: "panji@setrip.id",
|
email: "panji@setrip.id",
|
||||||
password,
|
password,
|
||||||
isVerified: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,6 +94,45 @@ async function main() {
|
|||||||
console.log(" Peserta: budi, sari, doni, maya, raka @gmail.com");
|
console.log(" Peserta: budi, sari, doni, maya, raka @gmail.com");
|
||||||
console.log(" Password semua: password123\n");
|
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 ====================
|
// ==================== TRIPS + IMAGES ====================
|
||||||
/**
|
/**
|
||||||
* Tanggal disimpan eksplisit di UTC agar filter `from`/`to` (YYYY-MM-DD UTC)
|
* Tanggal disimpan eksplisit di UTC agar filter `from`/`to` (YYYY-MM-DD UTC)
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { prisma } from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@/app/generated/prisma/client";
|
||||||
|
|
||||||
|
export const organizerRepo = {
|
||||||
|
async findByUserId(userId: string) {
|
||||||
|
return prisma.organizerVerification.findUnique({ where: { userId } });
|
||||||
|
},
|
||||||
|
|
||||||
|
async findById(id: string) {
|
||||||
|
return prisma.organizerVerification.findUnique({ where: { id } });
|
||||||
|
},
|
||||||
|
|
||||||
|
async findByNik(nik: string) {
|
||||||
|
return prisma.organizerVerification.findUnique({ where: { nik } });
|
||||||
|
},
|
||||||
|
|
||||||
|
async upsertForUser(
|
||||||
|
userId: string,
|
||||||
|
data: Omit<Prisma.OrganizerVerificationUncheckedCreateInput, "id" | "userId" | "createdAt" | "updatedAt">
|
||||||
|
) {
|
||||||
|
return prisma.organizerVerification.upsert({
|
||||||
|
where: { userId },
|
||||||
|
create: { userId, ...data },
|
||||||
|
update: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async listByStatus(status?: "PENDING" | "APPROVED" | "REJECTED") {
|
||||||
|
return prisma.organizerVerification.findMany({
|
||||||
|
where: status ? { status } : undefined,
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
include: {
|
||||||
|
user: { select: { id: true, name: true, email: true } },
|
||||||
|
reviewedBy: { select: { id: true, name: true, email: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateReview(
|
||||||
|
id: string,
|
||||||
|
data: {
|
||||||
|
status: "APPROVED" | "REJECTED";
|
||||||
|
rejectionReason?: string | null;
|
||||||
|
reviewedById: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const now = new Date();
|
||||||
|
return prisma.organizerVerification.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
status: data.status,
|
||||||
|
rejectionReason: data.rejectionReason ?? null,
|
||||||
|
reviewedById: data.reviewedById,
|
||||||
|
reviewedAt: now,
|
||||||
|
verifiedAt: data.status === "APPROVED" ? now : null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -98,7 +98,7 @@ export const tripRepo = {
|
|||||||
name: true,
|
name: true,
|
||||||
email: true,
|
email: true,
|
||||||
image: true,
|
image: true,
|
||||||
isVerified: true,
|
organizerVerification: { select: { status: true } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
images: { orderBy: { order: "asc" } },
|
images: { orderBy: { order: "asc" } },
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ import bcrypt from "bcryptjs";
|
|||||||
import { userRepo } from "@/server/repositories/user.repo";
|
import { userRepo } from "@/server/repositories/user.repo";
|
||||||
|
|
||||||
export const authService = {
|
export const authService = {
|
||||||
async register(data: { name: string; email: string; password: string }) {
|
async register(data: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
acceptedTermsAndPrivacy: boolean;
|
||||||
|
}) {
|
||||||
const existing = await userRepo.findByEmail(data.email);
|
const existing = await userRepo.findByEmail(data.email);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
throw new Error("Email sudah terdaftar");
|
throw new Error("Email sudah terdaftar");
|
||||||
@@ -14,6 +19,8 @@ export const authService = {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
|
acceptedTermsAndPrivacy: data.acceptedTermsAndPrivacy,
|
||||||
|
acceptedAt: data.acceptedTermsAndPrivacy ? new Date() : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { id: user.id, name: user.name, email: user.email };
|
return { id: user.id, name: user.name, email: user.email };
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||||
|
|
||||||
|
type SubmitInput = {
|
||||||
|
fullName: string;
|
||||||
|
nik: string;
|
||||||
|
birthDate: Date;
|
||||||
|
address: string;
|
||||||
|
ktpImageUrl: string;
|
||||||
|
selfieUrl: string;
|
||||||
|
bankName: string;
|
||||||
|
bankAccountNumber: string;
|
||||||
|
bankAccountName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const organizerService = {
|
||||||
|
async submitVerification(userId: string, data: SubmitInput) {
|
||||||
|
const existing = await organizerRepo.findByUserId(userId);
|
||||||
|
if (existing && existing.status === "APPROVED") {
|
||||||
|
throw new Error("Akun kamu sudah terverifikasi");
|
||||||
|
}
|
||||||
|
if (existing && existing.status === "PENDING") {
|
||||||
|
throw new Error("Pengajuan kamu masih dalam proses review");
|
||||||
|
}
|
||||||
|
|
||||||
|
const dupNik = await organizerRepo.findByNik(data.nik);
|
||||||
|
if (dupNik && dupNik.userId !== userId) {
|
||||||
|
throw new Error("NIK ini sudah dipakai akun lain");
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizerRepo.upsertForUser(userId, {
|
||||||
|
fullName: data.fullName,
|
||||||
|
nik: data.nik,
|
||||||
|
birthDate: data.birthDate,
|
||||||
|
address: data.address,
|
||||||
|
ktpImageUrl: data.ktpImageUrl,
|
||||||
|
selfieUrl: data.selfieUrl,
|
||||||
|
bankName: data.bankName,
|
||||||
|
bankAccountNumber: data.bankAccountNumber,
|
||||||
|
bankAccountName: data.bankAccountName,
|
||||||
|
status: "PENDING",
|
||||||
|
rejectionReason: null,
|
||||||
|
reviewedAt: null,
|
||||||
|
reviewedById: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async reviewVerification(input: {
|
||||||
|
verificationId: string;
|
||||||
|
decision: "APPROVED" | "REJECTED";
|
||||||
|
rejectionReason?: string;
|
||||||
|
reviewerId: string;
|
||||||
|
}) {
|
||||||
|
const verification = await organizerRepo.findById(input.verificationId);
|
||||||
|
if (!verification) {
|
||||||
|
throw new Error("Pengajuan tidak ditemukan");
|
||||||
|
}
|
||||||
|
if (verification.status !== "PENDING") {
|
||||||
|
throw new Error("Pengajuan ini sudah diproses");
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizerRepo.updateReview(input.verificationId, {
|
||||||
|
status: input.decision,
|
||||||
|
rejectionReason: input.decision === "REJECTED" ? input.rejectionReason ?? null : null,
|
||||||
|
reviewedById: input.reviewerId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async getStatusForUser(userId: string) {
|
||||||
|
return organizerRepo.findByUserId(userId);
|
||||||
|
},
|
||||||
|
|
||||||
|
async isApproved(userId: string) {
|
||||||
|
const v = await organizerRepo.findByUserId(userId);
|
||||||
|
return v?.status === "APPROVED";
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -11,11 +11,7 @@ export type OrganizerTrust = {
|
|||||||
|
|
||||||
export const trustService = {
|
export const trustService = {
|
||||||
async getOrganizerTrust(organizerId: string): Promise<OrganizerTrust> {
|
async getOrganizerTrust(organizerId: string): Promise<OrganizerTrust> {
|
||||||
const [user, tripsCreated, reviewAgg] = await Promise.all([
|
const [tripsCreated, reviewAgg, organizerVerification] = await Promise.all([
|
||||||
prisma.user.findUnique({
|
|
||||||
where: { id: organizerId },
|
|
||||||
select: { isVerified: true },
|
|
||||||
}),
|
|
||||||
prisma.trip.count({ where: { organizerId } }),
|
prisma.trip.count({ where: { organizerId } }),
|
||||||
prisma.tripReview.aggregate({
|
prisma.tripReview.aggregate({
|
||||||
where: {
|
where: {
|
||||||
@@ -24,11 +20,15 @@ export const trustService = {
|
|||||||
_avg: { rating: true },
|
_avg: { rating: true },
|
||||||
_count: { _all: true },
|
_count: { _all: true },
|
||||||
}),
|
}),
|
||||||
|
prisma.organizerVerification.findUnique({
|
||||||
|
where: { userId: organizerId },
|
||||||
|
select: { status: true },
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const avg = reviewAgg._avg.rating;
|
const avg = reviewAgg._avg.rating;
|
||||||
return {
|
return {
|
||||||
isVerified: user?.isVerified ?? false,
|
isVerified: organizerVerification?.status === "APPROVED",
|
||||||
tripsCreated,
|
tripsCreated,
|
||||||
avgRating:
|
avgRating:
|
||||||
avg != null ? Math.round(Number(avg) * 10) / 10 : null,
|
avg != null ? Math.round(Number(avg) * 10) / 10 : null,
|
||||||
|
|||||||
Reference in New Issue
Block a user