feat: payload cms
This commit is contained in:
48
src/payload/blocks/CategoryGrid.ts
Normal file
48
src/payload/blocks/CategoryGrid.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const CategoryGrid: Block = {
|
||||
slug: 'categoryGrid',
|
||||
interfaceName: 'CategoryGridBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'categories',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'href',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'ctaLabel',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
54
src/payload/blocks/CompanyHeritage.ts
Normal file
54
src/payload/blocks/CompanyHeritage.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const TeamLegacySection: Block = {
|
||||
slug: 'teamLegacySection',
|
||||
interfaceName: 'TeamLegacySectionBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'paragraph1',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'paragraph2',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'expertiseTitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'expertiseDesc',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'networkTitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'networkDesc',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'backgroundImage',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
26
src/payload/blocks/ContactSection.ts
Normal file
26
src/payload/blocks/ContactSection.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const ContactSection: Block = {
|
||||
slug: 'contactSection',
|
||||
interfaceName: 'ContactSectionBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'showForm',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Contact Form',
|
||||
},
|
||||
{
|
||||
name: 'showMap',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Map',
|
||||
},
|
||||
{
|
||||
name: 'showHours',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Opening Hours',
|
||||
},
|
||||
],
|
||||
};
|
||||
48
src/payload/blocks/HeroSection.ts
Normal file
48
src/payload/blocks/HeroSection.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const HeroSection: Block = {
|
||||
slug: 'heroSection',
|
||||
interfaceName: 'HeroSectionBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'badge',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'backgroundImage',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'ctaLabel',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'ctaHref',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'alignment',
|
||||
type: 'select',
|
||||
defaultValue: 'left',
|
||||
options: [
|
||||
{ label: 'Left', value: 'left' },
|
||||
{ label: 'Center', value: 'center' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
141
src/payload/blocks/HomeBlocks.ts
Normal file
141
src/payload/blocks/HomeBlocks.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const HomeHero: Block = {
|
||||
slug: 'homeHero',
|
||||
interfaceName: 'HomeHeroBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'secondaryCtaLabel', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeProductCategories: Block = {
|
||||
slug: 'homeProductCategories',
|
||||
interfaceName: 'HomeProductCategoriesBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeWhatWeDo: Block = {
|
||||
slug: 'homeWhatWeDo',
|
||||
interfaceName: 'HomeWhatWeDoBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'expertiseLabel', type: 'text', localized: true },
|
||||
{ name: 'quote', type: 'textarea', localized: true },
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeRecentPosts: Block = {
|
||||
slug: 'homeRecentPosts',
|
||||
interfaceName: 'HomeRecentPostsBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeExperience: Block = {
|
||||
slug: 'homeExperience',
|
||||
interfaceName: 'HomeExperienceBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'paragraph1', type: 'textarea', localized: true },
|
||||
{ name: 'paragraph2', type: 'textarea', localized: true },
|
||||
{ name: 'badge1', type: 'text', localized: true },
|
||||
{ name: 'badge1Text', type: 'text', localized: true },
|
||||
{ name: 'badge2', type: 'text', localized: true },
|
||||
{ name: 'badge2Text', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeWhyChooseUs: Block = {
|
||||
slug: 'homeWhyChooseUs',
|
||||
interfaceName: 'HomeWhyChooseUsBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'tagline', type: 'text', localized: true },
|
||||
{
|
||||
name: 'features',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [{ name: 'feature', type: 'text' }],
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeMeetTheTeam: Block = {
|
||||
slug: 'homeMeetTheTeam',
|
||||
interfaceName: 'HomeMeetTheTeamBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'description', type: 'textarea', localized: true },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'networkLabel', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeGallery: Block = {
|
||||
slug: 'homeGallery',
|
||||
interfaceName: 'HomeGalleryBlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeVideo: Block = {
|
||||
slug: 'homeVideo',
|
||||
interfaceName: 'HomeVideoBlock',
|
||||
fields: [{ name: 'title', type: 'text', localized: true }],
|
||||
};
|
||||
|
||||
export const HomeCTA: Block = {
|
||||
slug: 'homeCTA',
|
||||
interfaceName: 'HomeCTABlock',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'description', type: 'textarea', localized: true },
|
||||
{ name: 'buttonLabel', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const homeBlocksArray = [
|
||||
HomeHero,
|
||||
HomeProductCategories,
|
||||
HomeWhatWeDo,
|
||||
HomeRecentPosts,
|
||||
HomeExperience,
|
||||
HomeWhyChooseUs,
|
||||
HomeMeetTheTeam,
|
||||
HomeGallery,
|
||||
HomeVideo,
|
||||
HomeCTA,
|
||||
];
|
||||
27
src/payload/blocks/ImageGallery.ts
Normal file
27
src/payload/blocks/ImageGallery.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const ImageGallery: Block = {
|
||||
slug: 'imageGallery',
|
||||
interfaceName: 'ImageGalleryBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'images',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'alt',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
41
src/payload/blocks/ManifestoGrid.ts
Normal file
41
src/payload/blocks/ManifestoGrid.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const ManifestoGrid: Block = {
|
||||
slug: 'manifestoGrid',
|
||||
interfaceName: 'ManifestoGridBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'tagline',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
28
src/payload/blocks/SupportCTA.ts
Normal file
28
src/payload/blocks/SupportCTA.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const SupportCTA: Block = {
|
||||
slug: 'supportCTA',
|
||||
interfaceName: 'SupportCTABlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'buttonLabel',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'buttonHref',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
62
src/payload/blocks/TeamProfile.ts
Normal file
62
src/payload/blocks/TeamProfile.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const TeamProfile: Block = {
|
||||
slug: 'teamProfile',
|
||||
interfaceName: 'TeamProfileBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'quote',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'linkedinUrl',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'linkedinLabel',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'layout',
|
||||
type: 'select',
|
||||
defaultValue: 'imageRight',
|
||||
options: [
|
||||
{ label: 'Image Right', value: 'imageRight' },
|
||||
{ label: 'Image Left', value: 'imageLeft' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'colorScheme',
|
||||
type: 'select',
|
||||
defaultValue: 'dark',
|
||||
options: [
|
||||
{ label: 'Dark', value: 'dark' },
|
||||
{ label: 'Light', value: 'light' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,27 +1,41 @@
|
||||
import { AnimatedImage } from './AnimatedImage';
|
||||
import { Callout } from './Callout';
|
||||
import { CategoryGrid } from './CategoryGrid';
|
||||
import { ChatBubble } from './ChatBubble';
|
||||
import { ComparisonGrid } from './ComparisonGrid';
|
||||
import { ContactSection } from './ContactSection';
|
||||
import { HeroSection } from './HeroSection';
|
||||
import { HighlightBox } from './HighlightBox';
|
||||
import { ImageGallery } from './ImageGallery';
|
||||
import { ManifestoGrid } from './ManifestoGrid';
|
||||
import { PowerCTA } from './PowerCTA';
|
||||
import { ProductTabs } from './ProductTabs';
|
||||
import { SplitHeading } from './SplitHeading';
|
||||
import { Stats } from './Stats';
|
||||
import { StickyNarrative } from './StickyNarrative';
|
||||
import { TeamProfile } from './TeamProfile';
|
||||
import { TechnicalGrid } from './TechnicalGrid';
|
||||
import { VisualLinkPreview } from './VisualLinkPreview';
|
||||
import { homeBlocksArray } from './HomeBlocks';
|
||||
|
||||
export const payloadBlocks = [
|
||||
...homeBlocksArray,
|
||||
AnimatedImage,
|
||||
Callout,
|
||||
CategoryGrid,
|
||||
ChatBubble,
|
||||
ComparisonGrid,
|
||||
ContactSection,
|
||||
HeroSection,
|
||||
HighlightBox,
|
||||
ImageGallery,
|
||||
ManifestoGrid,
|
||||
PowerCTA,
|
||||
ProductTabs,
|
||||
SplitHeading,
|
||||
Stats,
|
||||
StickyNarrative,
|
||||
TeamProfile,
|
||||
TechnicalGrid,
|
||||
VisualLinkPreview,
|
||||
];
|
||||
|
||||
@@ -1,44 +1,65 @@
|
||||
import { CollectionConfig } from 'payload';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical';
|
||||
import { payloadBlocks } from '../blocks/allBlocks';
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: ['title', 'slug', 'locale', 'updatedAt'],
|
||||
defaultColumns: ['title', 'slug', 'layout', '_status', 'updatedAt'],
|
||||
},
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
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,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'The URL slug for this locale (e.g. "impressum" for DE, "imprint" for EN).',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'locale',
|
||||
name: 'layout',
|
||||
type: 'select',
|
||||
defaultValue: 'default',
|
||||
options: [
|
||||
{ label: 'English', value: 'en' },
|
||||
{ label: 'German', value: 'de' },
|
||||
{ label: 'Default (Article)', value: 'default' },
|
||||
{ label: 'Full Bleed (Blocks Only)', value: 'fullBleed' },
|
||||
],
|
||||
required: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'Full Bleed pages render blocks edge-to-edge without a generic hero wrapper.',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'excerpt',
|
||||
type: 'textarea',
|
||||
localized: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
@@ -54,7 +75,15 @@ export const Pages: CollectionConfig = {
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({}),
|
||||
localized: true,
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({
|
||||
blocks: payloadBlocks,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -19,22 +19,16 @@ export const Posts: CollectionConfig = {
|
||||
defaultColumns: ['featuredImage', 'title', 'date', 'updatedAt', '_status'],
|
||||
},
|
||||
versions: {
|
||||
drafts: true, // Enables Draft/Published workflows
|
||||
drafts: true,
|
||||
},
|
||||
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: [
|
||||
{
|
||||
@@ -56,19 +50,20 @@ export const Posts: CollectionConfig = {
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'Unique slug per locale (e.g. same slug can exist in DE and EN).',
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ value, data }) => {
|
||||
// Auto-generate slug from title if left blank
|
||||
if (value || !data?.title) return value;
|
||||
return data.title
|
||||
.toLowerCase()
|
||||
@@ -81,6 +76,7 @@ export const Posts: CollectionConfig = {
|
||||
{
|
||||
name: 'excerpt',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'A short summary for blog feed cards and SEO.',
|
||||
},
|
||||
@@ -104,22 +100,10 @@ export const Posts: CollectionConfig = {
|
||||
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',
|
||||
localized: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'Used for tag bucketing (e.g. "Kabel Technologie").',
|
||||
@@ -128,6 +112,7 @@ export const Posts: CollectionConfig = {
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ProductTabs } from '../blocks/ProductTabs';
|
||||
export const Products: CollectionConfig = {
|
||||
slug: 'products',
|
||||
admin: {
|
||||
defaultColumns: ['featuredImage', 'title', 'sku', 'locale', 'updatedAt', '_status'],
|
||||
defaultColumns: ['featuredImage', 'title', 'sku', 'updatedAt', '_status'],
|
||||
},
|
||||
versions: {
|
||||
drafts: true,
|
||||
@@ -42,6 +42,7 @@ export const Products: CollectionConfig = {
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'sku',
|
||||
@@ -52,6 +53,7 @@ export const Products: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
// slug is shared: the cable name (e.g. "n2xy") is the same in DE and EN
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
@@ -63,19 +65,7 @@ export const Products: CollectionConfig = {
|
||||
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',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'categories',
|
||||
@@ -112,11 +102,13 @@ export const Products: CollectionConfig = {
|
||||
{
|
||||
name: 'application',
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
editor: lexicalEditor({}),
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
|
||||
12
src/payload/components/Icon.tsx
Normal file
12
src/payload/components/Icon.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Icon() {
|
||||
return (
|
||||
<img
|
||||
src="/logo-blue.svg"
|
||||
alt="KLZ"
|
||||
className="klz-admin-icon"
|
||||
style={{ maxWidth: '100%', height: 'auto', maxHeight: '32px', display: 'block' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
12
src/payload/components/Logo.tsx
Normal file
12
src/payload/components/Logo.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Logo() {
|
||||
return (
|
||||
<img
|
||||
src="/logo-blue.svg"
|
||||
alt="KLZ Cables"
|
||||
className="klz-admin-logo"
|
||||
style={{ maxWidth: '100%', height: 'auto', maxHeight: '40px', display: 'block' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -29,12 +29,12 @@ export async function seedDatabase(payload: Payload) {
|
||||
payload.logger.info('📦 No products found. Creating smoke test product (NAY2Y)...');
|
||||
await payload.create({
|
||||
collection: 'products',
|
||||
locale: 'de',
|
||||
data: {
|
||||
title: 'NAY2Y Smoke Test',
|
||||
sku: 'SMOKE-TEST-001',
|
||||
slug: 'nay2y',
|
||||
description: 'A dummy product for CI/CD smoke testing and OG image verification.',
|
||||
locale: 'de',
|
||||
categories: [{ category: 'Power Cables' }],
|
||||
_status: 'published',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user