frontend.sensualmoment.de/src/lib/api.ts
CCS Admin f4e610e81e feat: complete frontend scaffold with all pages and components
- Project foundation: Next.js 16, Tailwind v4, Google Fonts, payload-contracts
- Shared components: Navigation (scroll effect), Footer (Deep Navy), Logo (wordmark), ScrollReveal
- Homepage: Hero, AboutPreview, GalleryPreview, Testimonials, Packages, BlogPreview, Contact
- Inner pages: ueber-mich, galerie, pakete, journal, journal/[slug], kontakt, faq, impressum, datenschutz, agb
- CMS API client (src/lib/api.ts) with tenant-scoped fetch helpers
- server.js for Plesk Passenger deployment
- Color palette: Dark Wine, Blush, Bordeaux, Deep Navy, Creme, Espresso
- Fonts: Playfair Display (headlines), Cormorant Garamond (body), Josefin Sans (UI)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:57:57 +00:00

115 lines
3.1 KiB
TypeScript

const CMS_URL = process.env.NEXT_PUBLIC_CMS_URL || "https://pl.porwoll.tech"
const TENANT_ID = process.env.NEXT_PUBLIC_TENANT_ID || "13"
interface FetchOptions {
collection: string
where?: Record<string, unknown>
limit?: number
page?: number
sort?: string
depth?: number
locale?: string
}
export async function fetchFromCMS<T = unknown>({
collection,
where = {},
limit = 100,
page = 1,
sort,
depth = 1,
locale = "de",
}: FetchOptions): Promise<{ docs: T[]; totalDocs: number; totalPages: number }> {
const params = new URLSearchParams()
params.set("where[tenant][equals]", TENANT_ID)
params.set("limit", String(limit))
params.set("page", String(page))
params.set("depth", String(depth))
params.set("locale", locale)
if (sort) params.set("sort", sort)
for (const [key, value] of Object.entries(where)) {
if (typeof value === "object" && value !== null) {
for (const [op, val] of Object.entries(value as Record<string, unknown>)) {
params.set("where[" + key + "][" + op + "]", String(val))
}
} else {
params.set("where[" + key + "][equals]", String(value))
}
}
const url = CMS_URL + "/api/" + collection + "?" + params.toString()
const res = await fetch(url, { next: { revalidate: 60 } })
if (!res.ok) {
console.error("CMS fetch failed:", url, res.status)
return { docs: [], totalDocs: 0, totalPages: 0 }
}
return res.json()
}
export async function fetchPage(slug: string) {
const { docs } = await fetchFromCMS<{ id: number; title: string; layout: unknown[]; slug: string }>({
collection: "pages",
where: { slug: { equals: slug } },
depth: 2,
})
return docs[0] || null
}
export async function fetchNavigation() {
const { docs } = await fetchFromCMS<{ mainMenu: unknown[]; footerMenu: unknown[] }>({
collection: "navigations",
depth: 2,
})
return docs[0] || null
}
export async function fetchSiteSettings() {
const { docs } = await fetchFromCMS<Record<string, unknown>>({
collection: "site-settings",
depth: 1,
})
return docs[0] || null
}
export async function fetchTestimonials() {
const { docs } = await fetchFromCMS<{ quote: string; author: string; role?: string }>({
collection: "testimonials",
where: { isActive: { equals: true } },
sort: "order",
})
return docs
}
export async function fetchFAQs(category?: string) {
const where: Record<string, unknown> = {}
if (category) where.category = { equals: category }
const { docs } = await fetchFromCMS<{ question: string; answer: unknown; category: string }>({
collection: "faqs",
where,
sort: "order",
limit: 50,
})
return docs
}
export async function fetchSocialLinks() {
const { docs } = await fetchFromCMS<{ platform: string; url: string; label?: string }>({
collection: "social-links",
sort: "order",
})
return docs
}
export async function fetchPosts(limit = 3) {
const { docs } = await fetchFromCMS<{ title: string; slug: string; excerpt?: string; coverImage?: unknown; publishedAt?: string }>({
collection: "posts",
where: { status: { equals: "published" } },
sort: "-publishedAt",
limit,
depth: 2,
})
return docs
}