import { z } from "zod/v4"; import { LIMITS } from "@/lib/limits"; import { isTripDepartureDayPast, tripStoredInstantFromYmd, } from "@/lib/trip-dates"; export const tripImageUrlsSchema = z .array( z .string() .trim() .max(LIMITS.MAX_URL_LENGTH, "URL terlalu panjang") .url("Setiap URL gambar harus valid (http/https)") ) .max(LIMITS.MAX_IMAGE_URLS, `Maksimal ${LIMITS.MAX_IMAGE_URLS} foto`); export const createTripSchema = z .object({ title: z .string() .trim() .min(3, "Judul minimal 3 karakter") .max( LIMITS.MAX_TITLE_LENGTH, `Judul maksimal ${LIMITS.MAX_TITLE_LENGTH} karakter` ), description: z.preprocess( (val) => { if (val == null) return undefined; const s = String(val).trim(); return s === "" ? undefined : s; }, z .string() .max( LIMITS.MAX_DESCRIPTION_LENGTH, `Deskripsi maksimal ${LIMITS.MAX_DESCRIPTION_LENGTH} karakter` ) .optional() ), mountain: z .string() .trim() .min(2, "Nama gunung harus diisi") .max( LIMITS.MAX_MOUNTAIN_LENGTH, `Nama gunung maksimal ${LIMITS.MAX_MOUNTAIN_LENGTH} karakter` ), location: z .string() .trim() .min(2, "Lokasi harus diisi") .max( LIMITS.MAX_LOCATION_LENGTH, `Lokasi maksimal ${LIMITS.MAX_LOCATION_LENGTH} karakter` ), date: z .string() .refine((val) => !Number.isNaN(Date.parse(val)), "Tanggal berangkat tidak valid"), endDate: z .string() .optional() .refine( (val) => !val || !Number.isNaN(Date.parse(val)), "Tanggal pulang tidak valid" ), maxParticipants: z.coerce .number() .int("Jumlah peserta harus bilangan bulat") .min( LIMITS.MIN_PARTICIPANTS, `Minimal ${LIMITS.MIN_PARTICIPANTS} peserta` ) .max( LIMITS.MAX_PARTICIPANTS, `Maksimal ${LIMITS.MAX_PARTICIPANTS} peserta` ), price: z.coerce .number() .int("Harga harus bilangan bulat (tanpa desimal)") .min(0, "Harga tidak valid") .max( LIMITS.MAX_PRICE_IDR, `Harga maksimal Rp ${LIMITS.MAX_PRICE_IDR.toLocaleString("id-ID")}` ), }) .superRefine((data, ctx) => { const dep = tripStoredInstantFromYmd(data.date); if (!Number.isNaN(dep.getTime()) && isTripDepartureDayPast(dep)) { ctx.addIssue({ code: "custom", message: "Tanggal berangkat tidak boleh di masa lalu", path: ["date"], }); } if (data.endDate) { const startY = data.date.slice(0, 10); const endY = data.endDate.slice(0, 10); if (endY < startY) { ctx.addIssue({ code: "custom", message: "Tanggal pulang tidak boleh sebelum tanggal berangkat", path: ["endDate"], }); } } }); export type CreateTripInput = z.infer;