diff --git a/src/blocks/AboutProfile/Component.tsx b/src/blocks/AboutProfile/Component.tsx
new file mode 100644
index 0000000..04244e6
--- /dev/null
+++ b/src/blocks/AboutProfile/Component.tsx
@@ -0,0 +1,100 @@
+import React from 'react'
+import { ImageMedia } from '../ImageMedia' // Adjust this path to wherever your ImageMedia file lives
+import type { Media as MediaType } from '@/payload-types'
+
+type ButtonGroup = {
+ label?: string
+ url?: string
+ newTab?: boolean
+}
+
+type AboutProfileBlockProps = {
+ imagePosition?: 'left' | 'right'
+ image?: MediaType | null
+ heading?: string
+ subheading?: string
+ body?: string
+ primaryButton?: ButtonGroup
+ secondaryButton?: ButtonGroup
+}
+
+export function AboutProfileBlock({
+ imagePosition = 'left',
+ image,
+ heading,
+ subheading,
+ body,
+ primaryButton,
+ secondaryButton,
+}: AboutProfileBlockProps) {
+ const isRight = imagePosition === 'right'
+
+ // Using your unified ImageMedia component handles S3 URLs, fallback widths, heights, and alt texts seamlessly
+ const imageEl = image ? (
+
+
+
+ ) : null
+
+ const hasPrimary = primaryButton?.label && primaryButton?.url
+ const hasSecondary = secondaryButton?.label && secondaryButton?.url
+
+ return (
+
+ {(heading || subheading) && (
+
+ {heading && (
+
+ {heading}
+
+ )}
+ {subheading && (
+
{subheading}
+ )}
+
+ )}
+
+
+ {imageEl}
+
+
+ {body && (
+
{body}
+ )}
+
+ {(hasPrimary || hasSecondary) && (
+
+ )}
+
+
+
+ )
+}
diff --git a/src/blocks/AboutProfile/config.ts b/src/blocks/AboutProfile/config.ts
new file mode 100644
index 0000000..8899800
--- /dev/null
+++ b/src/blocks/AboutProfile/config.ts
@@ -0,0 +1,41 @@
+import type { Block } from 'payload'
+
+export const AboutProfileBlock: Block = {
+ slug: 'aboutProfile',
+ labels: { singular: 'About Profile', plural: 'About Profiles' },
+ fields: [
+ {
+ name: 'imagePosition',
+ type: 'select',
+ label: 'Image position',
+ defaultValue: 'left',
+ options: [
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' },
+ ],
+ },
+ { name: 'image', type: 'upload', relationTo: 'media', label: 'Image' },
+ { name: 'heading', type: 'text', label: 'Heading' },
+ { name: 'subheading', type: 'text', label: 'Subheading' },
+ { name: 'body', type: 'textarea', label: 'Body text' },
+ {
+ name: 'primaryButton',
+ type: 'group',
+ label: 'Primary button (Download CV)',
+ fields: [
+ { name: 'label', type: 'text', label: 'Label', defaultValue: 'Download CV' },
+ { name: 'file', type: 'upload', relationTo: 'media', label: 'Upload CV File' },
+ ],
+ },
+ {
+ name: 'secondaryButton',
+ type: 'group',
+ label: 'Secondary button (Custom)',
+ fields: [
+ { name: 'label', type: 'text', label: 'Label' },
+ { name: 'url', type: 'text', label: 'URL' },
+ { name: 'newTab', type: 'checkbox', label: 'Open in new tab', defaultValue: true },
+ ],
+ },
+ ],
+}
diff --git a/src/blocks/RenderBlocks.tsx b/src/blocks/RenderBlocks.tsx
index 7d2d3e2..d0649c8 100644
--- a/src/blocks/RenderBlocks.tsx
+++ b/src/blocks/RenderBlocks.tsx
@@ -12,6 +12,7 @@ import { SkillsMarqueeBlock } from '@/blocks/SkillsMarquee/Component'
import { KanbanColorBlock } from '@/blocks/KanbanColor/Component'
import { KanbanHoriBlock } from '@/blocks/KanbanHori/Component'
import { ShowcaseBlock } from '@/blocks/Showcase/Component'
+import { AboutProfileBlock } from '@/blocks/AboutProfile/Component'
const blockComponents = {
archive: ArchiveBlock,
@@ -24,6 +25,7 @@ const blockComponents = {
kanbanColor: KanbanColorBlock,
kanbanHori: KanbanHoriBlock,
showcase: ShowcaseBlock,
+ aboutProfile: AboutProfileBlock,
}
export const RenderBlocks: React.FC<{
diff --git a/src/collections/Pages/index.ts b/src/collections/Pages/index.ts
index 1db6c30..b21a4df 100644
--- a/src/collections/Pages/index.ts
+++ b/src/collections/Pages/index.ts
@@ -12,6 +12,7 @@ import { SkillsMarqueeBlock } from '../../blocks/SkillsMarquee/config'
import { KanbanColorBlock } from '../../blocks/KanbanColor/config'
import { KanbanHoriBlock } from '../../blocks/KanbanHori/config'
import { ShowcaseBlock } from '../../blocks/Showcase/config'
+import { AboutProfileBlock } from '../../blocks/AboutProfile/config'
import { hero } from '@/heros/config'
import { slugField } from 'payload'
import { populatePublishedAt } from '../../hooks/populatePublishedAt'
@@ -85,6 +86,7 @@ export const Pages: CollectionConfig<'pages'> = {
KanbanColorBlock,
KanbanHoriBlock,
ShowcaseBlock,
+ AboutProfileBlock,
],
required: true,
admin: {
diff --git a/src/payload-types.ts b/src/payload-types.ts
index ad769c3..2935720 100644
--- a/src/payload-types.ts
+++ b/src/payload-types.ts
@@ -329,6 +329,25 @@ export interface Page {
blockName?: string | null;
blockType: 'showcase';
}
+ | {
+ imagePosition?: ('left' | 'right') | null;
+ image?: (string | null) | Media;
+ heading?: string | null;
+ subheading?: string | null;
+ body?: string | null;
+ primaryButton?: {
+ label?: string | null;
+ file?: (string | null) | Media;
+ };
+ secondaryButton?: {
+ label?: string | null;
+ url?: string | null;
+ newTab?: boolean | null;
+ };
+ id?: string | null;
+ blockName?: string | null;
+ blockType: 'aboutProfile';
+ }
)[];
meta?: {
title?: string | null;
@@ -1323,6 +1342,30 @@ export interface PagesSelect {
id?: T;
blockName?: T;
};
+ aboutProfile?:
+ | T
+ | {
+ imagePosition?: T;
+ image?: T;
+ heading?: T;
+ subheading?: T;
+ body?: T;
+ primaryButton?:
+ | T
+ | {
+ label?: T;
+ file?: T;
+ };
+ secondaryButton?:
+ | T
+ | {
+ label?: T;
+ url?: T;
+ newTab?: T;
+ };
+ id?: T;
+ blockName?: T;
+ };
};
meta?:
| T