create review and profile
This commit is contained in:
+106
-13
@@ -1,17 +1,110 @@
|
||||
import { z } from "zod/v4";
|
||||
import { LIMITS } from "@/lib/limits";
|
||||
import {
|
||||
isTripDepartureDayPast,
|
||||
tripStoredInstantFromYmd,
|
||||
} from "@/lib/trip-dates";
|
||||
|
||||
export const createTripSchema = z.object({
|
||||
title: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
description: z.string().optional(),
|
||||
mountain: z.string().min(2, "Nama gunung harus diisi"),
|
||||
location: z.string().min(2, "Lokasi harus diisi"),
|
||||
date: z.string().refine((val) => !isNaN(Date.parse(val)), "Tanggal berangkat tidak valid"),
|
||||
endDate: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine((val) => !val || !isNaN(Date.parse(val)), "Tanggal pulang tidak valid"),
|
||||
maxParticipants: z.coerce.number().min(1, "Minimal 1 peserta"),
|
||||
price: z.coerce.number().min(0, "Harga tidak valid"),
|
||||
});
|
||||
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<typeof createTripSchema>;
|
||||
|
||||
Reference in New Issue
Block a user