project setup with core files including configuration, package management, and basic structure. Added .gitignore, README, and various TypeScript types for CMS components. Implemented initial components and layouts for the application.
This commit is contained in:
235
middlelayer/mappers/pageMapper.ts
Normal file
235
middlelayer/mappers/pageMapper.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import type { Page as CmsPage } from "../types/cms/Page";
|
||||
import type { HTMLSkeleton } from "../types/cms/Html";
|
||||
import type { MarkdownSkeleton } from "../types/cms/Markdown";
|
||||
import type { ComponentIframeSkeleton } from "../types/cms/Iframe";
|
||||
import type { ImageGallerySkeleton } from "../types/cms/ImageGallery";
|
||||
import type { ComponentImageSkeleton } from "../types/cms/Image";
|
||||
import type { QuoteSkeleton } from "../types/cms/Quote";
|
||||
import type { ComponentYoutubeVideoSkeleton } from "../types/cms/YoutubeVideo";
|
||||
import type { ComponentHeadlineSkeleton } from "../types/cms/Headline";
|
||||
import type { Page, ContentItem, ContentRow } from "../types/page";
|
||||
import { ContentType } from "../types/cms/ContentType.enum";
|
||||
|
||||
type ContentEntry =
|
||||
| HTMLSkeleton
|
||||
| MarkdownSkeleton
|
||||
| ComponentIframeSkeleton
|
||||
| ImageGallerySkeleton
|
||||
| ComponentImageSkeleton
|
||||
| QuoteSkeleton
|
||||
| ComponentYoutubeVideoSkeleton
|
||||
| ComponentHeadlineSkeleton;
|
||||
|
||||
/**
|
||||
* Mapper für Page-Transformationen
|
||||
* Konvertiert CMS-Typen zu unseren Domain-Typen
|
||||
*/
|
||||
export class PageMapper {
|
||||
/**
|
||||
* Strategy Pattern: Map mit Mapper-Funktionen für jeden Content-Type
|
||||
*/
|
||||
private static contentMappers = new Map<
|
||||
ContentType,
|
||||
(entry: ContentEntry) => ContentItem | null
|
||||
>([
|
||||
[
|
||||
ContentType.html,
|
||||
(entry) => {
|
||||
const htmlEntry = entry as HTMLSkeleton;
|
||||
return {
|
||||
type: "html",
|
||||
name: htmlEntry.fields.id || "",
|
||||
html: htmlEntry.fields.html,
|
||||
layout: htmlEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.markdown,
|
||||
(entry) => {
|
||||
const markdownEntry = entry as MarkdownSkeleton;
|
||||
return {
|
||||
type: "markdown",
|
||||
name: markdownEntry.fields.name,
|
||||
content: markdownEntry.fields.content,
|
||||
layout: markdownEntry.fields.layout,
|
||||
alignment: markdownEntry.fields.alignment,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.iframe,
|
||||
(entry) => {
|
||||
const iframeEntry = entry as ComponentIframeSkeleton;
|
||||
return {
|
||||
type: "iframe",
|
||||
name: iframeEntry.fields.name,
|
||||
content: iframeEntry.fields.content,
|
||||
iframe: iframeEntry.fields.iframe,
|
||||
overlayImageUrl: iframeEntry.fields.overlayImage?.fields.file.url,
|
||||
layout: iframeEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.imgGallery,
|
||||
(entry) => {
|
||||
const galleryEntry = entry as ImageGallerySkeleton;
|
||||
return {
|
||||
type: "imageGallery",
|
||||
name: galleryEntry.fields.name,
|
||||
images: galleryEntry.fields.images.map((img) => ({
|
||||
url: img.fields.file.url,
|
||||
title: img.fields.title,
|
||||
description: img.fields.description,
|
||||
})),
|
||||
description: galleryEntry.fields.description,
|
||||
layout: galleryEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.image,
|
||||
(entry) => {
|
||||
const imageEntry = entry as ComponentImageSkeleton;
|
||||
return {
|
||||
type: "image",
|
||||
name: imageEntry.fields.name,
|
||||
imageUrl: imageEntry.fields.image.fields.file.url,
|
||||
caption: imageEntry.fields.caption,
|
||||
maxWidth: imageEntry.fields.maxWidth,
|
||||
aspectRatio: imageEntry.fields.aspectRatio,
|
||||
layout: imageEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.quote,
|
||||
(entry) => {
|
||||
const quoteEntry = entry as QuoteSkeleton;
|
||||
return {
|
||||
type: "quote",
|
||||
quote: quoteEntry.fields.quote,
|
||||
author: quoteEntry.fields.author,
|
||||
variant: quoteEntry.fields.variant,
|
||||
layout: quoteEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.youtubeVideo,
|
||||
(entry) => {
|
||||
const videoEntry = entry as ComponentYoutubeVideoSkeleton;
|
||||
return {
|
||||
type: "youtubeVideo",
|
||||
id: videoEntry.fields.id,
|
||||
youtubeId: videoEntry.fields.youtubeId,
|
||||
params: videoEntry.fields.params,
|
||||
title: videoEntry.fields.title,
|
||||
description: videoEntry.fields.description,
|
||||
layout: videoEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ContentType.headline,
|
||||
(entry) => {
|
||||
const headlineEntry = entry as ComponentHeadlineSkeleton;
|
||||
return {
|
||||
type: "headline",
|
||||
internal: headlineEntry.fields.internal,
|
||||
text: headlineEntry.fields.text,
|
||||
tag: headlineEntry.fields.tag,
|
||||
align: headlineEntry.fields.align,
|
||||
layout: headlineEntry.fields.layout,
|
||||
};
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Mappt ein Contentful Content-Item zu unserem ContentItem
|
||||
* Verwendet Strategy Pattern für wartbaren Code
|
||||
*/
|
||||
private static mapContentItem(entry: ContentEntry): ContentItem | null {
|
||||
if (!entry.contentTypeId || !entry.fields) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const mapper = this.contentMappers.get(entry.contentTypeId);
|
||||
if (!mapper) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mapper(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mappt eine CMS Content-Row zu unserer ContentRow
|
||||
*/
|
||||
private static mapContentRow(
|
||||
content: ContentEntry[],
|
||||
justifyContent: string,
|
||||
alignItems: string
|
||||
): ContentRow | undefined {
|
||||
if (!content || content.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const mappedContent = content
|
||||
.map((entry) => this.mapContentItem(entry))
|
||||
.filter((item): item is ContentItem => item !== null);
|
||||
|
||||
if (mappedContent.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
justifyContent: justifyContent as ContentRow["justifyContent"],
|
||||
alignItems: alignItems as ContentRow["alignItems"],
|
||||
content: mappedContent,
|
||||
};
|
||||
}
|
||||
|
||||
static fromCms(cmsPage: CmsPage): Page {
|
||||
return {
|
||||
slug: cmsPage.slug,
|
||||
name: cmsPage.name,
|
||||
linkName: cmsPage.linkName,
|
||||
headline: cmsPage.headline,
|
||||
subheadline: cmsPage.subheadline,
|
||||
seoTitle: cmsPage.seoTitle,
|
||||
seoMetaRobots: cmsPage.seoMetaRobots,
|
||||
seoDescription: cmsPage.seoDescription,
|
||||
topFullwidthBanner: cmsPage.topFullwidthBanner
|
||||
? {
|
||||
name: cmsPage.topFullwidthBanner.fields.name,
|
||||
variant: cmsPage.topFullwidthBanner.fields.variant,
|
||||
headline: cmsPage.topFullwidthBanner.fields.headline,
|
||||
subheadline: cmsPage.topFullwidthBanner.fields.subheadline,
|
||||
text: cmsPage.topFullwidthBanner.fields.text,
|
||||
imageUrl: cmsPage.topFullwidthBanner.fields.img.fields.file.url,
|
||||
}
|
||||
: undefined,
|
||||
row1: this.mapContentRow(
|
||||
cmsPage.row1Content,
|
||||
cmsPage.row1JustifyContent,
|
||||
cmsPage.row1AlignItems
|
||||
),
|
||||
row2: this.mapContentRow(
|
||||
cmsPage.row2Content,
|
||||
cmsPage.row2JustifyContent,
|
||||
cmsPage.row2AlignItems
|
||||
),
|
||||
row3: this.mapContentRow(
|
||||
cmsPage.row3Content,
|
||||
cmsPage.row3JustifyContent,
|
||||
cmsPage.row3AlignItems
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
static fromCmsArray(cmsPages: CmsPage[]): Page[] {
|
||||
return cmsPages.map((page) => this.fromCms(page));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user