admin/validation/eventCategories.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

38 lines
2.4 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { z } from 'zod'
// Slugs are URL/API identifiers, always Latin — unlike mobile/OTP fields
// elsewhere in this app, there's deliberately no Persian-digit conversion
// here (see convertPersianToEnglish usage in validation/auth.ts).
const SLUG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/
const HEX_COLOR_PATTERN = /^#[0-9A-Fa-f]{6}$/
// SEO/char-limited field limits — also referenced by CategoryFormModal's
// SeoCharCounterHint call sites, so the UI hint and the zod schema can
// never drift apart.
export const CATEGORY_META_TITLE_MAX = 70
export const CATEGORY_META_DESCRIPTION_MAX = 160
export const CATEGORY_META_KEYWORDS_MAX = 255
export const CategoryFormValidation = z.object({
name: z.string().trim().min(1, 'نام دسته‌بندی را وارد کنید').max(100, 'نام حداکثر ۱۰۰ کاراکتر است'),
slug: z
.string()
.trim()
.min(1, 'اسلاگ را وارد کنید')
.max(120, 'اسلاگ حداکثر ۱۲۰ کاراکتر است')
.regex(SLUG_PATTERN, 'اسلاگ فقط می‌تواند شامل حروف انگلیسی کوچک، عدد و خط تیره باشد'),
shortDescription: z.string().trim().max(255, 'توضیح کوتاه حداکثر ۲۵۵ کاراکتر است').optional().or(z.literal('')),
description: z.string().trim().optional().or(z.literal('')),
icon: z.string().trim().max(50, 'نام آیکون حداکثر ۵۰ کاراکتر است').optional().or(z.literal('')),
color: z.string().trim().regex(HEX_COLOR_PATTERN, 'رنگ باید به‌صورت کد هگز باشد (مثل #FF5733)').optional().or(z.literal('')),
metaTitle: z.string().trim().max(CATEGORY_META_TITLE_MAX, 'عنوان SEO حداکثر ۷۰ کاراکتر است').optional().or(z.literal('')),
metaDescription: z.string().trim().max(CATEGORY_META_DESCRIPTION_MAX, 'توضیحات SEO حداکثر ۱۶۰ کاراکتر است').optional().or(z.literal('')),
metaKeywords: z.string().trim().max(CATEGORY_META_KEYWORDS_MAX, 'کلمات کلیدی حداکثر ۲۵۵ کاراکتر است').optional().or(z.literal('')),
ogImageUrl: z.union([z.literal(''), z.url('آدرس تصویر Open Graph معتبر نیست').max(2048)]).optional(),
sortOrder: z.number().int('ترتیب باید عدد صحیح باشد').min(0, 'ترتیب نمی‌تواند منفی باشد'),
isActive: z.boolean(),
isFeatured: z.boolean(),
})
export type CategoryFormValues = z.infer<typeof CategoryFormValidation>