357 lines
7.4 KiB
TypeScript
357 lines
7.4 KiB
TypeScript
import {
|
|
createNavigationItemInputSchema,
|
|
createNavigationMenuInputSchema,
|
|
createPageInputSchema,
|
|
updateNavigationItemInputSchema,
|
|
updateNavigationMenuInputSchema,
|
|
updatePageInputSchema,
|
|
upsertPageTranslationInputSchema,
|
|
} from "@cms/content"
|
|
import { z } from "zod"
|
|
|
|
import { db } from "./client"
|
|
|
|
export type PublicNavigationItem = {
|
|
id: string
|
|
label: string
|
|
href: string
|
|
children: PublicNavigationItem[]
|
|
}
|
|
|
|
const supportedLocaleSchema = z.enum(["de", "en", "es", "fr"])
|
|
const upsertNavigationItemTranslationInputSchema = z.object({
|
|
navigationItemId: z.string().uuid(),
|
|
locale: supportedLocaleSchema,
|
|
label: z.string().min(1).max(180),
|
|
})
|
|
|
|
function resolvePublishedAt(status: string): Date | null {
|
|
return status === "published" ? new Date() : null
|
|
}
|
|
|
|
export async function listPages(limit = 50) {
|
|
return db.page.findMany({
|
|
orderBy: [{ updatedAt: "desc" }],
|
|
take: limit,
|
|
})
|
|
}
|
|
|
|
export async function listPublishedPageSlugs() {
|
|
const pages = await db.page.findMany({
|
|
where: { status: "published" },
|
|
orderBy: { updatedAt: "desc" },
|
|
select: {
|
|
slug: true,
|
|
updatedAt: true,
|
|
},
|
|
})
|
|
|
|
return pages
|
|
}
|
|
|
|
export async function getPageById(id: string) {
|
|
return db.page.findUnique({
|
|
where: { id },
|
|
})
|
|
}
|
|
|
|
export async function getPublishedPageBySlug(slug: string) {
|
|
return db.page.findFirst({
|
|
where: {
|
|
slug,
|
|
status: "published",
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function getPublishedPageBySlugForLocale(slug: string, locale: string) {
|
|
const page = await db.page.findFirst({
|
|
where: {
|
|
slug,
|
|
status: "published",
|
|
},
|
|
include: {
|
|
translations: {
|
|
where: {
|
|
locale,
|
|
},
|
|
take: 1,
|
|
},
|
|
},
|
|
})
|
|
|
|
if (!page) {
|
|
return null
|
|
}
|
|
|
|
const translation = page.translations[0]
|
|
|
|
return {
|
|
...page,
|
|
title: translation?.title ?? page.title,
|
|
summary: translation?.summary ?? page.summary,
|
|
content: translation?.content ?? page.content,
|
|
seoTitle: translation?.seoTitle ?? page.seoTitle,
|
|
seoDescription: translation?.seoDescription ?? page.seoDescription,
|
|
}
|
|
}
|
|
|
|
export async function createPage(input: unknown) {
|
|
const payload = createPageInputSchema.parse(input)
|
|
|
|
return db.page.create({
|
|
data: {
|
|
...payload,
|
|
publishedAt: resolvePublishedAt(payload.status),
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function updatePage(input: unknown) {
|
|
const payload = updatePageInputSchema.parse(input)
|
|
const { id, ...data } = payload
|
|
|
|
return db.page.update({
|
|
where: { id },
|
|
data: {
|
|
...data,
|
|
publishedAt:
|
|
data.status === undefined ? undefined : data.status === "published" ? new Date() : null,
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function deletePage(id: string) {
|
|
return db.page.delete({
|
|
where: { id },
|
|
})
|
|
}
|
|
|
|
export async function upsertPageTranslation(input: unknown) {
|
|
const payload = upsertPageTranslationInputSchema.parse(input)
|
|
const { pageId, locale, ...data } = payload
|
|
|
|
return db.pageTranslation.upsert({
|
|
where: {
|
|
pageId_locale: {
|
|
pageId,
|
|
locale,
|
|
},
|
|
},
|
|
create: {
|
|
pageId,
|
|
locale,
|
|
...data,
|
|
},
|
|
update: data,
|
|
})
|
|
}
|
|
|
|
export async function listPageTranslations(pageId: string) {
|
|
return db.pageTranslation.findMany({
|
|
where: { pageId },
|
|
orderBy: [{ locale: "asc" }],
|
|
})
|
|
}
|
|
|
|
export async function listNavigationMenus() {
|
|
return db.navigationMenu.findMany({
|
|
orderBy: [{ location: "asc" }, { name: "asc" }],
|
|
include: {
|
|
items: {
|
|
orderBy: [{ sortOrder: "asc" }, { label: "asc" }],
|
|
include: {
|
|
page: {
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
slug: true,
|
|
},
|
|
},
|
|
translations: {
|
|
orderBy: [{ locale: "asc" }],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
function resolveNavigationHref(item: {
|
|
href: string | null
|
|
page: {
|
|
slug: string
|
|
status: string
|
|
} | null
|
|
}): string | null {
|
|
if (item.href) {
|
|
return item.href
|
|
}
|
|
|
|
if (item.page?.status === "published") {
|
|
return item.page.slug === "home" ? "/" : `/${item.page.slug}`
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export async function listPublicNavigation(
|
|
location = "header",
|
|
locale?: string,
|
|
): Promise<PublicNavigationItem[]> {
|
|
const normalizedLocale = locale ? supportedLocaleSchema.safeParse(locale).data : undefined
|
|
|
|
const menu = await db.navigationMenu.findFirst({
|
|
where: {
|
|
location,
|
|
isVisible: true,
|
|
},
|
|
orderBy: { updatedAt: "desc" },
|
|
include: {
|
|
items: {
|
|
where: {
|
|
isVisible: true,
|
|
},
|
|
orderBy: [{ sortOrder: "asc" }, { label: "asc" }],
|
|
include: {
|
|
page: {
|
|
select: {
|
|
slug: true,
|
|
status: true,
|
|
},
|
|
},
|
|
translations: normalizedLocale
|
|
? {
|
|
where: { locale: normalizedLocale },
|
|
take: 1,
|
|
}
|
|
: false,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
if (!menu) {
|
|
return []
|
|
}
|
|
|
|
const itemMap = new Map<
|
|
string,
|
|
{
|
|
id: string
|
|
label: string
|
|
href: string
|
|
parentId: string | null
|
|
children: PublicNavigationItem[]
|
|
}
|
|
>()
|
|
|
|
for (const item of menu.items) {
|
|
const href = resolveNavigationHref(item)
|
|
|
|
if (!href) {
|
|
continue
|
|
}
|
|
|
|
itemMap.set(item.id, {
|
|
id: item.id,
|
|
label: item.translations?.[0]?.label ?? item.label,
|
|
href,
|
|
parentId: item.parentId,
|
|
children: [],
|
|
})
|
|
}
|
|
|
|
const roots: PublicNavigationItem[] = []
|
|
|
|
for (const entry of itemMap.values()) {
|
|
if (entry.parentId) {
|
|
const parent = itemMap.get(entry.parentId)
|
|
|
|
if (parent) {
|
|
parent.children.push({
|
|
id: entry.id,
|
|
label: entry.label,
|
|
href: entry.href,
|
|
children: entry.children,
|
|
})
|
|
continue
|
|
}
|
|
}
|
|
|
|
roots.push({
|
|
id: entry.id,
|
|
label: entry.label,
|
|
href: entry.href,
|
|
children: entry.children,
|
|
})
|
|
}
|
|
|
|
return roots
|
|
}
|
|
|
|
export async function createNavigationMenu(input: unknown) {
|
|
const payload = createNavigationMenuInputSchema.parse(input)
|
|
|
|
return db.navigationMenu.create({
|
|
data: payload,
|
|
})
|
|
}
|
|
|
|
export async function updateNavigationMenu(input: unknown) {
|
|
const payload = updateNavigationMenuInputSchema.parse(input)
|
|
const { id, ...data } = payload
|
|
|
|
return db.navigationMenu.update({
|
|
where: { id },
|
|
data,
|
|
})
|
|
}
|
|
|
|
export async function deleteNavigationMenu(id: string) {
|
|
return db.navigationMenu.delete({
|
|
where: { id },
|
|
})
|
|
}
|
|
|
|
export async function createNavigationItem(input: unknown) {
|
|
const payload = createNavigationItemInputSchema.parse(input)
|
|
|
|
return db.navigationItem.create({
|
|
data: payload,
|
|
})
|
|
}
|
|
|
|
export async function updateNavigationItem(input: unknown) {
|
|
const payload = updateNavigationItemInputSchema.parse(input)
|
|
const { id, ...data } = payload
|
|
|
|
return db.navigationItem.update({
|
|
where: { id },
|
|
data,
|
|
})
|
|
}
|
|
|
|
export async function deleteNavigationItem(id: string) {
|
|
return db.navigationItem.delete({
|
|
where: { id },
|
|
})
|
|
}
|
|
|
|
export async function upsertNavigationItemTranslation(input: unknown) {
|
|
const payload = upsertNavigationItemTranslationInputSchema.parse(input)
|
|
|
|
return db.navigationItemTranslation.upsert({
|
|
where: {
|
|
navigationItemId_locale: {
|
|
navigationItemId: payload.navigationItemId,
|
|
locale: payload.locale,
|
|
},
|
|
},
|
|
create: payload,
|
|
update: {
|
|
label: payload.label,
|
|
},
|
|
})
|
|
}
|