diff --git a/src/app/(frontend)/blogs/[slug]/page.tsx b/src/app/(frontend)/blogs/[slug]/page.tsx index 5c5912b..2a72433 100644 --- a/src/app/(frontend)/blogs/[slug]/page.tsx +++ b/src/app/(frontend)/blogs/[slug]/page.tsx @@ -3,13 +3,7 @@ import Header from '@/components/common/Header' import Footer from '@/components/common/Footer' import { notFound } from 'next/navigation' import { getBlogBySlug } from '@/lib/payload/blogs' -import Template1 from '@/components/blogs/templates/Template1' -import Template2 from '@/components/blogs/templates/Template2' - -const templates = { - template1: Template1, - template2: Template2, -} as const +import { BLOG_TEMPLATES } from '@/lib/blog-templates' export default async function BlogPage({ params, @@ -20,7 +14,7 @@ export default async function BlogPage({ const blog = await getBlogBySlug(slug) if (!blog) return notFound() - const Template = templates[blog.template] + const Template = BLOG_TEMPLATES[blog.template] return ( <> diff --git a/src/assets/groupedLeaves.svg b/src/assets/groupedLeaves.svg new file mode 100644 index 0000000..0fb3bc2 --- /dev/null +++ b/src/assets/groupedLeaves.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/collections/Blogs.ts b/src/collections/Blogs.ts index cf5cd08..aea7fce 100644 --- a/src/collections/Blogs.ts +++ b/src/collections/Blogs.ts @@ -33,9 +33,7 @@ export const Blogs: CollectionConfig = { beforeValidate: [ async ({ data = {}, originalDoc, req, operation }) => { const baseSource = - data.slug || - data.title || - (operation === 'update' ? originalDoc?.title : '') + data.slug || data.title || (operation === 'update' ? originalDoc?.title : '') const base = slugify((baseSource || '').toString(), { lower: true, @@ -103,6 +101,8 @@ export const Blogs: CollectionConfig = { options: [ { label: 'Template 1', value: 'template1' }, { label: 'Template 2', value: 'template2' }, + { label: 'Template 3', value: 'template3' }, + { label: 'Template 4', value: 'template4' }, ], admin: { description: 'Select which layout template to use when rendering this blog.', diff --git a/src/components/blogs/templates/Template3.tsx b/src/components/blogs/templates/Template3.tsx new file mode 100644 index 0000000..8e8dda0 --- /dev/null +++ b/src/components/blogs/templates/Template3.tsx @@ -0,0 +1,132 @@ +import React from 'react' +import Image from 'next/image' +import gL from '@/assets/groupedLeaves.svg' +import authorImage from '@/assets/people_placeholder.png' +import type { BlogType } from '@/types/blog' + +interface Template3Props { + blog: BlogType +} + +export default function Template3({ blog }: Template3Props) { + return ( +
+ {/* Navigation section */} + + + {/* Hero block */} +
+ {blog.imageAlt} + +
+

+ {/* TODO: Remove placeholder fields once the field is added to Payload blog collection */} + { + // blog.category || + 'Behind the Scenes'} +

+

+ {blog.title} +

+

{blog.description}

+
+ Written By: + {blog.authorName} +
+
+
+ +
+ {/* Content + Sidebar Image */} +
+
+
+ child.type === 'paragraph' + ? `

${(child.children ?? []) + .map((c: any) => c.text || '') + .join('')}

` + : '', + ) + .join('') || 'Content coming soon...', + }} + /> +
+ +
+
+ {blog.imageAlt} +
+

+ {/* TODO: Remove placeholder fields once the field is added to Payload blog collection */} + { + // blog.quote || + `"Insert an inspirational quote here!"`} +

+ {/* TODO: Remove placeholder fields once the field is added to Payload blog collection */} +

{ + // blog.quoteAuthor || + '- Author'}

+
+
+
+
+ + {/* About the Author */} +
+
+
+
+ {blog.authorName +
+
+ +
+

About the Author

+

+ {/* TODO: Remove placeholder fields once the field is added to Payload blog collection */} + { + // blog.authorBio || + 'This is a short bio about the author. They share stories, insights, and moments that inspire others.'} +

+
+
+
+ + {/* Bottom Decoration */} +
+ Decorative leaves +
+
+
+ ) +} diff --git a/src/components/blogs/templates/Template4.tsx b/src/components/blogs/templates/Template4.tsx new file mode 100644 index 0000000..704c9c3 --- /dev/null +++ b/src/components/blogs/templates/Template4.tsx @@ -0,0 +1,187 @@ +import React from 'react' +import Image from 'next/image' +import bigGreenKoru from '@/assets/bigGreenKoru.png' +import leaf from '@/assets/leaf.svg' +import type { BlogType } from '@/types/blog' + +interface Template3Props { + blog: BlogType +} + +export default function Template4({ blog }: Template3Props) { + // Split content into different sections + const getContentSections = () => { + const paragraphs = + blog.content?.root?.children + ?.filter((child: any) => child.type === 'paragraph') + ?.map((child: any) => (child.children ?? []).map((c: any) => c.text || '').join('')) || [] + + const totalParagraphs = paragraphs.length + const sectionSize = Math.ceil(totalParagraphs / 3) + + return { + section1: paragraphs.slice(0, sectionSize), + section2: paragraphs.slice(sectionSize, sectionSize * 2), + section3: paragraphs.slice(sectionSize * 2), + } + } + + const contentSections = getContentSections() + + const renderContent = (paragraphs: string[]) => { + if (paragraphs.length === 0) return 'Content coming soon...' + return paragraphs.map((text, index) => `

${text}

`).join('') + } + + return ( +
+ {/* Breadcrumb Navigation */} + + + {/* Hero Section */} +
+

Stories

+ +

+ {blog.title} +

+ + {/* Large decorative koru background */} +
+ Kiwi decoration +
+ + {blog.description && ( +
+ {blog.description} +
+ )} + + {/* Main Content Section */} +
+
+ {/* First Row - Image Left, Text Right */} +
+
+
+ {blog.title} +
+ {/* Decorative leaves */} +
+ Decorative leaf +
+
+ Decorative leaf +
+
+ +
+
+
+
+ + {/* Second Row - Text Left, Image Right */} +
+
+
+
+ +
+
+ {blog.title} +
+ {/* Blue koru decoration */} +
+ Decorative koru +
+
+
+ + {/* Quote Section */} +
+ {/* TODO: Remove the placeholder quote once the field is added to Payload blog collection */} +
+ "it doloribus ut rerum culpa est eligendi veniam Aut quia en assumenda eum pa + nostrum vel" +
+
- Pauline Smith
+
+ + {/* Third Row - Image Left, Text Right */} +
+
+
+ {blog.title} +
+ {/* Decorative leaves */} +
+ Decorative leaf +
+
+ Decorative leaf +
+
+ +
+
+
+
+ + {/* Bottom decorative leaves */} +
+
+ Decorative leaf +
+
+ Decorative leaf +
+
+ Decorative leaf +
+
+
+
+
+
+ ) +} diff --git a/src/lib/blog-templates.ts b/src/lib/blog-templates.ts new file mode 100644 index 0000000..be9bf3e --- /dev/null +++ b/src/lib/blog-templates.ts @@ -0,0 +1,16 @@ +import Template1 from '@/components/blogs/templates/Template1' +import Template2 from '@/components/blogs/templates/Template2' +import Template3 from '@/components/blogs/templates/Template3' +import Template4 from "@/components/blogs/templates/Template4"; + +// All valid template keys +export const TEMPLATE_KEYS = ['template1', 'template2', 'template3', 'template4'] as const +export type TemplateKey = typeof TEMPLATE_KEYS[number] + +// Map of key -> actual React component +export const BLOG_TEMPLATES: Record> = { + template1: Template1, + template2: Template2, + template3: Template3, + template4: Template4, +} diff --git a/src/lib/mapBlog.ts b/src/lib/mapBlog.ts index bdf12e7..e611325 100644 --- a/src/lib/mapBlog.ts +++ b/src/lib/mapBlog.ts @@ -1,6 +1,7 @@ import type { Blog } from '@/payload-types' import placeholderImage from '@/assets/landscape_placeholder.png' -import { TEMPLATE_KEYS, type TemplateKey, type BlogType } from '@/types/blog' +import { type BlogType } from '@/types/blog' +import { TEMPLATE_KEYS, type TemplateKey } from "@/lib/blog-templates"; export function mapPayloadBlog(b: Blog): BlogType { const template: TemplateKey = TEMPLATE_KEYS.includes(b.template as TemplateKey) diff --git a/src/lib/payload/blogs.ts b/src/lib/payload/blogs.ts index e8e3564..38410fd 100644 --- a/src/lib/payload/blogs.ts +++ b/src/lib/payload/blogs.ts @@ -2,16 +2,7 @@ import { fetchJSON } from './client'; import type { Blog } from '@/payload-types'; import {mapPayloadBlog} from "@/lib/mapBlog"; import {BlogType} from "@/types/blog"; - -type Paginated = { - docs: T[]; - totalDocs: number; - limit: number; - page: number; - totalPages: number; - hasNextPage: boolean; - hasPrevPage: boolean; -}; +import type {Paginated} from "@/types/pagination"; export async function getBlogs(opts: { page?: number; limit?: number } = {}) { diff --git a/src/lib/payload/events.ts b/src/lib/payload/events.ts index d5fbd5d..dc20fd8 100644 --- a/src/lib/payload/events.ts +++ b/src/lib/payload/events.ts @@ -2,16 +2,8 @@ import { mapPayloadEvent } from '@/lib/mapEvent' import { EventType } from '@/types/event' import type { Event } from '@/payload-types' import {fetchJSON} from "@/lib/payload/client"; +import type {Paginated} from "@/types/pagination"; -type Paginated = { - docs: T[]; - totalDocs: number; - limit: number; - page: number; - totalPages: number; - hasNextPage: boolean; - hasPrevPage: boolean; -}; /** * Fetch upcoming events (future-dated). diff --git a/src/lib/payload/members.ts b/src/lib/payload/members.ts index a6f9b1f..8684da1 100644 --- a/src/lib/payload/members.ts +++ b/src/lib/payload/members.ts @@ -1,20 +1,9 @@ -import { fetchJSON } from './client' -import type { Member } from '@/payload-types' +import {fetchJSON} from './client' +import type {Member} from '@/payload-types' +import type {Paginated} from "@/types/pagination"; -type PaginatedResponse = { - docs: Member[] - totalDocs: number - limit: number - totalPages: number - page: number - pagingCounter: number - hasPrevPage: boolean - hasNextPage: boolean - prevPage: number | null - nextPage: number | null -} export async function getMembers() { - const data = await fetchJSON('/api/members?depth=2&sort=createdAt&limit=100') + const data = await fetchJSON>('/api/members?depth=2&sort=createdAt&limit=100') return data.docs } diff --git a/src/payload-types.ts b/src/payload-types.ts index c388b82..60da439 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -201,7 +201,7 @@ export interface Blog { /** * Select which layout template to use when rendering this blog. */ - template: 'template1' | 'template2'; + template: 'template1' | 'template2' | 'template3' | 'template4'; published?: boolean | null; updatedAt: string; createdAt: string; diff --git a/src/types/blog.ts b/src/types/blog.ts index 5b46a95..ce94394 100644 --- a/src/types/blog.ts +++ b/src/types/blog.ts @@ -1,7 +1,5 @@ import { StaticImageData } from 'next/image' - -export const TEMPLATE_KEYS = ['template1', 'template2'] as const -export type TemplateKey = typeof TEMPLATE_KEYS[number] +import type {TemplateKey} from '@/lib/blog-templates' export type BlogType = { id: string diff --git a/src/types/pagination.ts b/src/types/pagination.ts new file mode 100644 index 0000000..b52e311 --- /dev/null +++ b/src/types/pagination.ts @@ -0,0 +1,12 @@ +export type Paginated = { + docs: T[] + totalDocs: number + limit: number + totalPages: number + page: number + hasNextPage?: boolean + hasPrevPage?: boolean + pagingCounter?: number + prevPage?: number | null + nextPage?: number | null +}