feat: payload cms
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Failing after 1m13s
Build & Deploy / 🏗️ Build (push) Failing after 5m53s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Smoke Test (push) Has been skipped
Build & Deploy / ⚡ Lighthouse (push) Has been skipped
Build & Deploy / ♿ WCAG (push) Has been skipped
Build & Deploy / 🛡️ Quality Gates (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s

This commit is contained in:
2026-02-24 02:28:48 +01:00
parent 41cfe19cbf
commit a5d77fc69b
89 changed files with 25282 additions and 1903 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,356 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
export async function up({ db }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_posts_locale" AS ENUM('en', 'de');
CREATE TYPE "public"."enum_posts_status" AS ENUM('draft', 'published');
CREATE TYPE "public"."enum__posts_v_version_locale" AS ENUM('en', 'de');
CREATE TYPE "public"."enum__posts_v_version_status" AS ENUM('draft', 'published');
CREATE TYPE "public"."enum_form_submissions_type" AS ENUM('contact', 'product_quote');
CREATE TYPE "public"."enum_products_locale" AS ENUM('en', 'de');
CREATE TYPE "public"."enum_products_status" AS ENUM('draft', 'published');
CREATE TYPE "public"."enum__products_v_version_locale" AS ENUM('en', 'de');
CREATE TYPE "public"."enum__products_v_version_status" AS ENUM('draft', 'published');
CREATE TABLE "users_sessions" (
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"created_at" timestamp(3) with time zone,
"expires_at" timestamp(3) with time zone NOT NULL
);
CREATE TABLE "users" (
"id" serial PRIMARY KEY NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"email" varchar NOT NULL,
"reset_password_token" varchar,
"reset_password_expiration" timestamp(3) with time zone,
"salt" varchar,
"hash" varchar,
"login_attempts" numeric DEFAULT 0,
"lock_until" timestamp(3) with time zone
);
CREATE TABLE "media" (
"id" serial PRIMARY KEY NOT NULL,
"alt" varchar NOT NULL,
"caption" varchar,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"url" varchar,
"thumbnail_u_r_l" varchar,
"filename" varchar,
"mime_type" varchar,
"filesize" numeric,
"width" numeric,
"height" numeric,
"focal_x" numeric,
"focal_y" numeric,
"sizes_thumbnail_url" varchar,
"sizes_thumbnail_width" numeric,
"sizes_thumbnail_height" numeric,
"sizes_thumbnail_mime_type" varchar,
"sizes_thumbnail_filesize" numeric,
"sizes_thumbnail_filename" varchar,
"sizes_card_url" varchar,
"sizes_card_width" numeric,
"sizes_card_height" numeric,
"sizes_card_mime_type" varchar,
"sizes_card_filesize" numeric,
"sizes_card_filename" varchar,
"sizes_hero_url" varchar,
"sizes_hero_width" numeric,
"sizes_hero_height" numeric,
"sizes_hero_mime_type" varchar,
"sizes_hero_filesize" numeric,
"sizes_hero_filename" varchar,
"sizes_hero_mobile_url" varchar,
"sizes_hero_mobile_width" numeric,
"sizes_hero_mobile_height" numeric,
"sizes_hero_mobile_mime_type" varchar,
"sizes_hero_mobile_filesize" numeric,
"sizes_hero_mobile_filename" varchar
);
CREATE TABLE "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" varchar,
"slug" varchar,
"excerpt" varchar,
"date" timestamp(3) with time zone,
"featured_image_id" integer,
"locale" "enum_posts_locale" DEFAULT 'en',
"category" varchar,
"content" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"_status" "enum_posts_status" DEFAULT 'draft'
);
CREATE TABLE "_posts_v" (
"id" serial PRIMARY KEY NOT NULL,
"parent_id" integer,
"version_title" varchar,
"version_slug" varchar,
"version_excerpt" varchar,
"version_date" timestamp(3) with time zone,
"version_featured_image_id" integer,
"version_locale" "enum__posts_v_version_locale" DEFAULT 'en',
"version_category" varchar,
"version_content" jsonb,
"version_updated_at" timestamp(3) with time zone,
"version_created_at" timestamp(3) with time zone,
"version__status" "enum__posts_v_version_status" DEFAULT 'draft',
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"latest" boolean
);
CREATE TABLE "form_submissions" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar NOT NULL,
"email" varchar NOT NULL,
"type" "enum_form_submissions_type" NOT NULL,
"product_name" varchar,
"message" varchar NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "products_categories" (
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"category" varchar
);
CREATE TABLE "products" (
"id" serial PRIMARY KEY NOT NULL,
"title" varchar,
"sku" varchar,
"slug" varchar,
"description" varchar,
"locale" "enum_products_locale" DEFAULT 'de',
"application" jsonb,
"content" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"_status" "enum_products_status" DEFAULT 'draft'
);
CREATE TABLE "products_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"media_id" integer
);
CREATE TABLE "_products_v_version_categories" (
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL,
"id" serial PRIMARY KEY NOT NULL,
"category" varchar,
"_uuid" varchar
);
CREATE TABLE "_products_v" (
"id" serial PRIMARY KEY NOT NULL,
"parent_id" integer,
"version_title" varchar,
"version_sku" varchar,
"version_slug" varchar,
"version_description" varchar,
"version_locale" "enum__products_v_version_locale" DEFAULT 'de',
"version_application" jsonb,
"version_content" jsonb,
"version_updated_at" timestamp(3) with time zone,
"version_created_at" timestamp(3) with time zone,
"version__status" "enum__products_v_version_status" DEFAULT 'draft',
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"latest" boolean
);
CREATE TABLE "_products_v_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"media_id" integer
);
CREATE TABLE "payload_kv" (
"id" serial PRIMARY KEY NOT NULL,
"key" varchar NOT NULL,
"data" jsonb NOT NULL
);
CREATE TABLE "payload_locked_documents" (
"id" serial PRIMARY KEY NOT NULL,
"global_slug" varchar,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "payload_locked_documents_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"users_id" integer,
"media_id" integer,
"posts_id" integer,
"form_submissions_id" integer,
"products_id" integer
);
CREATE TABLE "payload_preferences" (
"id" serial PRIMARY KEY NOT NULL,
"key" varchar,
"value" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "payload_preferences_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"users_id" integer
);
CREATE TABLE "payload_migrations" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar,
"batch" numeric,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "posts" ADD CONSTRAINT "posts_featured_image_id_media_id_fk" FOREIGN KEY ("featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "_posts_v" ADD CONSTRAINT "_posts_v_parent_id_posts_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."posts"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "_posts_v" ADD CONSTRAINT "_posts_v_version_featured_image_id_media_id_fk" FOREIGN KEY ("version_featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "products_categories" ADD CONSTRAINT "products_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "products_rels" ADD CONSTRAINT "products_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "products_rels" ADD CONSTRAINT "products_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "_products_v_version_categories" ADD CONSTRAINT "_products_v_version_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_products_v"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "_products_v" ADD CONSTRAINT "_products_v_parent_id_products_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."products"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "_products_v_rels" ADD CONSTRAINT "_products_v_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."_products_v"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "_products_v_rels" ADD CONSTRAINT "_products_v_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_posts_fk" FOREIGN KEY ("posts_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_form_submissions_fk" FOREIGN KEY ("form_submissions_id") REFERENCES "public"."form_submissions"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_products_fk" FOREIGN KEY ("products_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order");
CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id");
CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at");
CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at");
CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email");
CREATE INDEX "media_updated_at_idx" ON "media" USING btree ("updated_at");
CREATE INDEX "media_created_at_idx" ON "media" USING btree ("created_at");
CREATE UNIQUE INDEX "media_filename_idx" ON "media" USING btree ("filename");
CREATE INDEX "media_sizes_thumbnail_sizes_thumbnail_filename_idx" ON "media" USING btree ("sizes_thumbnail_filename");
CREATE INDEX "media_sizes_card_sizes_card_filename_idx" ON "media" USING btree ("sizes_card_filename");
CREATE INDEX "media_sizes_hero_sizes_hero_filename_idx" ON "media" USING btree ("sizes_hero_filename");
CREATE INDEX "media_sizes_hero_mobile_sizes_hero_mobile_filename_idx" ON "media" USING btree ("sizes_hero_mobile_filename");
CREATE UNIQUE INDEX "posts_slug_idx" ON "posts" USING btree ("slug");
CREATE INDEX "posts_featured_image_idx" ON "posts" USING btree ("featured_image_id");
CREATE INDEX "posts_updated_at_idx" ON "posts" USING btree ("updated_at");
CREATE INDEX "posts_created_at_idx" ON "posts" USING btree ("created_at");
CREATE INDEX "posts__status_idx" ON "posts" USING btree ("_status");
CREATE INDEX "_posts_v_parent_idx" ON "_posts_v" USING btree ("parent_id");
CREATE INDEX "_posts_v_version_version_slug_idx" ON "_posts_v" USING btree ("version_slug");
CREATE INDEX "_posts_v_version_version_featured_image_idx" ON "_posts_v" USING btree ("version_featured_image_id");
CREATE INDEX "_posts_v_version_version_updated_at_idx" ON "_posts_v" USING btree ("version_updated_at");
CREATE INDEX "_posts_v_version_version_created_at_idx" ON "_posts_v" USING btree ("version_created_at");
CREATE INDEX "_posts_v_version_version__status_idx" ON "_posts_v" USING btree ("version__status");
CREATE INDEX "_posts_v_created_at_idx" ON "_posts_v" USING btree ("created_at");
CREATE INDEX "_posts_v_updated_at_idx" ON "_posts_v" USING btree ("updated_at");
CREATE INDEX "_posts_v_latest_idx" ON "_posts_v" USING btree ("latest");
CREATE INDEX "form_submissions_updated_at_idx" ON "form_submissions" USING btree ("updated_at");
CREATE INDEX "form_submissions_created_at_idx" ON "form_submissions" USING btree ("created_at");
CREATE INDEX "products_categories_order_idx" ON "products_categories" USING btree ("_order");
CREATE INDEX "products_categories_parent_id_idx" ON "products_categories" USING btree ("_parent_id");
CREATE UNIQUE INDEX "products_sku_idx" ON "products" USING btree ("sku");
CREATE INDEX "products_updated_at_idx" ON "products" USING btree ("updated_at");
CREATE INDEX "products_created_at_idx" ON "products" USING btree ("created_at");
CREATE INDEX "products__status_idx" ON "products" USING btree ("_status");
CREATE INDEX "products_rels_order_idx" ON "products_rels" USING btree ("order");
CREATE INDEX "products_rels_parent_idx" ON "products_rels" USING btree ("parent_id");
CREATE INDEX "products_rels_path_idx" ON "products_rels" USING btree ("path");
CREATE INDEX "products_rels_media_id_idx" ON "products_rels" USING btree ("media_id");
CREATE INDEX "_products_v_version_categories_order_idx" ON "_products_v_version_categories" USING btree ("_order");
CREATE INDEX "_products_v_version_categories_parent_id_idx" ON "_products_v_version_categories" USING btree ("_parent_id");
CREATE INDEX "_products_v_parent_idx" ON "_products_v" USING btree ("parent_id");
CREATE INDEX "_products_v_version_version_sku_idx" ON "_products_v" USING btree ("version_sku");
CREATE INDEX "_products_v_version_version_updated_at_idx" ON "_products_v" USING btree ("version_updated_at");
CREATE INDEX "_products_v_version_version_created_at_idx" ON "_products_v" USING btree ("version_created_at");
CREATE INDEX "_products_v_version_version__status_idx" ON "_products_v" USING btree ("version__status");
CREATE INDEX "_products_v_created_at_idx" ON "_products_v" USING btree ("created_at");
CREATE INDEX "_products_v_updated_at_idx" ON "_products_v" USING btree ("updated_at");
CREATE INDEX "_products_v_latest_idx" ON "_products_v" USING btree ("latest");
CREATE INDEX "_products_v_rels_order_idx" ON "_products_v_rels" USING btree ("order");
CREATE INDEX "_products_v_rels_parent_idx" ON "_products_v_rels" USING btree ("parent_id");
CREATE INDEX "_products_v_rels_path_idx" ON "_products_v_rels" USING btree ("path");
CREATE INDEX "_products_v_rels_media_id_idx" ON "_products_v_rels" USING btree ("media_id");
CREATE UNIQUE INDEX "payload_kv_key_idx" ON "payload_kv" USING btree ("key");
CREATE INDEX "payload_locked_documents_global_slug_idx" ON "payload_locked_documents" USING btree ("global_slug");
CREATE INDEX "payload_locked_documents_updated_at_idx" ON "payload_locked_documents" USING btree ("updated_at");
CREATE INDEX "payload_locked_documents_created_at_idx" ON "payload_locked_documents" USING btree ("created_at");
CREATE INDEX "payload_locked_documents_rels_order_idx" ON "payload_locked_documents_rels" USING btree ("order");
CREATE INDEX "payload_locked_documents_rels_parent_idx" ON "payload_locked_documents_rels" USING btree ("parent_id");
CREATE INDEX "payload_locked_documents_rels_path_idx" ON "payload_locked_documents_rels" USING btree ("path");
CREATE INDEX "payload_locked_documents_rels_users_id_idx" ON "payload_locked_documents_rels" USING btree ("users_id");
CREATE INDEX "payload_locked_documents_rels_media_id_idx" ON "payload_locked_documents_rels" USING btree ("media_id");
CREATE INDEX "payload_locked_documents_rels_posts_id_idx" ON "payload_locked_documents_rels" USING btree ("posts_id");
CREATE INDEX "payload_locked_documents_rels_form_submissions_id_idx" ON "payload_locked_documents_rels" USING btree ("form_submissions_id");
CREATE INDEX "payload_locked_documents_rels_products_id_idx" ON "payload_locked_documents_rels" USING btree ("products_id");
CREATE INDEX "payload_preferences_key_idx" ON "payload_preferences" USING btree ("key");
CREATE INDEX "payload_preferences_updated_at_idx" ON "payload_preferences" USING btree ("updated_at");
CREATE INDEX "payload_preferences_created_at_idx" ON "payload_preferences" USING btree ("created_at");
CREATE INDEX "payload_preferences_rels_order_idx" ON "payload_preferences_rels" USING btree ("order");
CREATE INDEX "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" USING btree ("parent_id");
CREATE INDEX "payload_preferences_rels_path_idx" ON "payload_preferences_rels" USING btree ("path");
CREATE INDEX "payload_preferences_rels_users_id_idx" ON "payload_preferences_rels" USING btree ("users_id");
CREATE INDEX "payload_migrations_updated_at_idx" ON "payload_migrations" USING btree ("updated_at");
CREATE INDEX "payload_migrations_created_at_idx" ON "payload_migrations" USING btree ("created_at");`);
}
export async function down({ db }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
DROP TABLE "users_sessions" CASCADE;
DROP TABLE "users" CASCADE;
DROP TABLE "media" CASCADE;
DROP TABLE "posts" CASCADE;
DROP TABLE "_posts_v" CASCADE;
DROP TABLE "form_submissions" CASCADE;
DROP TABLE "products_categories" CASCADE;
DROP TABLE "products" CASCADE;
DROP TABLE "products_rels" CASCADE;
DROP TABLE "_products_v_version_categories" CASCADE;
DROP TABLE "_products_v" CASCADE;
DROP TABLE "_products_v_rels" CASCADE;
DROP TABLE "payload_kv" CASCADE;
DROP TABLE "payload_locked_documents" CASCADE;
DROP TABLE "payload_locked_documents_rels" CASCADE;
DROP TABLE "payload_preferences" CASCADE;
DROP TABLE "payload_preferences_rels" CASCADE;
DROP TABLE "payload_migrations" CASCADE;
DROP TYPE "public"."enum_posts_locale";
DROP TYPE "public"."enum_posts_status";
DROP TYPE "public"."enum__posts_v_version_locale";
DROP TYPE "public"."enum__posts_v_version_status";
DROP TYPE "public"."enum_form_submissions_type";
DROP TYPE "public"."enum_products_locale";
DROP TYPE "public"."enum_products_status";
DROP TYPE "public"."enum__products_v_version_locale";
DROP TYPE "public"."enum__products_v_version_status";`);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
export async function up({ db }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
DROP INDEX "products_sku_idx";
DROP INDEX "_products_v_version_version_sku_idx";`);
}
export async function down({ db }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
CREATE UNIQUE INDEX "products_sku_idx" ON "products" USING btree ("sku");
CREATE INDEX "_products_v_version_version_sku_idx" ON "_products_v" USING btree ("version_sku");`);
}

15
src/migrations/index.ts Normal file
View File

@@ -0,0 +1,15 @@
import * as migration_20260223_195005_products_collection from './20260223_195005_products_collection';
import * as migration_20260223_195151_remove_sku_unique from './20260223_195151_remove_sku_unique';
export const migrations = [
{
up: migration_20260223_195005_products_collection.up,
down: migration_20260223_195005_products_collection.down,
name: '20260223_195005_products_collection',
},
{
up: migration_20260223_195151_remove_sku_unique.up,
down: migration_20260223_195151_remove_sku_unique.down,
name: '20260223_195151_remove_sku_unique',
},
];

View File

@@ -0,0 +1,793 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:db-schema` to regenerate this file.
*/
import type {} from '@payloadcms/db-postgres';
import {
pgTable,
index,
uniqueIndex,
foreignKey,
integer,
varchar,
timestamp,
serial,
numeric,
jsonb,
boolean,
pgEnum,
} from '@payloadcms/db-postgres/drizzle/pg-core';
import { sql, relations } from '@payloadcms/db-postgres/drizzle';
export const enum_posts_locale = pgEnum('enum_posts_locale', ['en', 'de']);
export const enum_posts_status = pgEnum('enum_posts_status', ['draft', 'published']);
export const enum__posts_v_version_locale = pgEnum('enum__posts_v_version_locale', ['en', 'de']);
export const enum__posts_v_version_status = pgEnum('enum__posts_v_version_status', [
'draft',
'published',
]);
export const enum_form_submissions_type = pgEnum('enum_form_submissions_type', [
'contact',
'product_quote',
]);
export const enum_products_locale = pgEnum('enum_products_locale', ['en', 'de']);
export const enum_products_status = pgEnum('enum_products_status', ['draft', 'published']);
export const enum__products_v_version_locale = pgEnum('enum__products_v_version_locale', [
'en',
'de',
]);
export const enum__products_v_version_status = pgEnum('enum__products_v_version_status', [
'draft',
'published',
]);
export const users_sessions = pgTable(
'users_sessions',
{
_order: integer('_order').notNull(),
_parentID: integer('_parent_id').notNull(),
id: varchar('id').primaryKey(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 }),
expiresAt: timestamp('expires_at', {
mode: 'string',
withTimezone: true,
precision: 3,
}).notNull(),
},
(columns) => [
index('users_sessions_order_idx').on(columns._order),
index('users_sessions_parent_id_idx').on(columns._parentID),
foreignKey({
columns: [columns['_parentID']],
foreignColumns: [users.id],
name: 'users_sessions_parent_id_fk',
}).onDelete('cascade'),
],
);
export const users = pgTable(
'users',
{
id: serial('id').primaryKey(),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
email: varchar('email').notNull(),
resetPasswordToken: varchar('reset_password_token'),
resetPasswordExpiration: timestamp('reset_password_expiration', {
mode: 'string',
withTimezone: true,
precision: 3,
}),
salt: varchar('salt'),
hash: varchar('hash'),
loginAttempts: numeric('login_attempts', { mode: 'number' }).default(0),
lockUntil: timestamp('lock_until', { mode: 'string', withTimezone: true, precision: 3 }),
},
(columns) => [
index('users_updated_at_idx').on(columns.updatedAt),
index('users_created_at_idx').on(columns.createdAt),
uniqueIndex('users_email_idx').on(columns.email),
],
);
export const media = pgTable(
'media',
{
id: serial('id').primaryKey(),
alt: varchar('alt').notNull(),
caption: varchar('caption'),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
url: varchar('url'),
thumbnailURL: varchar('thumbnail_u_r_l'),
filename: varchar('filename'),
mimeType: varchar('mime_type'),
filesize: numeric('filesize', { mode: 'number' }),
width: numeric('width', { mode: 'number' }),
height: numeric('height', { mode: 'number' }),
focalX: numeric('focal_x', { mode: 'number' }),
focalY: numeric('focal_y', { mode: 'number' }),
sizes_thumbnail_url: varchar('sizes_thumbnail_url'),
sizes_thumbnail_width: numeric('sizes_thumbnail_width', { mode: 'number' }),
sizes_thumbnail_height: numeric('sizes_thumbnail_height', { mode: 'number' }),
sizes_thumbnail_mimeType: varchar('sizes_thumbnail_mime_type'),
sizes_thumbnail_filesize: numeric('sizes_thumbnail_filesize', { mode: 'number' }),
sizes_thumbnail_filename: varchar('sizes_thumbnail_filename'),
sizes_card_url: varchar('sizes_card_url'),
sizes_card_width: numeric('sizes_card_width', { mode: 'number' }),
sizes_card_height: numeric('sizes_card_height', { mode: 'number' }),
sizes_card_mimeType: varchar('sizes_card_mime_type'),
sizes_card_filesize: numeric('sizes_card_filesize', { mode: 'number' }),
sizes_card_filename: varchar('sizes_card_filename'),
sizes_hero_url: varchar('sizes_hero_url'),
sizes_hero_width: numeric('sizes_hero_width', { mode: 'number' }),
sizes_hero_height: numeric('sizes_hero_height', { mode: 'number' }),
sizes_hero_mimeType: varchar('sizes_hero_mime_type'),
sizes_hero_filesize: numeric('sizes_hero_filesize', { mode: 'number' }),
sizes_hero_filename: varchar('sizes_hero_filename'),
sizes_hero_mobile_url: varchar('sizes_hero_mobile_url'),
sizes_hero_mobile_width: numeric('sizes_hero_mobile_width', { mode: 'number' }),
sizes_hero_mobile_height: numeric('sizes_hero_mobile_height', { mode: 'number' }),
sizes_hero_mobile_mimeType: varchar('sizes_hero_mobile_mime_type'),
sizes_hero_mobile_filesize: numeric('sizes_hero_mobile_filesize', { mode: 'number' }),
sizes_hero_mobile_filename: varchar('sizes_hero_mobile_filename'),
},
(columns) => [
index('media_updated_at_idx').on(columns.updatedAt),
index('media_created_at_idx').on(columns.createdAt),
uniqueIndex('media_filename_idx').on(columns.filename),
index('media_sizes_thumbnail_sizes_thumbnail_filename_idx').on(
columns.sizes_thumbnail_filename,
),
index('media_sizes_card_sizes_card_filename_idx').on(columns.sizes_card_filename),
index('media_sizes_hero_sizes_hero_filename_idx').on(columns.sizes_hero_filename),
index('media_sizes_hero_mobile_sizes_hero_mobile_filename_idx').on(
columns.sizes_hero_mobile_filename,
),
],
);
export const posts = pgTable(
'posts',
{
id: serial('id').primaryKey(),
title: varchar('title'),
slug: varchar('slug'),
excerpt: varchar('excerpt'),
date: timestamp('date', { mode: 'string', withTimezone: true, precision: 3 }),
featuredImage: integer('featured_image_id').references(() => media.id, {
onDelete: 'set null',
}),
locale: enum_posts_locale('locale').default('en'),
category: varchar('category'),
content: jsonb('content'),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
_status: enum_posts_status('_status').default('draft'),
},
(columns) => [
uniqueIndex('posts_slug_idx').on(columns.slug),
index('posts_featured_image_idx').on(columns.featuredImage),
index('posts_updated_at_idx').on(columns.updatedAt),
index('posts_created_at_idx').on(columns.createdAt),
index('posts__status_idx').on(columns._status),
],
);
export const _posts_v = pgTable(
'_posts_v',
{
id: serial('id').primaryKey(),
parent: integer('parent_id').references(() => posts.id, {
onDelete: 'set null',
}),
version_title: varchar('version_title'),
version_slug: varchar('version_slug'),
version_excerpt: varchar('version_excerpt'),
version_date: timestamp('version_date', { mode: 'string', withTimezone: true, precision: 3 }),
version_featuredImage: integer('version_featured_image_id').references(() => media.id, {
onDelete: 'set null',
}),
version_locale: enum__posts_v_version_locale('version_locale').default('en'),
version_category: varchar('version_category'),
version_content: jsonb('version_content'),
version_updatedAt: timestamp('version_updated_at', {
mode: 'string',
withTimezone: true,
precision: 3,
}),
version_createdAt: timestamp('version_created_at', {
mode: 'string',
withTimezone: true,
precision: 3,
}),
version__status: enum__posts_v_version_status('version__status').default('draft'),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
latest: boolean('latest'),
},
(columns) => [
index('_posts_v_parent_idx').on(columns.parent),
index('_posts_v_version_version_slug_idx').on(columns.version_slug),
index('_posts_v_version_version_featured_image_idx').on(columns.version_featuredImage),
index('_posts_v_version_version_updated_at_idx').on(columns.version_updatedAt),
index('_posts_v_version_version_created_at_idx').on(columns.version_createdAt),
index('_posts_v_version_version__status_idx').on(columns.version__status),
index('_posts_v_created_at_idx').on(columns.createdAt),
index('_posts_v_updated_at_idx').on(columns.updatedAt),
index('_posts_v_latest_idx').on(columns.latest),
],
);
export const form_submissions = pgTable(
'form_submissions',
{
id: serial('id').primaryKey(),
name: varchar('name').notNull(),
email: varchar('email').notNull(),
type: enum_form_submissions_type('type').notNull(),
productName: varchar('product_name'),
message: varchar('message').notNull(),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
},
(columns) => [
index('form_submissions_updated_at_idx').on(columns.updatedAt),
index('form_submissions_created_at_idx').on(columns.createdAt),
],
);
export const products_categories = pgTable(
'products_categories',
{
_order: integer('_order').notNull(),
_parentID: integer('_parent_id').notNull(),
id: varchar('id').primaryKey(),
category: varchar('category'),
},
(columns) => [
index('products_categories_order_idx').on(columns._order),
index('products_categories_parent_id_idx').on(columns._parentID),
foreignKey({
columns: [columns['_parentID']],
foreignColumns: [products.id],
name: 'products_categories_parent_id_fk',
}).onDelete('cascade'),
],
);
export const products = pgTable(
'products',
{
id: serial('id').primaryKey(),
title: varchar('title'),
sku: varchar('sku'),
slug: varchar('slug'),
description: varchar('description'),
locale: enum_products_locale('locale').default('de'),
application: jsonb('application'),
content: jsonb('content'),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
_status: enum_products_status('_status').default('draft'),
},
(columns) => [
uniqueIndex('products_sku_idx').on(columns.sku),
index('products_updated_at_idx').on(columns.updatedAt),
index('products_created_at_idx').on(columns.createdAt),
index('products__status_idx').on(columns._status),
],
);
export const products_rels = pgTable(
'products_rels',
{
id: serial('id').primaryKey(),
order: integer('order'),
parent: integer('parent_id').notNull(),
path: varchar('path').notNull(),
mediaID: integer('media_id'),
},
(columns) => [
index('products_rels_order_idx').on(columns.order),
index('products_rels_parent_idx').on(columns.parent),
index('products_rels_path_idx').on(columns.path),
index('products_rels_media_id_idx').on(columns.mediaID),
foreignKey({
columns: [columns['parent']],
foreignColumns: [products.id],
name: 'products_rels_parent_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['mediaID']],
foreignColumns: [media.id],
name: 'products_rels_media_fk',
}).onDelete('cascade'),
],
);
export const _products_v_version_categories = pgTable(
'_products_v_version_categories',
{
_order: integer('_order').notNull(),
_parentID: integer('_parent_id').notNull(),
id: serial('id').primaryKey(),
category: varchar('category'),
_uuid: varchar('_uuid'),
},
(columns) => [
index('_products_v_version_categories_order_idx').on(columns._order),
index('_products_v_version_categories_parent_id_idx').on(columns._parentID),
foreignKey({
columns: [columns['_parentID']],
foreignColumns: [_products_v.id],
name: '_products_v_version_categories_parent_id_fk',
}).onDelete('cascade'),
],
);
export const _products_v = pgTable(
'_products_v',
{
id: serial('id').primaryKey(),
parent: integer('parent_id').references(() => products.id, {
onDelete: 'set null',
}),
version_title: varchar('version_title'),
version_sku: varchar('version_sku'),
version_slug: varchar('version_slug'),
version_description: varchar('version_description'),
version_locale: enum__products_v_version_locale('version_locale').default('de'),
version_application: jsonb('version_application'),
version_content: jsonb('version_content'),
version_updatedAt: timestamp('version_updated_at', {
mode: 'string',
withTimezone: true,
precision: 3,
}),
version_createdAt: timestamp('version_created_at', {
mode: 'string',
withTimezone: true,
precision: 3,
}),
version__status: enum__products_v_version_status('version__status').default('draft'),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
latest: boolean('latest'),
},
(columns) => [
index('_products_v_parent_idx').on(columns.parent),
index('_products_v_version_version_sku_idx').on(columns.version_sku),
index('_products_v_version_version_updated_at_idx').on(columns.version_updatedAt),
index('_products_v_version_version_created_at_idx').on(columns.version_createdAt),
index('_products_v_version_version__status_idx').on(columns.version__status),
index('_products_v_created_at_idx').on(columns.createdAt),
index('_products_v_updated_at_idx').on(columns.updatedAt),
index('_products_v_latest_idx').on(columns.latest),
],
);
export const _products_v_rels = pgTable(
'_products_v_rels',
{
id: serial('id').primaryKey(),
order: integer('order'),
parent: integer('parent_id').notNull(),
path: varchar('path').notNull(),
mediaID: integer('media_id'),
},
(columns) => [
index('_products_v_rels_order_idx').on(columns.order),
index('_products_v_rels_parent_idx').on(columns.parent),
index('_products_v_rels_path_idx').on(columns.path),
index('_products_v_rels_media_id_idx').on(columns.mediaID),
foreignKey({
columns: [columns['parent']],
foreignColumns: [_products_v.id],
name: '_products_v_rels_parent_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['mediaID']],
foreignColumns: [media.id],
name: '_products_v_rels_media_fk',
}).onDelete('cascade'),
],
);
export const payload_kv = pgTable(
'payload_kv',
{
id: serial('id').primaryKey(),
key: varchar('key').notNull(),
data: jsonb('data').notNull(),
},
(columns) => [uniqueIndex('payload_kv_key_idx').on(columns.key)],
);
export const payload_locked_documents = pgTable(
'payload_locked_documents',
{
id: serial('id').primaryKey(),
globalSlug: varchar('global_slug'),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
},
(columns) => [
index('payload_locked_documents_global_slug_idx').on(columns.globalSlug),
index('payload_locked_documents_updated_at_idx').on(columns.updatedAt),
index('payload_locked_documents_created_at_idx').on(columns.createdAt),
],
);
export const payload_locked_documents_rels = pgTable(
'payload_locked_documents_rels',
{
id: serial('id').primaryKey(),
order: integer('order'),
parent: integer('parent_id').notNull(),
path: varchar('path').notNull(),
usersID: integer('users_id'),
mediaID: integer('media_id'),
postsID: integer('posts_id'),
'form-submissionsID': integer('form_submissions_id'),
productsID: integer('products_id'),
},
(columns) => [
index('payload_locked_documents_rels_order_idx').on(columns.order),
index('payload_locked_documents_rels_parent_idx').on(columns.parent),
index('payload_locked_documents_rels_path_idx').on(columns.path),
index('payload_locked_documents_rels_users_id_idx').on(columns.usersID),
index('payload_locked_documents_rels_media_id_idx').on(columns.mediaID),
index('payload_locked_documents_rels_posts_id_idx').on(columns.postsID),
index('payload_locked_documents_rels_form_submissions_id_idx').on(
columns['form-submissionsID'],
),
index('payload_locked_documents_rels_products_id_idx').on(columns.productsID),
foreignKey({
columns: [columns['parent']],
foreignColumns: [payload_locked_documents.id],
name: 'payload_locked_documents_rels_parent_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['usersID']],
foreignColumns: [users.id],
name: 'payload_locked_documents_rels_users_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['mediaID']],
foreignColumns: [media.id],
name: 'payload_locked_documents_rels_media_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['postsID']],
foreignColumns: [posts.id],
name: 'payload_locked_documents_rels_posts_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['form-submissionsID']],
foreignColumns: [form_submissions.id],
name: 'payload_locked_documents_rels_form_submissions_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['productsID']],
foreignColumns: [products.id],
name: 'payload_locked_documents_rels_products_fk',
}).onDelete('cascade'),
],
);
export const payload_preferences = pgTable(
'payload_preferences',
{
id: serial('id').primaryKey(),
key: varchar('key'),
value: jsonb('value'),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
},
(columns) => [
index('payload_preferences_key_idx').on(columns.key),
index('payload_preferences_updated_at_idx').on(columns.updatedAt),
index('payload_preferences_created_at_idx').on(columns.createdAt),
],
);
export const payload_preferences_rels = pgTable(
'payload_preferences_rels',
{
id: serial('id').primaryKey(),
order: integer('order'),
parent: integer('parent_id').notNull(),
path: varchar('path').notNull(),
usersID: integer('users_id'),
},
(columns) => [
index('payload_preferences_rels_order_idx').on(columns.order),
index('payload_preferences_rels_parent_idx').on(columns.parent),
index('payload_preferences_rels_path_idx').on(columns.path),
index('payload_preferences_rels_users_id_idx').on(columns.usersID),
foreignKey({
columns: [columns['parent']],
foreignColumns: [payload_preferences.id],
name: 'payload_preferences_rels_parent_fk',
}).onDelete('cascade'),
foreignKey({
columns: [columns['usersID']],
foreignColumns: [users.id],
name: 'payload_preferences_rels_users_fk',
}).onDelete('cascade'),
],
);
export const payload_migrations = pgTable(
'payload_migrations',
{
id: serial('id').primaryKey(),
name: varchar('name'),
batch: numeric('batch', { mode: 'number' }),
updatedAt: timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
createdAt: timestamp('created_at', { mode: 'string', withTimezone: true, precision: 3 })
.defaultNow()
.notNull(),
},
(columns) => [
index('payload_migrations_updated_at_idx').on(columns.updatedAt),
index('payload_migrations_created_at_idx').on(columns.createdAt),
],
);
export const relations_users_sessions = relations(users_sessions, ({ one }) => ({
_parentID: one(users, {
fields: [users_sessions._parentID],
references: [users.id],
relationName: 'sessions',
}),
}));
export const relations_users = relations(users, ({ many }) => ({
sessions: many(users_sessions, {
relationName: 'sessions',
}),
}));
export const relations_media = relations(media, () => ({}));
export const relations_posts = relations(posts, ({ one }) => ({
featuredImage: one(media, {
fields: [posts.featuredImage],
references: [media.id],
relationName: 'featuredImage',
}),
}));
export const relations__posts_v = relations(_posts_v, ({ one }) => ({
parent: one(posts, {
fields: [_posts_v.parent],
references: [posts.id],
relationName: 'parent',
}),
version_featuredImage: one(media, {
fields: [_posts_v.version_featuredImage],
references: [media.id],
relationName: 'version_featuredImage',
}),
}));
export const relations_form_submissions = relations(form_submissions, () => ({}));
export const relations_products_categories = relations(products_categories, ({ one }) => ({
_parentID: one(products, {
fields: [products_categories._parentID],
references: [products.id],
relationName: 'categories',
}),
}));
export const relations_products_rels = relations(products_rels, ({ one }) => ({
parent: one(products, {
fields: [products_rels.parent],
references: [products.id],
relationName: '_rels',
}),
mediaID: one(media, {
fields: [products_rels.mediaID],
references: [media.id],
relationName: 'media',
}),
}));
export const relations_products = relations(products, ({ many }) => ({
categories: many(products_categories, {
relationName: 'categories',
}),
_rels: many(products_rels, {
relationName: '_rels',
}),
}));
export const relations__products_v_version_categories = relations(
_products_v_version_categories,
({ one }) => ({
_parentID: one(_products_v, {
fields: [_products_v_version_categories._parentID],
references: [_products_v.id],
relationName: 'version_categories',
}),
}),
);
export const relations__products_v_rels = relations(_products_v_rels, ({ one }) => ({
parent: one(_products_v, {
fields: [_products_v_rels.parent],
references: [_products_v.id],
relationName: '_rels',
}),
mediaID: one(media, {
fields: [_products_v_rels.mediaID],
references: [media.id],
relationName: 'media',
}),
}));
export const relations__products_v = relations(_products_v, ({ one, many }) => ({
parent: one(products, {
fields: [_products_v.parent],
references: [products.id],
relationName: 'parent',
}),
version_categories: many(_products_v_version_categories, {
relationName: 'version_categories',
}),
_rels: many(_products_v_rels, {
relationName: '_rels',
}),
}));
export const relations_payload_kv = relations(payload_kv, () => ({}));
export const relations_payload_locked_documents_rels = relations(
payload_locked_documents_rels,
({ one }) => ({
parent: one(payload_locked_documents, {
fields: [payload_locked_documents_rels.parent],
references: [payload_locked_documents.id],
relationName: '_rels',
}),
usersID: one(users, {
fields: [payload_locked_documents_rels.usersID],
references: [users.id],
relationName: 'users',
}),
mediaID: one(media, {
fields: [payload_locked_documents_rels.mediaID],
references: [media.id],
relationName: 'media',
}),
postsID: one(posts, {
fields: [payload_locked_documents_rels.postsID],
references: [posts.id],
relationName: 'posts',
}),
'form-submissionsID': one(form_submissions, {
fields: [payload_locked_documents_rels['form-submissionsID']],
references: [form_submissions.id],
relationName: 'form-submissions',
}),
productsID: one(products, {
fields: [payload_locked_documents_rels.productsID],
references: [products.id],
relationName: 'products',
}),
}),
);
export const relations_payload_locked_documents = relations(
payload_locked_documents,
({ many }) => ({
_rels: many(payload_locked_documents_rels, {
relationName: '_rels',
}),
}),
);
export const relations_payload_preferences_rels = relations(
payload_preferences_rels,
({ one }) => ({
parent: one(payload_preferences, {
fields: [payload_preferences_rels.parent],
references: [payload_preferences.id],
relationName: '_rels',
}),
usersID: one(users, {
fields: [payload_preferences_rels.usersID],
references: [users.id],
relationName: 'users',
}),
}),
);
export const relations_payload_preferences = relations(payload_preferences, ({ many }) => ({
_rels: many(payload_preferences_rels, {
relationName: '_rels',
}),
}));
export const relations_payload_migrations = relations(payload_migrations, () => ({}));
type DatabaseSchema = {
enum_posts_locale: typeof enum_posts_locale;
enum_posts_status: typeof enum_posts_status;
enum__posts_v_version_locale: typeof enum__posts_v_version_locale;
enum__posts_v_version_status: typeof enum__posts_v_version_status;
enum_form_submissions_type: typeof enum_form_submissions_type;
enum_products_locale: typeof enum_products_locale;
enum_products_status: typeof enum_products_status;
enum__products_v_version_locale: typeof enum__products_v_version_locale;
enum__products_v_version_status: typeof enum__products_v_version_status;
users_sessions: typeof users_sessions;
users: typeof users;
media: typeof media;
posts: typeof posts;
_posts_v: typeof _posts_v;
form_submissions: typeof form_submissions;
products_categories: typeof products_categories;
products: typeof products;
products_rels: typeof products_rels;
_products_v_version_categories: typeof _products_v_version_categories;
_products_v: typeof _products_v;
_products_v_rels: typeof _products_v_rels;
payload_kv: typeof payload_kv;
payload_locked_documents: typeof payload_locked_documents;
payload_locked_documents_rels: typeof payload_locked_documents_rels;
payload_preferences: typeof payload_preferences;
payload_preferences_rels: typeof payload_preferences_rels;
payload_migrations: typeof payload_migrations;
relations_users_sessions: typeof relations_users_sessions;
relations_users: typeof relations_users;
relations_media: typeof relations_media;
relations_posts: typeof relations_posts;
relations__posts_v: typeof relations__posts_v;
relations_form_submissions: typeof relations_form_submissions;
relations_products_categories: typeof relations_products_categories;
relations_products_rels: typeof relations_products_rels;
relations_products: typeof relations_products;
relations__products_v_version_categories: typeof relations__products_v_version_categories;
relations__products_v_rels: typeof relations__products_v_rels;
relations__products_v: typeof relations__products_v;
relations_payload_kv: typeof relations_payload_kv;
relations_payload_locked_documents_rels: typeof relations_payload_locked_documents_rels;
relations_payload_locked_documents: typeof relations_payload_locked_documents;
relations_payload_preferences_rels: typeof relations_payload_preferences_rels;
relations_payload_preferences: typeof relations_payload_preferences;
relations_payload_migrations: typeof relations_payload_migrations;
};
declare module '@payloadcms/db-postgres' {
export interface GeneratedDatabaseSchema {
schema: DatabaseSchema;
}
}

View File

@@ -0,0 +1,25 @@
import { Block } from 'payload';
export const AnimatedImage: Block = {
slug: 'animatedImage',
fields: [
{
name: 'src',
type: 'text',
required: true,
},
{
name: 'alt',
type: 'text',
required: true,
},
{
name: 'width',
type: 'number',
},
{
name: 'height',
type: 'number',
},
],
};

View File

@@ -0,0 +1,20 @@
import { Block } from 'payload';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
export const Callout: Block = {
slug: 'callout',
fields: [
{
name: 'type',
type: 'select',
options: ['info', 'warning', 'important', 'tip', 'caution'],
defaultValue: 'info',
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({}),
required: true,
},
],
};

View File

@@ -0,0 +1,34 @@
import { Block } from 'payload';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
export const ChatBubble: Block = {
slug: 'chatBubble',
fields: [
{
name: 'author',
type: 'text',
defaultValue: 'KLZ Team',
},
{
name: 'avatar',
type: 'text',
},
{
name: 'role',
type: 'text',
defaultValue: 'Assistant',
},
{
name: 'align',
type: 'select',
options: ['left', 'right'],
defaultValue: 'left',
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({}),
required: true,
},
],
};

View File

@@ -0,0 +1,47 @@
import { Block } from 'payload';
export const ComparisonGrid: Block = {
slug: 'comparisonGrid',
fields: [
{
name: 'title',
label: 'Main Heading',
type: 'text',
required: true,
},
{
name: 'leftLabel',
type: 'text',
required: true,
},
{
name: 'rightLabel',
type: 'text',
required: true,
},
{
name: 'items',
type: 'array',
required: true,
minRows: 1,
fields: [
{
name: 'label',
label: 'Row Label',
type: 'text',
required: true,
},
{
name: 'leftValue',
type: 'text',
required: true,
},
{
name: 'rightValue',
type: 'text',
required: true,
},
],
},
],
};

View File

@@ -0,0 +1,20 @@
import { Block } from 'payload';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
export const HighlightBox: Block = {
slug: 'highlightBox',
fields: [
{
name: 'type',
type: 'select',
options: ['info', 'warning', 'success', 'error', 'neutral'],
defaultValue: 'neutral',
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({}),
required: true,
},
],
};

View File

@@ -0,0 +1,12 @@
import { Block } from 'payload';
export const PowerCTA: Block = {
slug: 'powerCTA',
fields: [
{
name: 'locale',
type: 'text',
required: true,
},
],
};

View File

@@ -0,0 +1,96 @@
import { Block } from 'payload';
export const ProductTabs: Block = {
slug: 'productTabs',
interfaceName: 'ProductTabsBlock',
fields: [
{
name: 'technicalItems',
type: 'array',
fields: [
{
name: 'label',
type: 'text',
required: true,
},
{
name: 'value',
type: 'text',
required: true,
},
{
name: 'unit',
type: 'text',
},
],
},
{
name: 'voltageTables',
type: 'array',
fields: [
{
name: 'voltageLabel',
type: 'text',
required: true,
},
{
name: 'metaItems',
type: 'array',
fields: [
{
name: 'label',
type: 'text',
required: true,
},
{
name: 'value',
type: 'text',
required: true,
},
{
name: 'unit',
type: 'text',
},
],
},
{
name: 'columns',
type: 'array',
fields: [
{
name: 'key',
type: 'text',
required: true,
},
{
name: 'label',
type: 'text',
required: true,
},
],
},
{
name: 'rows',
type: 'array',
fields: [
{
name: 'configuration',
type: 'text',
required: true,
},
{
name: 'cells',
type: 'array',
fields: [
{
name: 'value',
type: 'text',
},
],
},
],
},
],
},
],
};

View File

@@ -0,0 +1,23 @@
import { Block } from 'payload';
export const SplitHeading: Block = {
slug: 'splitHeading',
interfaceName: 'SplitHeadingBlock',
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'id',
type: 'text',
},
{
name: 'level',
type: 'select',
options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
defaultValue: 'h2',
},
],
};

View File

@@ -0,0 +1,25 @@
import { Block } from 'payload';
export const Stats: Block = {
slug: 'stats',
interfaceName: 'StatsBlock',
fields: [
{
name: 'stats',
type: 'array',
required: true,
fields: [
{
name: 'value',
type: 'text',
required: true,
},
{
name: 'label',
type: 'text',
required: true,
},
],
},
],
};

View File

@@ -0,0 +1,31 @@
import { Block } from 'payload';
export const StickyNarrative: Block = {
slug: 'stickyNarrative',
fields: [
{
name: 'title',
label: 'Main Heading',
type: 'text',
required: true,
},
{
name: 'items',
type: 'array',
required: true,
minRows: 1,
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'content',
type: 'textarea',
required: true,
},
],
},
],
};

View File

@@ -0,0 +1,30 @@
import { Block } from 'payload';
export const TechnicalGrid: Block = {
slug: 'technicalGrid',
fields: [
{
name: 'title',
label: 'Main Heading',
type: 'text',
},
{
name: 'items',
type: 'array',
required: true,
minRows: 1,
fields: [
{
name: 'label',
type: 'text',
required: true,
},
{
name: 'value',
type: 'text',
required: true,
},
],
},
],
};

View File

@@ -0,0 +1,30 @@
import { Block } from 'payload';
export const VisualLinkPreview: Block = {
slug: 'visualLinkPreview',
fields: [
{
name: 'url',
type: 'text',
required: true,
},
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'summary',
type: 'textarea',
required: true,
},
{
name: 'image',
type: 'text',
admin: {
description: 'Legacy HTTP string from the old hardcoded images.',
},
required: false,
},
],
};

View File

@@ -0,0 +1,27 @@
import { AnimatedImage } from './AnimatedImage';
import { Callout } from './Callout';
import { ChatBubble } from './ChatBubble';
import { ComparisonGrid } from './ComparisonGrid';
import { HighlightBox } from './HighlightBox';
import { PowerCTA } from './PowerCTA';
import { ProductTabs } from './ProductTabs';
import { SplitHeading } from './SplitHeading';
import { Stats } from './Stats';
import { StickyNarrative } from './StickyNarrative';
import { TechnicalGrid } from './TechnicalGrid';
import { VisualLinkPreview } from './VisualLinkPreview';
export const payloadBlocks = [
AnimatedImage,
Callout,
ChatBubble,
ComparisonGrid,
HighlightBox,
PowerCTA,
ProductTabs,
SplitHeading,
Stats,
StickyNarrative,
TechnicalGrid,
VisualLinkPreview,
];

View File

@@ -0,0 +1,67 @@
import type { CollectionConfig } from 'payload';
export const FormSubmissions: CollectionConfig = {
slug: 'form-submissions',
admin: {
useAsTitle: 'name',
defaultColumns: ['name', 'email', 'type', 'createdAt'],
description: 'Captured leads from Contact and Product Quote forms.',
},
access: {
// Only Admins can view and delete leads via dashboard.
read: ({ req: { user } }) => Boolean(user) || process.env.NODE_ENV === 'development',
update: ({ req: { user } }) => Boolean(user) || process.env.NODE_ENV === 'development',
delete: ({ req: { user } }) => Boolean(user) || process.env.NODE_ENV === 'development',
// Next.js server actions handle secure inserts natively. No public client create access.
create: () => false,
},
fields: [
{
name: 'name',
type: 'text',
required: true,
admin: {
readOnly: true,
},
},
{
name: 'email',
type: 'email',
required: true,
admin: {
readOnly: true,
},
},
{
name: 'type',
type: 'select',
options: [
{ label: 'General Contact', value: 'contact' },
{ label: 'Product Quote', value: 'product_quote' },
],
required: true,
admin: {
position: 'sidebar',
readOnly: true,
},
},
{
name: 'productName',
type: 'text',
admin: {
position: 'sidebar',
readOnly: true,
condition: (data) => data.type === 'product_quote',
description: 'The specific KLZ product the user requested a quote for.',
},
},
{
name: 'message',
type: 'textarea',
required: true,
admin: {
readOnly: true,
},
},
],
};

View File

@@ -0,0 +1,48 @@
import type { CollectionConfig } from 'payload';
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
admin: {
useAsTitle: 'alt',
defaultColumns: ['filename', 'alt', 'updatedAt'],
},
upload: {
staticDir: 'public/media',
adminThumbnail: 'thumbnail',
imageSizes: [
{
name: 'thumbnail',
width: 600,
// height: undefined allows wide 5:1 aspect ratios to be preserved without cropping
height: undefined,
position: 'centre',
},
{
name: 'card',
width: 768,
height: undefined,
position: 'centre',
},
{
name: 'tablet',
width: 1024,
height: undefined,
position: 'centre',
},
],
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
{
name: 'caption',
type: 'text',
},
],
};

View File

@@ -0,0 +1,61 @@
import { CollectionConfig } from 'payload';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'slug', 'locale', 'updatedAt'],
},
access: {
read: () => true,
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'slug',
type: 'text',
required: true,
admin: {
position: 'sidebar',
},
},
{
name: 'locale',
type: 'select',
options: [
{ label: 'English', value: 'en' },
{ label: 'German', value: 'de' },
],
required: true,
admin: {
position: 'sidebar',
},
},
{
name: 'excerpt',
type: 'textarea',
admin: {
position: 'sidebar',
},
},
{
name: 'featuredImage',
type: 'upload',
relationTo: 'media',
admin: {
position: 'sidebar',
},
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({}),
required: true,
},
],
};

View File

@@ -0,0 +1,154 @@
import type { CollectionConfig } from 'payload';
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical';
import { StickyNarrative } from '../blocks/StickyNarrative';
import { ComparisonGrid } from '../blocks/ComparisonGrid';
import { VisualLinkPreview } from '../blocks/VisualLinkPreview';
import { TechnicalGrid } from '../blocks/TechnicalGrid';
import { HighlightBox } from '../blocks/HighlightBox';
import { AnimatedImage } from '../blocks/AnimatedImage';
import { ChatBubble } from '../blocks/ChatBubble';
import { PowerCTA } from '../blocks/PowerCTA';
import { Callout } from '../blocks/Callout';
import { Stats } from '../blocks/Stats';
import { SplitHeading } from '../blocks/SplitHeading';
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title',
defaultColumns: ['featuredImage', 'title', 'date', 'updatedAt', '_status'],
},
versions: {
drafts: true, // Enables Draft/Published workflows
},
access: {
read: ({ req: { user } }) => {
// In local development, always show everything (including Drafts and scheduled future posts)
if (process.env.NODE_ENV === 'development') {
return true;
}
// If an Admin user is logged in, they can view everything
if (user) {
return true;
}
// For public unauthenticated visitors in PROD/STAGING contexts:
// Only serve Posts where Status = "published" AND the publish Date is in the past!
return {
and: [
{
_status: {
equals: 'published',
},
},
{
date: {
less_than_equal: new Date().toISOString(),
},
},
],
};
},
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
admin: {
position: 'sidebar',
},
hooks: {
beforeValidate: [
({ value, data }) => {
// Auto-generate slug from title if left blank
if (value || !data?.title) return value;
return data.title
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '');
},
],
},
},
{
name: 'excerpt',
type: 'text',
admin: {
description: 'A short summary for blog feed cards and SEO.',
},
},
{
name: 'date',
type: 'date',
required: true,
admin: {
position: 'sidebar',
description: 'Future dates will schedule the post to publish automatically.',
},
defaultValue: () => new Date().toISOString(),
},
{
name: 'featuredImage',
type: 'upload',
relationTo: 'media',
admin: {
position: 'sidebar',
description: 'The primary Hero image used for headers and OpenGraph previews.',
},
},
{
name: 'locale',
type: 'select',
required: true,
admin: {
position: 'sidebar',
},
options: [
{ label: 'English', value: 'en' },
{ label: 'German', value: 'de' },
],
defaultValue: 'en',
},
{
name: 'category',
type: 'text',
admin: {
position: 'sidebar',
description: 'Used for tag bucketing (e.g. "Kabel Technologie").',
},
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({
blocks: [
StickyNarrative,
ComparisonGrid,
VisualLinkPreview,
TechnicalGrid,
HighlightBox,
AnimatedImage,
ChatBubble,
PowerCTA,
Callout,
Stats,
SplitHeading,
],
}),
],
}),
},
],
};

View File

@@ -0,0 +1,144 @@
import type { CollectionConfig } from 'payload';
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical';
import { StickyNarrative } from '../blocks/StickyNarrative';
import { ComparisonGrid } from '../blocks/ComparisonGrid';
import { VisualLinkPreview } from '../blocks/VisualLinkPreview';
import { TechnicalGrid } from '../blocks/TechnicalGrid';
import { HighlightBox } from '../blocks/HighlightBox';
import { AnimatedImage } from '../blocks/AnimatedImage';
import { ChatBubble } from '../blocks/ChatBubble';
import { PowerCTA } from '../blocks/PowerCTA';
import { Callout } from '../blocks/Callout';
import { Stats } from '../blocks/Stats';
import { SplitHeading } from '../blocks/SplitHeading';
import { ProductTabs } from '../blocks/ProductTabs';
export const Products: CollectionConfig = {
slug: 'products',
admin: {
useAsTitle: 'title',
defaultColumns: ['featuredImage', 'title', 'sku', 'locale', 'updatedAt', '_status'],
},
versions: {
drafts: true,
},
access: {
read: ({ req: { user } }) => {
if (process.env.NODE_ENV === 'development') {
return true;
}
if (user) {
return true;
}
return {
_status: {
equals: 'published',
},
};
},
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'sku',
type: 'text',
required: true,
admin: {
position: 'sidebar',
},
},
{
name: 'slug',
type: 'text',
required: true,
admin: {
position: 'sidebar',
},
},
{
name: 'description',
type: 'textarea',
required: true,
},
{
name: 'locale',
type: 'select',
required: true,
admin: {
position: 'sidebar',
},
options: [
{ label: 'English', value: 'en' },
{ label: 'German', value: 'de' },
],
defaultValue: 'de',
},
{
name: 'categories',
type: 'array',
required: true,
fields: [
{
name: 'category',
type: 'text',
},
],
admin: {
position: 'sidebar',
},
},
{
name: 'featuredImage',
type: 'upload',
relationTo: 'media',
admin: {
position: 'sidebar',
description: 'The primary thumbnail used in list views.',
},
},
{
name: 'images',
type: 'upload',
relationTo: 'media',
hasMany: true,
admin: {
position: 'sidebar',
},
},
{
name: 'application',
type: 'richText',
editor: lexicalEditor({}),
},
{
name: 'content',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({
blocks: [
StickyNarrative,
ComparisonGrid,
VisualLinkPreview,
TechnicalGrid,
HighlightBox,
AnimatedImage,
ChatBubble,
PowerCTA,
Callout,
Stats,
SplitHeading,
ProductTabs,
],
}),
],
}),
},
],
};

View File

@@ -0,0 +1,12 @@
import type { CollectionConfig } from 'payload';
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
],
};

View File

@@ -0,0 +1,296 @@
/**
* Converts a Markdown+JSX string into a Lexical AST node array.
* Specifically adapted for klz-cables.com custom Component Blocks.
*/
function propValue(chunk: string, prop: string): string {
// Match prop="value" or prop='value' or prop={value}
// and also multiline props like prop={\n [\n {...}\n ]\n}
// For arrays or complex objects passed as props, basic regex might fail,
// but the MDX in klz-cables usually uses simpler props or children.
const match =
chunk.match(new RegExp(`${prop}=["']([^"']+)["']`)) ||
chunk.match(new RegExp(`${prop}=\\{([^}]+)\\}`));
return match ? match[1] : '';
}
function extractItemsProp(chunk: string, startTag: string): any[] {
// Match items={ [ ... ] } robustly without stopping at inner object braces
const itemsMatch = chunk.match(/items=\{\s*(\[[\s\S]*?\])\s*\}/);
if (itemsMatch) {
try {
const arrayString = itemsMatch[1].trim();
// Since klz-cables MDX passes pure JS object arrays like `items={[{title: 'A', content: 'B'}]}`,
// parsing it via Regex to JSON is extremely brittle due to unquoted keys and trailing commas.
// Using `new Function` safely evaluates the array AST directly in this Node script environment.
const fn = new Function(`return ${arrayString};`);
return fn();
} catch (_e: any) {
console.warn(`Could not parse items array for block ${startTag}:`, _e.message);
return [];
}
}
return [];
}
function blockNode(blockType: string, fields: Record<string, any>) {
return { type: 'block', format: '', version: 2, fields: { blockType, ...fields } };
}
function ensureChildren(parsedNodes: any[]): any[] {
// Lexical root nodes require at least one child node, or validation fails
if (parsedNodes.length === 0) {
return [
{
type: 'paragraph',
format: '',
indent: 0,
version: 1,
children: [{ mode: 'normal', type: 'text', text: ' ', version: 1 }],
},
];
}
return parsedNodes;
}
export function parseMarkdownToLexical(markdown: string): any[] {
const textNode = (text: string) => ({
type: 'paragraph',
format: '',
indent: 0,
version: 1,
children: [{ mode: 'normal', type: 'text', text, version: 1 }],
});
const nodes: any[] = [];
let content = markdown;
// Strip frontmatter
const fm = content.match(/^---\s*\n[\s\S]*?\n---/);
if (fm) content = content.replace(fm[0], '').trim();
// 1. EXTRACT MULTILINE WRAPPERS BEFORE CHUNKING
// This allows nested newlines inside components without breaking them.
const extractBlocks = [
{
tag: 'HighlightBox',
regex: /<HighlightBox([^>]*)>([\s\S]*?)<\/HighlightBox>/g,
build: (props: string, inner: string) =>
blockNode('highlightBox', {
title: propValue(`<Tag ${props}>`, 'title'),
color: propValue(`<Tag ${props}>`, 'color') || 'primary',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
direction: 'ltr',
children: ensureChildren(parseMarkdownToLexical(inner.trim())),
},
},
}),
},
{
tag: 'ChatBubble',
regex: /<ChatBubble([^>]*)>([\s\S]*?)<\/ChatBubble>/g,
build: (props: string, inner: string) =>
blockNode('chatBubble', {
author: propValue(`<Tag ${props}>`, 'author') || 'KLZ Team',
avatar: propValue(`<Tag ${props}>`, 'avatar'),
role: propValue(`<Tag ${props}>`, 'role') || 'Assistant',
align: propValue(`<Tag ${props}>`, 'align') || 'left',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
direction: 'ltr',
children: ensureChildren(parseMarkdownToLexical(inner.trim())),
},
},
}),
},
{
tag: 'Callout',
regex: /<Callout([^>]*)>([\s\S]*?)<\/Callout>/g,
build: (props: string, inner: string) =>
blockNode('callout', {
type: propValue(`<Tag ${props}>`, 'type') || 'info',
title: propValue(`<Tag ${props}>`, 'title'),
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
direction: 'ltr',
children: ensureChildren(parseMarkdownToLexical(inner.trim())),
},
},
}),
},
];
// Placeholder map to temporarily store extracted multi-line blocks
const placeholders = new Map<string, any>();
let placeholderIdx = 0;
for (const block of extractBlocks) {
content = content.replace(block.regex, (match, propsMatch, innerMatch) => {
const id = `__BLOCK_PLACEHOLDER_${placeholderIdx++}__`;
placeholders.set(id, block.build(propsMatch, innerMatch));
return `\n\n${id}\n\n`; // Pad with newlines so it becomes its own chunk
});
}
// 2. CHUNK THE REST (Paragraphs, Single-line Components)
const rawChunks = content.split(/\n\s*\n/);
for (let chunk of rawChunks) {
chunk = chunk.trim();
if (!chunk) continue;
// Has Placeholder?
if (chunk.startsWith('__BLOCK_PLACEHOLDER_')) {
nodes.push(placeholders.get(chunk));
continue;
}
// --- Custom Component: ProductTabs ---
if (chunk.includes('<ProductTabs')) {
const dataMatch = chunk.match(/data=\{({[\s\S]*?})\}\s*\/>/);
if (dataMatch) {
try {
const parsedData = JSON.parse(dataMatch[1]);
// Normalize String Arrays to Payload Object Arrays { value: "string" }
if (parsedData.voltageTables) {
parsedData.voltageTables.forEach((vt: any) => {
if (vt.rows) {
vt.rows.forEach((row: any) => {
if (row.cells && Array.isArray(row.cells)) {
row.cells = row.cells.map((cell: any) =>
typeof cell !== 'object' || cell === null ? { value: String(cell) } : cell,
);
}
});
}
});
}
nodes.push(
blockNode('productTabs', {
technicalItems: parsedData.technicalItems || [],
voltageTables: parsedData.voltageTables || [],
}),
);
} catch (e: any) {
console.warn(`Could not parse JSON payload for ProductTabs:`, e.message);
}
}
continue;
}
// --- Custom Component: StickyNarrative ---
if (chunk.includes('<StickyNarrative')) {
nodes.push(
blockNode('stickyNarrative', {
title: propValue(chunk, 'title'),
items: extractItemsProp(chunk, 'StickyNarrative'),
}),
);
continue;
}
// --- Custom Component: ComparisonGrid ---
if (chunk.includes('<ComparisonGrid')) {
nodes.push(
blockNode('comparisonGrid', {
title: propValue(chunk, 'title'),
leftLabel: propValue(chunk, 'leftLabel'),
rightLabel: propValue(chunk, 'rightLabel'),
items: extractItemsProp(chunk, 'ComparisonGrid'),
}),
);
continue;
}
// --- Custom Component: VisualLinkPreview ---
if (chunk.includes('<VisualLinkPreview')) {
nodes.push(
blockNode('visualLinkPreview', {
url: propValue(chunk, 'url'),
title: propValue(chunk, 'title'),
summary: propValue(chunk, 'summary'),
image: propValue(chunk, 'image'),
}),
);
continue;
}
// --- Custom Component: TechnicalGrid ---
if (chunk.includes('<TechnicalGrid')) {
nodes.push(
blockNode('technicalGrid', {
title: propValue(chunk, 'title'),
items: extractItemsProp(chunk, 'TechnicalGrid'),
}),
);
continue;
}
// --- Custom Component: AnimatedImage ---
if (chunk.includes('<AnimatedImage')) {
const widthMatch = chunk.match(/width=\{?(\d+)\}?/);
const heightMatch = chunk.match(/height=\{?(\d+)\}?/);
nodes.push(
blockNode('animatedImage', {
src: propValue(chunk, 'src'),
alt: propValue(chunk, 'alt'),
width: widthMatch ? parseInt(widthMatch[1], 10) : undefined,
height: heightMatch ? parseInt(heightMatch[1], 10) : undefined,
}),
);
continue;
}
// --- Custom Component: PowerCTA ---
if (chunk.includes('<PowerCTA')) {
nodes.push(
blockNode('powerCTA', {
locale: propValue(chunk, 'locale') || 'de',
}),
);
continue;
}
// --- Standard Markdown: Headings ---
const headingMatch = chunk.match(/^(#{1,6})\s+(.*)/);
if (headingMatch) {
nodes.push({
type: 'heading',
tag: `h${headingMatch[1].length}`,
format: '',
indent: 0,
version: 1,
direction: 'ltr',
children: [{ mode: 'normal', type: 'text', text: headingMatch[2], version: 1 }],
});
continue;
}
// --- Standard Markdown: Images ---
const imageMatch = chunk.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
if (imageMatch) {
nodes.push(textNode(chunk));
continue;
}
// Default: plain text paragraph
nodes.push(textNode(chunk));
}
return nodes;
}