Track all types in git, sync to server on deploy
Some checks failed
Deploy to Server / deploy (push) Failing after 7s

This commit is contained in:
Peter Meier
2026-03-15 14:38:37 +01:00
parent 286fab8452
commit 4de99db670
34 changed files with 1379 additions and 4 deletions

View File

@@ -27,6 +27,9 @@ jobs:
run: | run: |
echo "${{ secrets.SSH_DEPLOY_KEY }}" > /tmp/deploy_key echo "${{ secrets.SSH_DEPLOY_KEY }}" > /tmp/deploy_key
chmod 600 /tmp/deploy_key chmod 600 /tmp/deploy_key
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key" \
./types/ root@167.86.74.105:/opt/rustycms/types/
ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key root@167.86.74.105 \ ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key root@167.86.74.105 \
"docker compose -f /opt/rustycms/docker-compose.yml up -d && docker image prune -f" "docker compose -f /opt/rustycms/docker-compose.yml up -d && docker image prune -f"
rm /tmp/deploy_key rm /tmp/deploy_key

5
.gitignore vendored
View File

@@ -19,10 +19,7 @@ content/de/page/*
content/assets/* content/assets/*
!content/assets/mountains-w300.webp !content/assets/mountains-w300.webp
# Types: ignore all except demo types (demo + page for demo references) # Types are tracked in git and deployed to server via CI/CD
types/*
!types/demo.json5
!types/page.json5
.history .history
.cursor .cursor

60
types/blog_post.json5 Normal file
View File

@@ -0,0 +1,60 @@
{
"name": "blog_post",
"description": "Simple blog post with title, body, tags and publish status",
"tags": [
"content",
"blog"
],
"category": "content",
"fieldOrder": [
"author",
"body",
"created_at",
"excerpt",
"published",
"tags",
"title"
],
"fields": {
"author": {
"type": "string",
"description": "Author name"
},
"body": {
"type": "markdown",
"required": true,
"description": "Main content (Markdown)",
"minLength": 1
},
"created_at": {
"type": "datetime",
"auto": true,
"readonly": true,
"description": "Creation timestamp (auto-generated)"
},
"excerpt": {
"type": "string",
"description": "Short summary for previews",
"maxLength": 500
},
"published": {
"type": "boolean",
"default": false,
"description": "Whether the post is publicly visible"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Categorisation tags"
},
"title": {
"type": "string",
"required": true,
"description": "Title of the blog post",
"minLength": 1,
"maxLength": 200
}
}
}

23
types/calendar.json5 Normal file
View File

@@ -0,0 +1,23 @@
{
name: "calendar",
description: "Calendar (entries identified by slug).",
tags: [
"content"
],
category: "content",
fields: {
items: {
type: "array",
items: {
type: "reference",
collection: "calendar_item",
},
description: "Calendar entries (references to calendar_item)",
},
fromPost: {
type: "boolean",
default: false,
description: "Include entries from post",
},
},
}

33
types/calendar_item.json5 Normal file
View File

@@ -0,0 +1,33 @@
{
name: "calendar_item",
description: "Single calendar entry (event) with title, description and time.",
tags: [
"content"
],
category: "content",
fields: {
title: {
type: "string",
required: true,
description: "Event title",
section: "Event",
},
description: {
type: "string",
description: "Event description",
section: "Event",
},
terminZeit: {
type: "datetime",
required: true,
description: "Event date/time",
section: "Date & link",
},
link: {
type: "reference",
collection: "link",
description: "Optional link (e.g. registration or details page)",
section: "Date & link",
},
},
}

62
types/campaign.json5 Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "campaign",
"description": "Campaign with URL pattern and optional content",
"tags": [
"marketing",
"config"
],
"category": "config",
"fields": {
"css": {
"type": "string",
"description": "Optional CSS",
"widget": "code",
"codeLanguage": "css"
},
"html": {
"type": "html",
"description": "HTML content"
},
"insertHtml": {
"type": "string",
"required": true,
"default": "beforeend",
"enum": [
"afterbegin",
"beforeend",
"afterend",
"beforebegin",
"replace"
],
"description": "Position relative to selector"
},
"javascript": {
"type": "string",
"description": "Optional JavaScript",
"widget": "code",
"codeLanguage": "javascript"
},
"medias": {
"type": "array",
"items": {
"type": "reference",
"collection": "img"
},
"description": "Media (images)"
},
"selector": {
"type": "string",
"required": true,
"description": "CSS selector where content is inserted"
},
"timeUntil": {
"type": "datetime",
"description": "Time limit (until when the campaign runs)"
},
"urlPattern": {
"type": "string",
"required": true,
"description": "URL pattern (e.g. regex) for campaign usage"
}
}
}

27
types/campaigns.json5 Normal file
View File

@@ -0,0 +1,27 @@
{
// Corresponds to CF_Campaigns / Contentful_Campaigns.ts
name: "campaigns",
description: "Global campaign list and enable flag",
tags: ["marketing", "config"],
category: "config",
fields: {
id: {
type: "string",
required: true,
description: "Unique ID (e.g. campaigns-global)",
},
campaigns: {
type: "array",
items: {
type: "reference",
collection: "campaign",
},
description: "Campaign list",
},
enable: {
type: "boolean",
default: true,
description: "Campaigns enabled",
},
},
}

View File

@@ -0,0 +1,86 @@
{
// Reusable partial (CF_ComponentLayout) no own collection.
name: "component_layout",
description: "Reusable grid layout (mobile/tablet/desktop columns)",
tags: [
"layout",
"partial"
],
category: "layout",
reusable: true,
fields: {
breakout: {
type: "boolean",
default: false,
description: "Breakout layout (full width)",
},
mobile: {
type: "string",
required: true,
enum: [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
default: "12",
description: "Width on mobile (112)",
},
tablet: {
type: "string",
enum: [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
description: "Width on tablet (optional)",
},
desktop: {
type: "string",
enum: [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12"
],
description: "Width on desktop (optional)",
},
spaceBottom: {
type: "number",
enum: [
0,
0.5,
1,
1.5,
2
],
default: 0,
description: "Space below (rem)",
},
},
}

150
types/content_layout.json5 Normal file
View File

@@ -0,0 +1,150 @@
{
// Equivalent to: CF_Content interface
// Base type for pages/posts with a 3-row content layout
name: "content_layout",
description: "Base layout with 3 content rows (inherited by page, post, etc.)",
tags: ["layout", "partial"],
category: "layout",
fields: {
row1JustifyContent: {
type: "string",
enum: [
"start",
"end",
"center",
"between",
"around",
"evenly"
],
default: "start",
description: "Justify content for row 1",
},
row1AlignItems: {
type: "string",
enum: [
"start",
"end",
"center",
"baseline",
"stretch"
],
default: "stretch",
description: "Align items for row 1",
},
row1Content: {
type: "array",
items: {
type: "reference",
collections: [
"markdown",
"post_overview",
"html",
"headline",
"image",
"quote",
"youtube_video",
"image_gallery",
"iframe",
"searchable_text",
"fullwidth_banner",
"list",
"link_list",
"calendar"
],
},
description: "Content components for row 1 (Markdown, Post Overview, HTML, Headline, Image, …)",
},
row2JustifyContent: {
type: "string",
enum: [
"start",
"end",
"center",
"between",
"around",
"evenly"
],
default: "start",
},
row2AlignItems: {
type: "string",
enum: [
"start",
"end",
"center",
"baseline",
"stretch"
],
default: "stretch",
},
row2Content: {
type: "array",
items: {
type: "reference",
collections: [
"markdown",
"post_overview",
"html",
"headline",
"image",
"quote",
"youtube_video",
"image_gallery",
"iframe",
"searchable_text",
"fullwidth_banner",
"list",
"link_list",
"calendar"
],
},
description: "Content components for row 2",
},
row3JustifyContent: {
type: "string",
enum: [
"start",
"end",
"center",
"between",
"around",
"evenly"
],
default: "start",
},
row3AlignItems: {
type: "string",
enum: [
"start",
"end",
"center",
"baseline",
"stretch"
],
default: "stretch",
},
row3Content: {
type: "array",
items: {
type: "reference",
collections: [
"markdown",
"post_overview",
"html",
"headline",
"image",
"quote",
"youtube_video",
"image_gallery",
"iframe",
"searchable_text",
"fullwidth_banner",
"list",
"link_list",
"calendar"
],
},
description: "Content components for row 3",
},
}
}

17
types/footer.json5 Normal file
View File

@@ -0,0 +1,17 @@
{
// Corresponds to CF_Footer extends CF_Content / Contentful_Footer.ts
name: "footer",
description: "Footer with content layout",
tags: ["layout", "config"],
category: "config",
extends: [
"content_layout"
],
fields: {
id: {
type: "string",
required: true,
description: "Unique footer ID (e.g. for navigation)",
},
},
}

View File

@@ -0,0 +1,43 @@
{
// Equivalent to: CF_FullwidthBanner interface
name: "fullwidth_banner",
description: "Full-width banner with dark/light variant",
tags: [
"component",
"layout"
],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal name"
},
variant: {
type: "string",
required: true,
enum: [
"dark",
"light"
],
default: "light",
description: "Color variant"
},
headline: {
type: "string",
required: true
},
subheadline: {
type: "string"
},
text: {
type: "richtext",
description: "Banner body text"
},
image: {
type: "referenceOrInline",
collection: "img",
description: "Banner image"
},
}
}

37
types/headline.json5 Normal file
View File

@@ -0,0 +1,37 @@
{
// Corresponds to CF_ComponentHeadline / Contentful_Headline.ts
name: "headline",
description: "Headline component with optional layout",
tags: ["component", "layout"],
category: "components",
fields: {
internal: {
type: "string",
required: true,
description: "Internal key",
},
text: {
type: "string",
required: true,
description: "Headline text",
},
tag: {
type: "string",
required: true,
enum: ["h1", "h2", "h3", "h4", "h5", "h6"],
default: "h2",
description: "HTML tag for headline",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
align: {
type: "string",
enum: ["left", "center", "right"],
default: "left",
description: "Text alignment",
},
},
}

24
types/html.json5 Normal file
View File

@@ -0,0 +1,24 @@
{
// Corresponds to CF_HTML / Contentful_Html.ts
name: "html",
description: "Raw HTML content block with optional layout",
tags: ["content", "component"],
category: "components",
fields: {
id: {
type: "string",
required: true,
description: "Unique component ID",
},
html: {
type: "html",
required: true,
description: "HTML content (safely embedded)",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
},
}

34
types/iframe.json5 Normal file
View File

@@ -0,0 +1,34 @@
{
// Corresponds to CF_ComponentIframe / Contentful_Iframe.ts
name: "iframe",
description: "Embedded iframe with URL and optional title",
tags: ["media", "component"],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal component name",
},
content: {
type: "string",
required: true,
description: "Description/content for iframe",
},
iframe: {
type: "string",
required: true,
description: "Iframe URL or embed code",
},
overlayImage: {
type: "reference",
collection: "img",
description: "Optional overlay image (reference to img)",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
},
}

38
types/image.json5 Normal file
View File

@@ -0,0 +1,38 @@
{
name: "image",
description: "Image component with inline img (src + description) and layout",
tags: ["component", "media"],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal component name",
},
img: {
type: "referenceOrInline",
collection: "img",
required: true,
description: "Image: Slug (Referenz) oder Inline-Objekt (gleiche Felder wie img)",
},
caption: {
type: "string",
description: "Image caption",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid)",
},
maxWidth: {
type: "integer",
description: "Max width in px",
},
aspectRatio: {
type: "string",
description: "Aspect ratio",
enum: ["1:1", "4:3", "3:2", "16:9", "21:9", "2:3", "3:4"],
default: "4:3",
},
},
}

35
types/image_gallery.json5 Normal file
View File

@@ -0,0 +1,35 @@
{
// Corresponds to CF_ImageGallery / Contentful_ImageGallery.ts
name: "image_gallery",
description: "Gallery of image references",
tags: [
"media",
"component"
],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal gallery name",
},
images: {
type: "array",
required: true,
items: {
type: "referenceOrInline",
collection: "img",
},
description: "Images (references to img)",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
description: {
type: "string",
description: "Optional gallery description",
},
},
}

21
types/img.json5 Normal file
View File

@@ -0,0 +1,21 @@
{
name: "img",
description: "Image asset with URL and optional description (alt text)",
tags: [
"asset",
"media"
],
category: "components",
fields: {
description: {
type: "string",
description: "Alt text / description",
},
src: {
type: "string",
required: true,
description: "Image URL",
widget: "imageUrl",
},
},
}

75
types/link.json5 Normal file
View File

@@ -0,0 +1,75 @@
{
// Corresponds to CF_Link / Contentful_Link.ts
name: "link",
description: "Internal or external link with label and optional icon",
tags: ["navigation", "component"],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal link name",
},
internal: {
type: "string",
required: true,
description: "Internal key",
},
linkName: {
type: "string",
required: true,
description: "Display name (e.g. in navigation)",
},
url: {
type: "string",
required: true,
description: "Target URL",
},
icon: {
type: "string",
description: "Icon identifier",
},
color: {
type: "string",
description: "Color (e.g. for buttons)",
},
newTab: {
type: "boolean",
default: false,
description: "Open in new tab",
},
external: {
type: "boolean",
default: false,
description: "External link",
},
description: {
type: "string",
description: "Description",
},
alt: {
type: "string",
description: "Alt text (e.g. for icon)",
},
showText: {
type: "boolean",
default: true,
description: "Show text",
},
author: {
type: "string",
required: true,
description: "Author (e.g. for sources)",
},
date: {
type: "string",
required: true,
description: "Date (e.g. publication)",
},
source: {
type: "string",
required: true,
description: "Source",
},
},
}

23
types/link_list.json5 Normal file
View File

@@ -0,0 +1,23 @@
{
// Corresponds to CF_Link_List / Contentful_Link_List.ts (componentLinkList)
name: "link_list",
description: "List of links with optional headline",
tags: ["navigation", "component"],
category: "components",
fields: {
headline: {
type: "string",
required: true,
description: "Link list headline",
},
links: {
type: "array",
required: true,
items: {
type: "reference",
collection: "link",
},
description: "Links (references to link)",
},
},
}

20
types/list.json5 Normal file
View File

@@ -0,0 +1,20 @@
{
// Corresponds to CF_ComponentList / Contentful_List.ts
name: "list",
description: "Simple list of string items",
tags: ["content", "component"],
category: "components",
fields: {
internal: {
type: "string",
required: true,
description: "Internal key",
},
item: {
type: "array",
required: true,
items: { type: "string" },
description: "List entries",
},
},
}

29
types/markdown.json5 Normal file
View File

@@ -0,0 +1,29 @@
{
// Corresponds to CF_Markdown / Contentful_Markdown.ts
name: "markdown",
description: "Markdown content block with optional layout",
tags: ["content", "component"],
category: "components",
fields: {
name: {
type: "string",
required: true,
description: "Internal component name",
},
content: {
type: "textOrRef",
description: "Markdown/body text: either inline or file reference (file:path, e.g. file:slug.content.md)",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
alignment: {
type: "string",
enum: ["left", "center", "right"],
default: "left",
description: "Text alignment",
},
},
}

28
types/navigation.json5 Normal file
View File

@@ -0,0 +1,28 @@
{
// Corresponds to CF_Navigation / Contentful_Navigation.ts
name: "navigation",
description: "Navigation block with links (header/footer)",
tags: ["navigation", "layout"],
category: "config",
fields: {
name: {
type: "string",
required: true,
description: "Display name of navigation",
},
internal: {
type: "string",
required: true,
description: "Internal key (e.g. navigation-header, navigation-footer)",
},
links: {
type: "array",
required: true,
items: {
type: "reference",
collections: ["link", "page", "post"],
},
description: "Navigation entries (references to link, page or post)",
},
},
}

50
types/page_config.json5 Normal file
View File

@@ -0,0 +1,50 @@
{
// Corresponds to CF_PageConfig / Contentful_PageConfig.ts
name: "page_config",
description: "Global page config (logo, footer, etc.)",
tags: [
"config",
"layout"
],
category: "config",
fields: {
logo: {
type: "referenceOrInline",
collection: "img",
required: true,
description: "Logo image (reference to img)",
},
footerText1: {
type: "string",
required: true,
description: "Footer text (e.g. copyright)",
},
seoTitle: {
type: "string",
required: true,
description: "Default SEO title for website",
},
seoDescription: {
type: "string",
required: true,
description: "Default meta description",
},
blogTagPageHeadline: {
type: "string",
description: "Headline for tag page (blog)",
},
blogPostsPageHeadline: {
type: "string",
description: "Headline for blog overview page",
},
blogPostsPageSubHeadline: {
type: "string",
description: "Subheadline for blog overview page",
},
website: {
type: "string",
required: true,
description: "Website-URL",
},
},
}

94
types/post.json5 Normal file
View File

@@ -0,0 +1,94 @@
{
"name": "post",
"description": "Blog post with layout, SEO and optional tags",
"tags": [
"content",
"blog"
],
"category": "content",
"extends": [
"content_layout",
"seo"
],
"defaultSort": "_updated",
"defaultOrder": "desc",
"fieldOrder": [
"slug",
"headline",
"subheadline",
"excerpt",
"content",
"postImage",
"postTag",
"created",
"date",
"icon",
"important",
"linkName",
"showCommentSection"
],
"fields": {
"slug": {
"type": "string",
"required": true,
"unique": true
},
"headline": {
"type": "string",
"required": true
},
"subheadline": {
"type": "string"
},
"excerpt": {
"type": "string",
"description": "Short summary for previews",
"maxLength": 500
},
"content": {
"type": "markdown",
"required": true,
"description": "Post body (Markdown)"
},
"postImage": {
"type": "referenceOrInline",
"collection": "img",
"description": "Featured image"
},
"postTag": {
"type": "array",
"items": {
"type": "reference",
"collection": "tag"
},
"description": "Associated tags"
},
"created": {
"type": "datetime",
"auto": true,
"readonly": true
},
"date": {
"type": "datetime",
"description": "Optional display date"
},
"icon": {
"type": "string"
},
"important": {
"type": "boolean",
"required": true,
"default": false,
"description": "Mark post as important"
},
"linkName": {
"type": "string",
"required": true
},
"showCommentSection": {
"type": "boolean",
"default": true,
"description": "Show comment section (default: true)"
}
}
}

62
types/post_overview.json5 Normal file
View File

@@ -0,0 +1,62 @@
{
// Corresponds to CF_Post_Overview / Contentful_Post_Overview.ts (extends CF_Content, CF_SEO)
name: "post_overview",
description: "Post listing page with layout and SEO",
tags: ["content", "blog"],
category: "content",
extends: [
"content_layout",
"seo"
],
fields: {
id: {
type: "string",
required: true,
description: "Unique post overview ID",
},
headline: {
type: "string",
required: true,
description: "Headline",
},
text: {
type: "richtext",
description: "Intro text",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid)",
},
allPosts: {
type: "boolean",
default: false,
description: "Show all posts (otherwise filter)",
},
filterByTag: {
type: "array",
items: {
type: "reference",
collection: "tag",
},
description: "Only posts with these tags",
},
posts: {
type: "array",
items: {
type: "reference",
collection: "post",
},
description: "Fixed list of posts (when not allPosts)",
},
numberItems: {
type: "integer",
description: "Max. number of displayed entries",
},
design: {
type: "string",
enum: ["cards", "list"],
description: "Display as cards or list",
},
},
}

75
types/product.json5 Normal file
View File

@@ -0,0 +1,75 @@
{
"name": "product",
"description": "Product with SKU, price and optional fields (strict schema)",
"tags": [
"content",
"ecommerce"
],
"category": "content",
"strict": true,
"fields": {
"category": {
"type": "string",
"required": true,
"enum": [
"electronics",
"clothing",
"books",
"food",
"other"
],
"description": "Product category"
},
"created_at": {
"type": "datetime",
"auto": true,
"readonly": true
},
"description": {
"type": "string",
"description": "Detailed product description",
"maxLength": 5000
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product image URLs (110)",
"minItems": 1,
"maxItems": 10
},
"in_stock": {
"type": "boolean",
"default": true,
"description": "Whether the product is in stock"
},
"price": {
"type": "number",
"required": true,
"description": "Price in EUR",
"min": 0.0
},
"rating": {
"type": "number",
"nullable": true,
"description": "Average customer rating (null if unrated)",
"min": 0.0,
"max": 5.0
},
"sku": {
"type": "string",
"required": true,
"unique": true,
"description": "Stock Keeping Unit (e.g. EL-1234, BOOK-00042)",
"pattern": "^[A-Z]{2,4}-\\d{3,6}$"
},
"title": {
"type": "string",
"required": true,
"description": "Product name",
"minLength": 1,
"maxLength": 200
}
}
}

31
types/quote.json5 Normal file
View File

@@ -0,0 +1,31 @@
{
// Corresponds to CF_Quote / Contentful_Quote.ts
name: "quote",
description: "Quote block with author and optional variant",
tags: ["content", "component"],
category: "components",
fields: {
quote: {
type: "string",
required: true,
description: "Quote text",
},
author: {
type: "string",
required: true,
description: "Author or source of quote",
},
variant: {
type: "string",
required: true,
enum: ["left", "right"],
default: "left",
description: "Quote alignment",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
},
}

View File

@@ -0,0 +1,44 @@
{
// Corresponds to CF_ComponentSearchableText / Contentful_SearchableText.ts
name: "searchable_text",
description: "Searchable text with optional tag whitelist",
tags: ["content", "component", "search"],
category: "components",
fields: {
id: {
type: "string",
required: true,
description: "Unique component ID",
},
tagWhitelist: {
type: "array",
items: {
type: "reference",
collection: "tag",
},
description: "Optional: only search content with these tags",
},
textFragments: {
type: "array",
required: true,
items: {
type: "reference",
collection: "text_fragment",
},
description: "Searchable text fragments (references to text_fragment)",
},
title: {
type: "string",
description: "Search component title",
},
description: {
type: "string",
description: "Description",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
},
}

30
types/seo.json5 Normal file
View File

@@ -0,0 +1,30 @@
{
// Equivalent to: CF_SEO interface
name: "seo",
description: "SEO meta (title, robots, description; inherited by page/post)",
tags: ["seo", "partial"],
category: "layout",
fields: {
seoTitle: {
type: "string",
required: true,
description: "SEO page title"
},
seoMetaRobots: {
type: "string",
enum: [
"index, follow",
"noindex, follow",
"index, nofollow",
"noindex, nofollow"
],
default: "index, follow",
description: "Robots meta directive",
},
seoDescription: {
type: "string",
maxLength: 160,
description: "Meta description for search engines"
},
}
}

14
types/tag.json5 Normal file
View File

@@ -0,0 +1,14 @@
{
// Equivalent to: CF_Tag interface
name: "tag",
description: "Tag for categorising content",
tags: ["taxonomy", "config"],
category: "config",
fields: {
name: {
type: "string",
required: true,
unique: true
},
}
}

32
types/text_fragment.json5 Normal file
View File

@@ -0,0 +1,32 @@
{
// Corresponds to CF_TextFragment / Contentful_TextFragment.ts
name: "text_fragment",
description: "Reusable text fragment with optional tag references",
tags: ["content", "component"],
category: "components",
fields: {
id: {
type: "string",
required: true,
description: "Unique fragment ID",
},
tags: {
type: "array",
items: {
type: "reference",
collection: "tag",
},
description: "Optional tags for categorisation",
},
title: {
type: "string",
required: true,
description: "Text fragment title",
},
text: {
type: "richtext",
required: true,
description: "Searchable text content",
},
},
}

19
types/top_banner.json5 Normal file
View File

@@ -0,0 +1,19 @@
{
// Corresponds to CF_TopBanner / Contentful_TopBanner.ts
name: "top_banner",
description: "Top-of-page banner or notice text",
tags: ["component", "layout"],
category: "components",
fields: {
id: {
type: "string",
required: true,
description: "Unique ID",
},
text: {
type: "string",
required: true,
description: "Banner text (e.g. notice at top of page)",
},
},
}

View File

@@ -0,0 +1,21 @@
{
name: "translation_bundle",
description: "Übersetzungs-Bundle: ein Objekt mit beliebigen Keys (String) und Werten (String). Für i18n / UI-Texte.",
tags: [
"content",
"i18n"
],
category: "content",
partial: false,
fields: {
// object mit additionalProperties = beliebige Keys, Werte müssen Strings sein
strings: {
type: "object",
required: true,
additionalProperties: {
type: "string"
},
description: "Map: Übersetzungsschlüssel (key) → Text (value). Z. B. { \"searchable_text_help\": \"Hier könnt ihr...\", \"footer_copyright\": \"© 2024\" }",
},
},
}

38
types/youtube_video.json5 Normal file
View File

@@ -0,0 +1,38 @@
{
// Corresponds to CF_YoutubeVideo / Contentful_YoutubeVideo.ts
name: "youtube_video",
description: "Embedded YouTube video with optional params",
tags: [
"media",
"component"
],
category: "components",
fields: {
id: {
type: "string",
description: "Unique ID (optional, otherwise _slug)",
},
youtubeId: {
type: "string",
required: true,
description: "YouTube video ID (e.g. from URL ?v=VIDEO_ID)",
},
params: {
type: "string",
description: "Optional URL params (e.g. start=30, autoplay=1)",
},
title: {
type: "string",
description: "Video title",
},
description: {
type: "string",
description: "Short description",
},
layout: {
type: "object",
useFields: "component_layout",
description: "Column width (grid) like CF_ComponentLayout",
},
},
}