feat(pages): add scheduled publish workflow for page management

This commit is contained in:
2026-02-12 23:10:47 +01:00
parent a2bea6326e
commit 18b709b4b0
8 changed files with 95 additions and 12 deletions

View File

@@ -103,6 +103,7 @@ export const createPageInputSchema = z.object({
content: z.string().min(1),
seoTitle: z.string().max(180).nullable().optional(),
seoDescription: z.string().max(320).nullable().optional(),
scheduledPublishAt: z.date().nullable().optional(),
})
export const updatePageInputSchema = z.object({
@@ -114,6 +115,7 @@ export const updatePageInputSchema = z.object({
content: z.string().min(1).optional(),
seoTitle: z.string().max(180).nullable().optional(),
seoDescription: z.string().max(320).nullable().optional(),
scheduledPublishAt: z.date().nullable().optional(),
})
export const upsertPageTranslationInputSchema = z.object({

View File

@@ -0,0 +1,4 @@
ALTER TABLE "Page"
ADD COLUMN "scheduledPublishAt" TIMESTAMP(3);
CREATE INDEX "Page_scheduledPublishAt_idx" ON "Page"("scheduledPublishAt");

View File

@@ -291,6 +291,7 @@ model Page {
seoTitle String?
seoDescription String?
publishedAt DateTime?
scheduledPublishAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
navItems NavigationItem[]

View File

@@ -29,6 +29,15 @@ function resolvePublishedAt(status: string): Date | null {
return status === "published" ? new Date() : null
}
function resolvePublicPageWhere(slug?: string) {
const now = new Date()
return {
...(slug ? { slug } : {}),
OR: [{ status: "published" }, { scheduledPublishAt: { lte: now } }],
}
}
export async function listPages(limit = 50) {
return db.page.findMany({
orderBy: [{ updatedAt: "desc" }],
@@ -38,7 +47,7 @@ export async function listPages(limit = 50) {
export async function listPublishedPageSlugs() {
const pages = await db.page.findMany({
where: { status: "published" },
where: resolvePublicPageWhere(),
orderBy: { updatedAt: "desc" },
select: {
slug: true,
@@ -57,19 +66,13 @@ export async function getPageById(id: string) {
export async function getPublishedPageBySlug(slug: string) {
return db.page.findFirst({
where: {
slug,
status: "published",
},
where: resolvePublicPageWhere(slug),
})
}
export async function getPublishedPageBySlugForLocale(slug: string, locale: string) {
const page = await db.page.findFirst({
where: {
slug,
status: "published",
},
where: resolvePublicPageWhere(slug),
include: {
translations: {
where: {
@@ -98,11 +101,13 @@ export async function getPublishedPageBySlugForLocale(slug: string, locale: stri
export async function createPage(input: unknown) {
const payload = createPageInputSchema.parse(input)
const isImmediatelyPublished = payload.status === "published"
return db.page.create({
data: {
...payload,
publishedAt: resolvePublishedAt(payload.status),
scheduledPublishAt: isImmediatelyPublished ? null : (payload.scheduledPublishAt ?? null),
},
})
}
@@ -110,6 +115,7 @@ export async function createPage(input: unknown) {
export async function updatePage(input: unknown) {
const payload = updatePageInputSchema.parse(input)
const { id, ...data } = payload
const isImmediatelyPublished = data.status === "published"
return db.page.update({
where: { id },
@@ -117,6 +123,12 @@ export async function updatePage(input: unknown) {
...data,
publishedAt:
data.status === undefined ? undefined : data.status === "published" ? new Date() : null,
scheduledPublishAt:
data.scheduledPublishAt === undefined
? undefined
: isImmediatelyPublished
? null
: data.scheduledPublishAt,
},
})
}