feat(web): render cms pages and navigation from db
This commit is contained in:
@@ -8,6 +8,13 @@ import {
|
||||
|
||||
import { db } from "./client"
|
||||
|
||||
export type PublicNavigationItem = {
|
||||
id: string
|
||||
label: string
|
||||
href: string
|
||||
children: PublicNavigationItem[]
|
||||
}
|
||||
|
||||
function resolvePublishedAt(status: string): Date | null {
|
||||
return status === "published" ? new Date() : null
|
||||
}
|
||||
@@ -19,12 +26,34 @@ export async function listPages(limit = 50) {
|
||||
})
|
||||
}
|
||||
|
||||
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 createPage(input: unknown) {
|
||||
const payload = createPageInputSchema.parse(input)
|
||||
|
||||
@@ -76,6 +105,108 @@ export async function listNavigationMenus() {
|
||||
})
|
||||
}
|
||||
|
||||
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"): Promise<PublicNavigationItem[]> {
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
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.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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user