frontend.blogwoman.de/src/lib/api.ts
CCS Admin 7235d8b910 fix: navigation migration to payload-contracts
- Fix getNavigation() to use contracts API (no broken type filter)
- Single nav fetch in layout, pass mainMenu/footerMenu to components
- Header, Navigation, MobileMenu use CMS mainMenu schema
- Footer uses CMS footerMenu schema with linkType field
- Add pnpm-workspace.yaml for onlyBuiltDependencies allowlist
- Update payload-contracts to latest (navigation fix a0eea96)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:43:05 +00:00

245 lines
7 KiB
TypeScript

/**
* Payload CMS API functions — powered by @c2s/payload-contracts
*
* Uses the shared API client for transport (tenant isolation, fetch caching).
* Returns data typed with local interfaces for component compatibility.
*
* Navigation uses contracts types directly (schema matches).
* Other types use 'as unknown as' bridge since local types differ
* from CMS types (meta vs seo, id string vs number, etc.).
*/
import { cms } from "./cms"
import type { Navigation } from "@c2s/payload-contracts/types"
import type {
Page,
Post,
Favorite,
Series,
Testimonial,
FAQ,
SeoSettings,
PaginatedResponse,
SiteSettings,
} from "./types"
export type { Navigation }
const TENANT_ID = process.env.NEXT_PUBLIC_TENANT_ID || "9"
// Pages
export async function getPage(slug: string, locale = "de"): Promise<Page | null> {
const result = await cms.pages.getPage(slug, {
locale: locale as "de" | "en",
depth: 2,
})
return result as unknown as Page | null
}
export async function getPages(options: {
limit?: number
page?: number
locale?: string
} = {}): Promise<PaginatedResponse<Page>> {
const result = await cms.pages.getPages({
limit: options.limit || 100,
page: options.page || 1,
locale: (options.locale || "de") as "de" | "en",
depth: 1,
})
return result as unknown as PaginatedResponse<Page>
}
// Posts
export async function getPost(slug: string, locale = "de"): Promise<Post | null> {
const result = await cms.posts.getPost(slug, {
locale: locale as "de" | "en",
depth: 2,
})
return result as unknown as Post | null
}
export async function getPosts(options: {
type?: string
category?: string
series?: string
limit?: number
page?: number
locale?: string
featured?: boolean
} = {}): Promise<PaginatedResponse<Post>> {
try {
const where: Record<string, unknown> = {}
if (options.featured) where["isFeatured][equals"] = "true"
const result = await cms.posts.getPosts({
type: options.type,
category: options.category,
series: options.series,
limit: options.limit || 10,
page: options.page || 1,
locale: (options.locale || "de") as "de" | "en",
where,
})
return result as unknown as PaginatedResponse<Post>
} catch {
return { docs: [], totalDocs: 0, limit: 10, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null }
}
}
// Navigation — single document per tenant with mainMenu + footerMenu
// Uses contracts Navigation type directly (no bridge needed)
export async function getNavigation(): Promise<Navigation | null> {
try {
return await cms.navigation.getNavigation({ depth: 2 })
} catch {
return null
}
}
// Site Settings
export async function getSiteSettings(): Promise<SiteSettings | null> {
try {
const result = await cms.settings.getSiteSettings({ depth: 2 })
return result as unknown as SiteSettings | null
} catch {
return null
}
}
// SEO Settings (Global)
export async function getSeoSettings(): Promise<SeoSettings | null> {
try {
const result = await cms.settings.getSeoSettings()
return result as unknown as SeoSettings | null
} catch {
return null
}
}
// Testimonials
export async function getTestimonials(options: {
limit?: number
locale?: string
} = {}): Promise<PaginatedResponse<Testimonial>> {
const result = await cms.client.getCollection("testimonials", {
limit: options.limit || 10,
locale: (options.locale || "de") as "de" | "en",
depth: 1,
})
return result as unknown as PaginatedResponse<Testimonial>
}
// FAQs
export async function getFAQs(options: {
category?: string
limit?: number
locale?: string
} = {}): Promise<PaginatedResponse<FAQ>> {
const result = await cms.client.getCollection("faqs", {
limit: options.limit || 50,
locale: (options.locale || "de") as "de" | "en",
sort: "order",
depth: 1,
where: options.category ? { "category][equals": options.category } : undefined,
})
return result as unknown as PaginatedResponse<FAQ>
}
// BlogWoman: Favorites
export async function getFavorites(options: {
category?: string
badge?: string
limit?: number
page?: number
locale?: string
} = {}): Promise<PaginatedResponse<Favorite>> {
try {
const where: Record<string, unknown> = { "isActive][equals": "true" }
if (options.category) where["category][equals"] = options.category
if (options.badge) where["badge][equals"] = options.badge
const result = await cms.client.getCollection("favorites", {
limit: options.limit || 12,
page: options.page || 1,
locale: (options.locale || "de") as "de" | "en",
depth: 1,
where,
})
return result as unknown as PaginatedResponse<Favorite>
} catch {
return { docs: [], totalDocs: 0, limit: 12, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null }
}
}
export async function getFavorite(slug: string, locale = "de"): Promise<Favorite | null> {
const data = await cms.client.getCollection("favorites", {
locale: locale as "de" | "en",
depth: 1,
where: { "slug][equals": slug, "isActive][equals": "true" },
limit: 1,
})
return (data.docs[0] as unknown as Favorite) ?? null
}
// BlogWoman: Series
export async function getSeries(options: {
limit?: number
locale?: string
} = {}): Promise<PaginatedResponse<Series>> {
try {
const result = await cms.client.getCollection("series", {
limit: options.limit || 20,
locale: (options.locale || "de") as "de" | "en",
depth: 2,
where: { "isActive][equals": "true" },
})
return result as unknown as PaginatedResponse<Series>
} catch {
return { docs: [], totalDocs: 0, limit: 20, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null }
}
}
export async function getSeriesBySlug(slug: string, locale = "de"): Promise<Series | null> {
const data = await cms.client.getCollection("series", {
locale: locale as "de" | "en",
depth: 2,
where: { "slug][equals": slug, "isActive][equals": "true" },
limit: 1,
})
return (data.docs[0] as unknown as Series) ?? null
}
// Newsletter Subscription
export async function subscribeNewsletter(
email: string,
firstName?: string,
source = "website"
): Promise<{ success: boolean; message?: string }> {
return cms.post("/api/newsletter/subscribe", {
email,
firstName,
tenantId: Number(TENANT_ID),
source,
})
}
// Contact Form Submission
export async function submitContactForm(data: {
name: string
email: string
phone?: string
subject: string
message: string
formId?: number
}): Promise<{ success: boolean; message?: string }> {
return cms.post("/api/form-submissions", {
form: data.formId || 1,
submissionData: [
{ field: "name", value: data.name },
{ field: "email", value: data.email },
{ field: "phone", value: data.phone || "" },
{ field: "subject", value: data.subject },
{ field: "message", value: data.message },
],
})
}