mirror of
https://github.com/complexcaresolutions/frontend.blogwoman.de.git
synced 2026-03-17 17:24:00 +00:00
feat: migrate API layer to @c2s/payload-contracts
- Add @c2s/payload-contracts as shared API client dependency - Create src/lib/cms.ts with tenant-configured PayloadClient instance - Replace manual fetch logic in api.ts with contracts client calls - Add transpilePackages config for TypeScript source imports - Local types preserved for component compatibility (bridge pattern) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ba54d7a85d
commit
2500b8b16f
5 changed files with 167 additions and 274 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
transpilePackages: ["@c2s/payload-contracts"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
"next": "16.0.10",
|
"next": "16.0.10",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.1",
|
||||||
"tailwind-merge": "^3.4.0"
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"@c2s/payload-contracts": "github:complexcaresolutions/payload-contracts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@c2s/payload-contracts':
|
||||||
|
specifier: github:complexcaresolutions/payload-contracts
|
||||||
|
version: git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7(react@19.2.1)
|
||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
|
@ -122,6 +125,15 @@ packages:
|
||||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7':
|
||||||
|
resolution: {commit: 64847594b2150bfdce09a7bd7f54ad2f52d6f2b7, repo: git@github.com:complexcaresolutions/payload-contracts.git, type: git}
|
||||||
|
version: 1.0.0
|
||||||
|
peerDependencies:
|
||||||
|
react: ^19.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@emnapi/core@1.7.1':
|
'@emnapi/core@1.7.1':
|
||||||
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
|
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
|
||||||
|
|
||||||
|
|
@ -2021,6 +2033,10 @@ snapshots:
|
||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.28.5
|
'@babel/helper-validator-identifier': 7.28.5
|
||||||
|
|
||||||
|
'@c2s/payload-contracts@git+https://git@github.com:complexcaresolutions/payload-contracts.git#64847594b2150bfdce09a7bd7f54ad2f52d6f2b7(react@19.2.1)':
|
||||||
|
optionalDependencies:
|
||||||
|
react: 19.2.1
|
||||||
|
|
||||||
'@emnapi/core@1.7.1':
|
'@emnapi/core@1.7.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/wasi-threads': 1.1.0
|
'@emnapi/wasi-threads': 1.1.0
|
||||||
|
|
|
||||||
405
src/lib/api.ts
405
src/lib/api.ts
|
|
@ -1,80 +1,36 @@
|
||||||
|
/**
|
||||||
|
* Payload CMS API functions — powered by @c2s/payload-contracts
|
||||||
|
*
|
||||||
|
* Uses the shared API client for transport (tenant isolation, fetch caching)
|
||||||
|
* but returns data typed with local interfaces for component compatibility.
|
||||||
|
*
|
||||||
|
* The local types use simplified interfaces (e.g. id: string, meta instead of seo)
|
||||||
|
* while the contracts use the real CMS types. We use 'as unknown as' to bridge.
|
||||||
|
*/
|
||||||
|
import { cms } from "./cms"
|
||||||
import type {
|
import type {
|
||||||
Page,
|
Page,
|
||||||
Post,
|
Post,
|
||||||
Navigation,
|
Navigation,
|
||||||
SiteSettings,
|
|
||||||
Favorite,
|
Favorite,
|
||||||
Series,
|
Series,
|
||||||
Testimonial,
|
Testimonial,
|
||||||
FAQ,
|
FAQ,
|
||||||
SeoSettings,
|
SeoSettings,
|
||||||
PaginatedResponse,
|
PaginatedResponse,
|
||||||
FavoriteCategory,
|
SiteSettings,
|
||||||
FavoriteBadge,
|
} from "./types"
|
||||||
} from './types'
|
|
||||||
|
|
||||||
const PAYLOAD_URL = process.env.NEXT_PUBLIC_PAYLOAD_URL || 'https://cms.c2sgmbh.de'
|
const PAYLOAD_URL = process.env.NEXT_PUBLIC_PAYLOAD_URL || "https://cms.c2sgmbh.de"
|
||||||
const TENANT_ID = process.env.NEXT_PUBLIC_TENANT_ID || '9'
|
const TENANT_ID = process.env.NEXT_PUBLIC_TENANT_ID || "9"
|
||||||
|
|
||||||
// Shared empty paginated response for error fallbacks
|
|
||||||
const emptyPaginatedResponse = { docs: [], totalDocs: 0, limit: 10, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null }
|
|
||||||
|
|
||||||
interface FetchOptions {
|
|
||||||
revalidate?: number | false
|
|
||||||
tags?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAPI<T>(
|
|
||||||
endpoint: string,
|
|
||||||
options: FetchOptions & { defaultValue?: T } = {}
|
|
||||||
): Promise<T> {
|
|
||||||
const { revalidate = 60, tags, defaultValue } = options
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${PAYLOAD_URL}${endpoint}`, {
|
|
||||||
next: {
|
|
||||||
revalidate,
|
|
||||||
tags,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
console.error(`API error: ${res.status} ${res.statusText} for ${endpoint}`)
|
|
||||||
if (defaultValue !== undefined) {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
throw new Error(`API error: ${res.status} ${res.statusText}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json()
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Fetch error for ${endpoint}:`, error)
|
|
||||||
if (defaultValue !== undefined) {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
export async function getPage(
|
export async function getPage(slug: string, locale = "de"): Promise<Page | null> {
|
||||||
slug: string,
|
const result = await cms.pages.getPage(slug, {
|
||||||
locale = 'de'
|
locale: locale as "de" | "en",
|
||||||
): Promise<Page | null> {
|
depth: 2,
|
||||||
const params = new URLSearchParams({
|
|
||||||
'where[tenant][equals]': TENANT_ID,
|
|
||||||
'where[slug][equals]': slug,
|
|
||||||
'where[status][equals]': 'published',
|
|
||||||
locale,
|
|
||||||
depth: '2',
|
|
||||||
})
|
})
|
||||||
|
return result as unknown as Page | null
|
||||||
const data = await fetchAPI<PaginatedResponse<Page>>(
|
|
||||||
`/api/pages?${params}`,
|
|
||||||
{ tags: [`page-${slug}`] }
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPages(options: {
|
export async function getPages(options: {
|
||||||
|
|
@ -82,41 +38,26 @@ export async function getPages(options: {
|
||||||
page?: number
|
page?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
} = {}): Promise<PaginatedResponse<Page>> {
|
} = {}): Promise<PaginatedResponse<Page>> {
|
||||||
const params = new URLSearchParams({
|
const result = await cms.pages.getPages({
|
||||||
'where[tenant][equals]': TENANT_ID,
|
limit: options.limit || 100,
|
||||||
'where[status][equals]': 'published',
|
page: options.page || 1,
|
||||||
limit: String(options.limit || 100),
|
locale: (options.locale || "de") as "de" | "en",
|
||||||
page: String(options.page || 1),
|
depth: 1,
|
||||||
locale: options.locale || 'de',
|
|
||||||
depth: '1',
|
|
||||||
})
|
})
|
||||||
|
return result as unknown as PaginatedResponse<Page>
|
||||||
return fetchAPI<PaginatedResponse<Page>>(`/api/pages?${params}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Posts
|
// Posts
|
||||||
export async function getPost(
|
export async function getPost(slug: string, locale = "de"): Promise<Post | null> {
|
||||||
slug: string,
|
const result = await cms.posts.getPost(slug, {
|
||||||
locale = 'de'
|
locale: locale as "de" | "en",
|
||||||
): Promise<Post | null> {
|
depth: 2,
|
||||||
const params = new URLSearchParams({
|
|
||||||
'where[tenant][equals]': TENANT_ID,
|
|
||||||
'where[slug][equals]': slug,
|
|
||||||
'where[status][equals]': 'published',
|
|
||||||
locale,
|
|
||||||
depth: '2',
|
|
||||||
})
|
})
|
||||||
|
return result as unknown as Post | null
|
||||||
const data = await fetchAPI<PaginatedResponse<Post>>(
|
|
||||||
`/api/posts?${params}`,
|
|
||||||
{ tags: [`post-${slug}`] }
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPosts(options: {
|
export async function getPosts(options: {
|
||||||
type?: 'blog' | 'news' | 'press' | 'announcement'
|
type?: string
|
||||||
category?: string
|
category?: string
|
||||||
series?: string
|
series?: string
|
||||||
limit?: number
|
limit?: number
|
||||||
|
|
@ -124,83 +65,53 @@ export async function getPosts(options: {
|
||||||
locale?: string
|
locale?: string
|
||||||
featured?: boolean
|
featured?: boolean
|
||||||
} = {}): Promise<PaginatedResponse<Post>> {
|
} = {}): Promise<PaginatedResponse<Post>> {
|
||||||
const params = new URLSearchParams({
|
try {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
const where: Record<string, unknown> = {}
|
||||||
'where[status][equals]': 'published',
|
if (options.featured) where["isFeatured][equals"] = "true"
|
||||||
sort: '-publishedAt',
|
|
||||||
limit: String(options.limit || 10),
|
|
||||||
page: String(options.page || 1),
|
|
||||||
locale: options.locale || 'de',
|
|
||||||
depth: '1',
|
|
||||||
})
|
|
||||||
|
|
||||||
if (options.type) {
|
const result = await cms.posts.getPosts({
|
||||||
params.append('where[type][equals]', options.type)
|
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 }
|
||||||
}
|
}
|
||||||
if (options.category) {
|
|
||||||
params.append('where[categories][contains]', options.category)
|
|
||||||
}
|
|
||||||
if (options.series) {
|
|
||||||
params.append('where[series][equals]', options.series)
|
|
||||||
}
|
|
||||||
if (options.featured) {
|
|
||||||
params.append('where[isFeatured][equals]', 'true')
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetchAPI<PaginatedResponse<Post>>(
|
|
||||||
`/api/posts?${params}`,
|
|
||||||
{ tags: ['posts'], defaultValue: emptyPaginatedResponse as PaginatedResponse<Post> }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
export async function getNavigation(
|
export async function getNavigation(type: "header" | "footer" | "mobile"): Promise<Navigation | null> {
|
||||||
type: 'header' | 'footer' | 'mobile'
|
try {
|
||||||
): Promise<Navigation | null> {
|
const result = await cms.navigation.getNavigation(type, { depth: 2 })
|
||||||
const params = new URLSearchParams({
|
return result as unknown as Navigation | null
|
||||||
'where[tenant][equals]': TENANT_ID,
|
} catch {
|
||||||
'where[type][equals]': type,
|
return null
|
||||||
depth: '2',
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const data = await fetchAPI<PaginatedResponse<Navigation>>(
|
|
||||||
`/api/navigations?${params}`,
|
|
||||||
{
|
|
||||||
revalidate: 300,
|
|
||||||
tags: [`navigation-${type}`],
|
|
||||||
defaultValue: { docs: [], totalDocs: 0, limit: 10, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Site Settings
|
// Site Settings
|
||||||
export async function getSiteSettings(): Promise<SiteSettings | null> {
|
export async function getSiteSettings(): Promise<SiteSettings | null> {
|
||||||
const params = new URLSearchParams({
|
try {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
const result = await cms.settings.getSiteSettings({ depth: 2 })
|
||||||
depth: '2',
|
return result as unknown as SiteSettings | null
|
||||||
})
|
} catch {
|
||||||
|
return null
|
||||||
const data = await fetchAPI<PaginatedResponse<SiteSettings>>(
|
}
|
||||||
`/api/site-settings?${params}`,
|
|
||||||
{
|
|
||||||
revalidate: 300,
|
|
||||||
tags: ['site-settings'],
|
|
||||||
defaultValue: { docs: [], totalDocs: 0, limit: 10, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SEO Settings (Global)
|
// SEO Settings (Global)
|
||||||
export async function getSeoSettings(): Promise<SeoSettings | null> {
|
export async function getSeoSettings(): Promise<SeoSettings | null> {
|
||||||
return fetchAPI<SeoSettings>('/api/globals/seo-settings', {
|
try {
|
||||||
revalidate: 3600,
|
const result = await cms.settings.getSeoSettings()
|
||||||
tags: ['seo-settings'],
|
return result as unknown as SeoSettings | null
|
||||||
defaultValue: null as unknown as SeoSettings,
|
} catch {
|
||||||
})
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Testimonials
|
// Testimonials
|
||||||
|
|
@ -208,17 +119,12 @@ export async function getTestimonials(options: {
|
||||||
limit?: number
|
limit?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
} = {}): Promise<PaginatedResponse<Testimonial>> {
|
} = {}): Promise<PaginatedResponse<Testimonial>> {
|
||||||
const params = new URLSearchParams({
|
const result = await cms.client.getCollection("testimonials", {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
limit: options.limit || 10,
|
||||||
limit: String(options.limit || 10),
|
locale: (options.locale || "de") as "de" | "en",
|
||||||
locale: options.locale || 'de',
|
depth: 1,
|
||||||
depth: '1',
|
|
||||||
})
|
})
|
||||||
|
return result as unknown as PaginatedResponse<Testimonial>
|
||||||
return fetchAPI<PaginatedResponse<Testimonial>>(
|
|
||||||
`/api/testimonials?${params}`,
|
|
||||||
{ tags: ['testimonials'] }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FAQs
|
// FAQs
|
||||||
|
|
@ -227,72 +133,50 @@ export async function getFAQs(options: {
|
||||||
limit?: number
|
limit?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
} = {}): Promise<PaginatedResponse<FAQ>> {
|
} = {}): Promise<PaginatedResponse<FAQ>> {
|
||||||
const params = new URLSearchParams({
|
const result = await cms.client.getCollection("faqs", {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
limit: options.limit || 50,
|
||||||
sort: 'order',
|
locale: (options.locale || "de") as "de" | "en",
|
||||||
limit: String(options.limit || 50),
|
sort: "order",
|
||||||
locale: options.locale || 'de',
|
depth: 1,
|
||||||
depth: '1',
|
where: options.category ? { "category][equals": options.category } : undefined,
|
||||||
})
|
})
|
||||||
|
return result as unknown as PaginatedResponse<FAQ>
|
||||||
if (options.category) {
|
|
||||||
params.append('where[category][equals]', options.category)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetchAPI<PaginatedResponse<FAQ>>(
|
|
||||||
`/api/faqs?${params}`,
|
|
||||||
{ tags: ['faqs'] }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlogWoman: Favorites
|
// BlogWoman: Favorites
|
||||||
export async function getFavorites(options: {
|
export async function getFavorites(options: {
|
||||||
category?: FavoriteCategory
|
category?: string
|
||||||
badge?: FavoriteBadge
|
badge?: string
|
||||||
limit?: number
|
limit?: number
|
||||||
page?: number
|
page?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
} = {}): Promise<PaginatedResponse<Favorite>> {
|
} = {}): Promise<PaginatedResponse<Favorite>> {
|
||||||
const params = new URLSearchParams({
|
try {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
const where: Record<string, unknown> = { "isActive][equals": "true" }
|
||||||
'where[isActive][equals]': 'true',
|
if (options.category) where["category][equals"] = options.category
|
||||||
limit: String(options.limit || 12),
|
if (options.badge) where["badge][equals"] = options.badge
|
||||||
page: String(options.page || 1),
|
|
||||||
locale: options.locale || 'de',
|
|
||||||
depth: '1',
|
|
||||||
})
|
|
||||||
|
|
||||||
if (options.category) {
|
const result = await cms.client.getCollection("favorites", {
|
||||||
params.append('where[category][equals]', options.category)
|
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 }
|
||||||
}
|
}
|
||||||
if (options.badge) {
|
|
||||||
params.append('where[badge][equals]', options.badge)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetchAPI<PaginatedResponse<Favorite>>(
|
|
||||||
`/api/favorites?${params}`,
|
|
||||||
{ tags: ['favorites'], defaultValue: emptyPaginatedResponse as PaginatedResponse<Favorite> }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFavorite(
|
export async function getFavorite(slug: string, locale = "de"): Promise<Favorite | null> {
|
||||||
slug: string,
|
const data = await cms.client.getCollection("favorites", {
|
||||||
locale = 'de'
|
locale: locale as "de" | "en",
|
||||||
): Promise<Favorite | null> {
|
depth: 1,
|
||||||
const params = new URLSearchParams({
|
where: { "slug][equals": slug, "isActive][equals": "true" },
|
||||||
'where[tenant][equals]': TENANT_ID,
|
limit: 1,
|
||||||
'where[slug][equals]': slug,
|
|
||||||
'where[isActive][equals]': 'true',
|
|
||||||
locale,
|
|
||||||
depth: '1',
|
|
||||||
})
|
})
|
||||||
|
return (data.docs[0] as unknown as Favorite) ?? null
|
||||||
const data = await fetchAPI<PaginatedResponse<Favorite>>(
|
|
||||||
`/api/favorites?${params}`,
|
|
||||||
{ tags: [`favorite-${slug}`] }
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlogWoman: Series
|
// BlogWoman: Series
|
||||||
|
|
@ -300,58 +184,41 @@ export async function getSeries(options: {
|
||||||
limit?: number
|
limit?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
} = {}): Promise<PaginatedResponse<Series>> {
|
} = {}): Promise<PaginatedResponse<Series>> {
|
||||||
const params = new URLSearchParams({
|
try {
|
||||||
'where[tenant][equals]': TENANT_ID,
|
const result = await cms.client.getCollection("series", {
|
||||||
'where[isActive][equals]': 'true',
|
limit: options.limit || 20,
|
||||||
limit: String(options.limit || 20),
|
locale: (options.locale || "de") as "de" | "en",
|
||||||
locale: options.locale || 'de',
|
depth: 2,
|
||||||
depth: '2',
|
where: { "isActive][equals": "true" },
|
||||||
})
|
})
|
||||||
|
return result as unknown as PaginatedResponse<Series>
|
||||||
return fetchAPI<PaginatedResponse<Series>>(
|
} catch {
|
||||||
`/api/series?${params}`,
|
return { docs: [], totalDocs: 0, limit: 20, totalPages: 0, page: 1, pagingCounter: 1, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null }
|
||||||
{ tags: ['series'], defaultValue: emptyPaginatedResponse as PaginatedResponse<Series> }
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSeriesBySlug(
|
export async function getSeriesBySlug(slug: string, locale = "de"): Promise<Series | null> {
|
||||||
slug: string,
|
const data = await cms.client.getCollection("series", {
|
||||||
locale = 'de'
|
locale: locale as "de" | "en",
|
||||||
): Promise<Series | null> {
|
depth: 2,
|
||||||
const params = new URLSearchParams({
|
where: { "slug][equals": slug, "isActive][equals": "true" },
|
||||||
'where[tenant][equals]': TENANT_ID,
|
limit: 1,
|
||||||
'where[slug][equals]': slug,
|
|
||||||
'where[isActive][equals]': 'true',
|
|
||||||
locale,
|
|
||||||
depth: '2',
|
|
||||||
})
|
})
|
||||||
|
return (data.docs[0] as unknown as Series) ?? null
|
||||||
const data = await fetchAPI<PaginatedResponse<Series>>(
|
|
||||||
`/api/series?${params}`,
|
|
||||||
{ tags: [`series-${slug}`] }
|
|
||||||
)
|
|
||||||
|
|
||||||
return data.docs[0] || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newsletter Subscription
|
// Newsletter Subscription
|
||||||
export async function subscribeNewsletter(
|
export async function subscribeNewsletter(
|
||||||
email: string,
|
email: string,
|
||||||
firstName?: string,
|
firstName?: string,
|
||||||
source = 'website'
|
source = "website"
|
||||||
): Promise<{ success: boolean; message?: string }> {
|
): Promise<{ success: boolean; message?: string }> {
|
||||||
const res = await fetch(`${PAYLOAD_URL}/api/newsletter/subscribe`, {
|
return cms.post("/api/newsletter/subscribe", {
|
||||||
method: 'POST',
|
email,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
firstName,
|
||||||
body: JSON.stringify({
|
tenantId: Number(TENANT_ID),
|
||||||
email,
|
source,
|
||||||
firstName,
|
|
||||||
tenantId: Number(TENANT_ID),
|
|
||||||
source,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contact Form Submission
|
// Contact Form Submission
|
||||||
|
|
@ -363,20 +230,14 @@ export async function submitContactForm(data: {
|
||||||
message: string
|
message: string
|
||||||
formId?: number
|
formId?: number
|
||||||
}): Promise<{ success: boolean; message?: string }> {
|
}): Promise<{ success: boolean; message?: string }> {
|
||||||
const res = await fetch(`${PAYLOAD_URL}/api/form-submissions`, {
|
return cms.post("/api/form-submissions", {
|
||||||
method: 'POST',
|
form: data.formId || 1,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
submissionData: [
|
||||||
body: JSON.stringify({
|
{ field: "name", value: data.name },
|
||||||
form: data.formId || 1,
|
{ field: "email", value: data.email },
|
||||||
submissionData: [
|
{ field: "phone", value: data.phone || "" },
|
||||||
{ field: 'name', value: data.name },
|
{ field: "subject", value: data.subject },
|
||||||
{ field: 'email', value: data.email },
|
{ field: "message", value: data.message },
|
||||||
{ field: 'phone', value: data.phone || '' },
|
],
|
||||||
{ field: 'subject', value: data.subject },
|
|
||||||
{ field: 'message', value: data.message },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.json()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/lib/cms.ts
Normal file
15
src/lib/cms.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Payload CMS Client — initialized from @c2s/payload-contracts
|
||||||
|
*
|
||||||
|
* Single shared client instance for all API calls.
|
||||||
|
* Tenant isolation is handled automatically.
|
||||||
|
*/
|
||||||
|
import { createPayloadClient } from "@c2s/payload-contracts/api-client"
|
||||||
|
|
||||||
|
export const cms = createPayloadClient({
|
||||||
|
baseUrl: process.env.NEXT_PUBLIC_PAYLOAD_URL || "https://cms.c2sgmbh.de",
|
||||||
|
tenantId: process.env.NEXT_PUBLIC_TENANT_ID || "9",
|
||||||
|
defaultLocale: "de",
|
||||||
|
defaultDepth: 2,
|
||||||
|
defaultRevalidate: 60,
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue