import { PortfolioAssetKind, PortfolioSectionType } from "@prisma/client"; import { z } from "zod"; import { mediaFieldInputSchema } from "./media-validation"; const requiredText = (label: string) => z .string() .trim() .min(1, `${label} is required.`); const optionalTrimmedText = z.string().trim().optional().transform((value) => value ?? ""); export const categoryInputSchema = z.object({ id: z.string().trim().optional(), slug: requiredText("Category slug") .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Category slug must be lowercase and hyphenated."), nameAr: requiredText("Category nameAr"), nameEn: requiredText("Category nameEn"), nameDe: requiredText("Category nameDe"), descriptionAr: requiredText("Category descriptionAr"), descriptionEn: requiredText("Category descriptionEn"), descriptionDe: requiredText("Category descriptionDe"), sortOrder: z.coerce.number().int().min(0).max(9999), isActive: z.boolean(), }); export const sectionInputSchema = z.object({ id: z.string().trim().optional(), type: z.nativeEnum(PortfolioSectionType), titleAr: requiredText("Section titleAr"), titleEn: requiredText("Section titleEn"), titleDe: requiredText("Section titleDe"), bodyAr: requiredText("Section bodyAr"), bodyEn: requiredText("Section bodyEn"), bodyDe: requiredText("Section bodyDe"), imagePath: optionalTrimmedText, media: mediaFieldInputSchema.optional(), linkUrl: optionalTrimmedText.refine( (value) => value === "" || /^https?:\/\//.test(value) || value.startsWith("/"), "Section linkUrl must be an absolute URL or start with /.", ), sortOrder: z.coerce.number().int().min(0).max(9999), }); export const assetInputSchema = z.object({ id: z.string().trim().optional(), kind: z.nativeEnum(PortfolioAssetKind), filePath: optionalTrimmedText, fileFieldName: optionalTrimmedText, media: mediaFieldInputSchema.optional(), altAr: requiredText("Asset altAr"), altEn: requiredText("Asset altEn"), altDe: requiredText("Asset altDe"), sortOrder: z.coerce.number().int().min(0).max(9999), }); export const projectInputSchema = z.object({ id: z.string().trim().optional(), categoryId: requiredText("Project categoryId"), slug: requiredText("Project slug") .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Project slug must be lowercase and hyphenated."), titleAr: requiredText("Project titleAr"), titleEn: requiredText("Project titleEn"), titleDe: requiredText("Project titleDe"), summaryAr: requiredText("Project summaryAr"), summaryEn: requiredText("Project summaryEn"), summaryDe: requiredText("Project summaryDe"), clientName: requiredText("Project clientName"), projectYear: z.coerce.number().int().min(2000).max(2100), serviceLabelAr: requiredText("Project serviceLabelAr"), serviceLabelEn: requiredText("Project serviceLabelEn"), serviceLabelDe: requiredText("Project serviceLabelDe"), previewUrl: optionalTrimmedText.refine( (value) => value === "" || /^https?:\/\//.test(value), "Project previewUrl must be an absolute URL.", ), currentCoverImagePath: optionalTrimmedText, coverMedia: mediaFieldInputSchema.optional(), sortOrder: z.coerce.number().int().min(0).max(9999), isFeatured: z.boolean(), isPublished: z.boolean(), sections: z.array(sectionInputSchema), assets: z.array(assetInputSchema), });