Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
38 lines
2.4 KiB
TypeScript
38 lines
2.4 KiB
TypeScript
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>
|