import { z } from 'zod' const hexColor = z .string() .regex(/^#([0-9a-fA-F]{3}){1,2}$/i, 'invalid hex color') const url = z.string().url() const sectionHeroSchema = z.object({ type: z.literal('hero'), props: z.object({ heading: z.string().min(1), subheading: z.string().optional().default(''), imageUrl: url, }), }) const sectionFaqSchema = z.object({ type: z.literal('faq'), props: z.object({ items: z.array(z.object({ q: z.string().min(1), a: z.string().min(1) })).min(1), }), }) const sectionCtaSchema = z.object({ type: z.literal('cta'), props: z.object({ text: z.string().min(1), href: z.string().optional().default('#') }), }) const sectionFeaturesSchema = z.object({ type: z.literal('features'), props: z.object({ items: z.array(z.string().min(1)).min(1) }), }) const sectionTestimonialsSchema = z.object({ type: z.literal('testimonials'), props: z.object({ items: z.array(z.object({ author: z.string().min(1), quote: z.string().min(1) })).min(1) }), }) const sectionBenefitsSchema = z.object({ type: z.literal('benefits'), props: z.object({ items: z.array(z.string().min(1)).min(1) }), }) // 확장: hero, faq, cta, features, testimonials 허용 const sectionSchema = z.union([ sectionHeroSchema, sectionFaqSchema, sectionCtaSchema, sectionFeaturesSchema, sectionTestimonialsSchema, sectionBenefitsSchema, ]) const baseField = { name: z.string().min(1), label: z.string().min(1), required: z.boolean().optional().default(false), pattern: z.string().optional(), maxLength: z.number().int().positive().optional(), help: z.string().optional(), } const textField = z.object({ type: z.literal('text'), ...baseField }) const emailField = z.object({ type: z.literal('email'), ...baseField }) const telField = z.object({ type: z.literal('tel'), ...baseField }) const textareaField = z.object({ type: z.literal('textarea'), ...baseField }) const dateField = z.object({ type: z.literal('date'), ...baseField }) const checkboxField = z.object({ type: z.literal('checkbox'), ...baseField }) const radioField = z.object({ type: z.literal('radio'), ...baseField, options: z.array(z.string().min(1)).min(1), }) const selectField = z.object({ type: z.literal('select'), ...baseField, options: z.array(z.string().min(1)).min(1), }) // 파일 업로드는 제외 export const formFieldSchema = z.union([ textField, emailField, telField, textareaField, dateField, checkboxField, radioField, selectField, ]) export const pageSchema = z.object({ title: z.string().min(1), locale: z.string().min(2), theme: z.object({ primaryColor: hexColor, fontFamily: z.string().min(1), }), sections: z.array(sectionSchema), form: z.object({ actionUrl: z.string().optional().default(''), fields: z.array(formFieldSchema).min(0), spamProtection: z.object({ honeypotFieldName: z.string().min(1).default('_hp'), minSubmitSeconds: z.number().min(0).default(2), }), }), analytics: z .object({ ga4MeasurementId: z.string().optional().default(''), metaPixelId: z.string().optional().default(''), customHeadHtml: z.string().optional().default(''), }) .optional() .default({ ga4MeasurementId: '', metaPixelId: '', customHeadHtml: '' }), seo: z.object({ title: z.string().min(1), description: z.string().min(1), ogImage: url.optional(), }), }) export type Page = z.infer export type FormField = z.infer