Files
web_sport/packages/validation/src/content.ts
T
2026-08-13 23:20:23 +07:00

53 lines
1.8 KiB
TypeScript

import { z } from 'zod';
import { LOCALES } from '@sport/types';
import { anyIdSchema, slugSchema } from './common';
import { offsetPageQuerySchema } from './pagination';
const contentTranslationSchema = z.object({
slug: slugSchema,
title: z.string().trim().min(1).max(255),
excerpt: z.string().trim().max(500).nullish(),
/**
* Markdown, and required.
*
* A page with a title and no body is a broken link with extra steps — it
* renders, gets indexed, and tells a reader nothing.
*/
body: z.string().trim().min(1),
metaTitle: z.string().trim().max(255).nullish(),
metaDescription: z.string().trim().max(500).nullish(),
});
/**
* A page or post as an operator authors it.
*
* `translations` is a partial record: publishing an English-only post is a
* legitimate thing to do, and requiring every locale would mean inventing
* Vietnamese copy to get anything live.
*/
export const contentEntryInputSchema = z
.object({
type: z.enum(['PAGE', 'POST']),
status: z.enum(['DRAFT', 'PUBLISHED']).default('DRAFT'),
coverImageId: anyIdSchema.nullish(),
translations: z.partialRecord(z.enum(LOCALES), contentTranslationSchema),
})
.refine((value) => Object.keys(value.translations).length > 0, {
message: 'Write the entry in at least one language',
path: ['translations'],
});
export const contentListQuerySchema = offsetPageQuerySchema.extend({
type: z.enum(['PAGE', 'POST']).optional(),
status: z.enum(['DRAFT', 'PUBLISHED']).optional(),
q: z.string().trim().max(120).optional(),
});
export const postListQuerySchema = offsetPageQuerySchema;
export type ContentEntryInput = z.output<typeof contentEntryInputSchema>;
export type ContentListQuery = z.output<typeof contentListQuerySchema>;
export type PostListQuery = z.output<typeof postListQuerySchema>;