+ );
+}
diff --git a/components/campaigns/AnnouncementBanner.tsx b/components/campaigns/AnnouncementBanner.tsx
new file mode 100644
index 000000000000..dc595f415735
--- /dev/null
+++ b/components/campaigns/AnnouncementBanner.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+
+import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading';
+import { ParagraphTypeStyle } from '@/types/typography/Paragraph';
+
+import Button from '../buttons/Button';
+import Heading from '../typography/Heading';
+import Paragraph from '../typography/Paragraph';
+import AnnouncementRemainingDays from './AnnouncementRemainingDays';
+
+interface BannerProps {
+ title: string;
+ dateLocation: string;
+ cfaText: string;
+ eventName: string;
+ cfpDeadline: string;
+ link: string;
+ city: string;
+ activeBanner: boolean;
+ small: boolean;
+ className: string;
+}
+
+/**
+ * @description The banner to use for Announcement
+ * @param {string} props.title - The title of the banner
+ * @param {string} props.dateLocation - The date and location of the banner
+ * @param {string} props.cfaText - The call for action text
+ * @param {string} props.eventName - The name of the event
+ * @param {string} props.cfpDeadline - The deadline for the call for speakers
+ * @param {string} props.link - The link of the banner
+ * @param {string} props.city - The city of the banner
+ * @param {Boolean} props.activeBanner - Whether the banner is active
+ * @param {Boolean} props.small - Whether the banner is small
+ * @param {string} props.className - The class name of the banner
+ */
+export default function Banner({
+ title,
+ dateLocation,
+ cfaText,
+ eventName,
+ cfpDeadline,
+ link,
+ city,
+ activeBanner,
+ small,
+ className
+}: BannerProps) {
+ return (
+
+
+ {title}
+
+
+ {city}
+
+ {dateLocation}
+
+
+
+
+
+ );
+}
diff --git a/components/campaigns/AnnouncementHero.tsx b/components/campaigns/AnnouncementHero.tsx
new file mode 100644
index 000000000000..1699a07440ad
--- /dev/null
+++ b/components/campaigns/AnnouncementHero.tsx
@@ -0,0 +1,98 @@
+import { useEffect, useState } from 'react';
+
+import ArrowLeft from '../icons/ArrowLeft';
+import ArrowRight from '../icons/ArrowRight';
+import Container from '../layout/Container';
+import Banner from './AnnouncementBanner';
+import { banners } from './banners';
+
+interface IAnnouncementHeroProps {
+ className?: string;
+ small?: boolean;
+}
+
+/**
+ * @param {string} props.className - The class name of the announcement hero
+ * @param {Boolean} props.small - Whether the banner is small
+ * @param {Boolean} props.hideVideo - Whether the video should be hidden
+ * @description The announcement hero
+ */
+export default function AnnouncementHero({ className = '', small = false }: IAnnouncementHeroProps) {
+ const [activeIndex, setActiveIndex] = useState(0);
+
+ const len = banners.length;
+
+ const goToPrevious = () => {
+ setActiveIndex((prevIndex) => (prevIndex === 0 ? len - 1 : prevIndex - 1));
+ };
+
+ const goToNext = () => {
+ setActiveIndex((prevIndex) => (prevIndex === len - 1 ? 0 : prevIndex + 1));
+ };
+
+ const goToIndex = (index: number) => {
+ setActiveIndex(index);
+ };
+
+ useEffect(() => {
+ const interval = setInterval(() => setActiveIndex((index) => index + 1), 5000);
+
+ return () => {
+ clearInterval(interval);
+ };
+ }, [activeIndex]);
+
+ return (
+
+
+
+
+
+
+
+
+ {post.canonical && }
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/components/layout/Column.tsx b/components/layout/Column.tsx
new file mode 100644
index 000000000000..051848c8d568
--- /dev/null
+++ b/components/layout/Column.tsx
@@ -0,0 +1,15 @@
+interface IColumnProps {
+ children: React.ReactNode;
+}
+
+/**
+ * @description The column layout with the content
+ * @param props.children - The content of the column
+ */
+export default function Column({ children }: IColumnProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/components/layout/Container.tsx b/components/layout/Container.tsx
index f6969a871be7..7bbb6189a1e8 100644
--- a/components/layout/Container.tsx
+++ b/components/layout/Container.tsx
@@ -1,3 +1,5 @@
+import React from 'react';
+
interface IContainerProps {
children: React.ReactNode;
fluid?: boolean;
@@ -16,10 +18,10 @@ interface IContainerProps {
* @param {Boolean} props.fluid - Whether the container is fluid
* @param {Boolean} props.flex - Whether the container is flex
* @param {Boolean} props.wide - Whether the container is wide
- * @param {String} props.padding - The padding of the container
+ * @param {string} props.padding - The padding of the container
* @param {Boolean} props.flexReverse - Whether the container is flex reverse
- * @param {String} props.cssBreakingPoint - The CSS breaking point of the container
- * @param {String} props.className - The class name of the container
+ * @param {string} props.cssBreakingPoint - The CSS breaking point of the container
+ * @param {string} props.className - The class name of the container
* @param {React.ElementType} props.as - The element type of the container
*/
export default function Container({
diff --git a/components/layout/GenericLayout.tsx b/components/layout/GenericLayout.tsx
new file mode 100644
index 000000000000..8fc2ee7fdb23
--- /dev/null
+++ b/components/layout/GenericLayout.tsx
@@ -0,0 +1,52 @@
+// import AnnouncementHero from '../campaigns/AnnoucementHero';
+import Head from '../Head';
+import Container from './Container';
+
+interface IGenericLayoutProps {
+ title: string;
+ description: string;
+ image: string;
+ children?: React.ReactNode;
+ wide?: boolean;
+ hideBanner?: boolean;
+}
+
+/**
+ * @description The generic layout with the content
+ * @param props.title - The title of the page
+ * @param props.description - The description of the page
+ * @param props.image - The image of the page
+ * @param props.children - The content of the page
+ * @param props.wide - The width of the page
+ * @param props.hideBanner - The banner of the page
+ */
+export default function GenericLayout({
+ title,
+ description,
+ image,
+ children,
+ wide = true,
+ // eslint-disable-next-line unused-imports/no-unused-vars, no-unused-vars
+ hideBanner = false
+}: IGenericLayoutProps) {
+ if (!title || !description || !image) {
+ throw new Error('Props `title`, `description`, and `image` are required at GenericLayout component.');
+ }
+
+ return (
+
+
+
+
+ {/* */}
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/components/layout/GenericPostLayout.tsx b/components/layout/GenericPostLayout.tsx
new file mode 100644
index 000000000000..2fecd6be148e
--- /dev/null
+++ b/components/layout/GenericPostLayout.tsx
@@ -0,0 +1,53 @@
+import ErrorPage from 'next/error';
+import { useRouter } from 'next/router';
+
+import type { IPosts } from '@/types/post';
+
+import GenericPostContext from '../../context/GenericPostContext';
+// import AnnouncementHero from '../campaigns/AnnoucementHero';
+import Head from '../Head';
+import Container from './Container';
+
+interface IGenericPostLayoutProps {
+ // Commnon to all posts
+ post: IPosts['blog'][number];
+ children: React.ReactNode;
+}
+
+/**
+ * @description The blog layout with the post and its content
+ * @param props.post - The blog post to render in the layout
+ * @param props.children - The content of the blog post
+ */
+export default function GenericPostLayout({ post, children }: IGenericPostLayoutProps) {
+ const router = useRouter();
+
+ if (!post) return ;
+ if (post.title === undefined && post.slug !== '/about') throw new Error('Post title is required');
+
+ if (!router.isFallback && !post?.slug) {
+ return ;
+ }
+
+ return (
+
+ {/* */}
+
+
+
+
+ {post.title}
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/components/layout/GenericWideLayout.tsx b/components/layout/GenericWideLayout.tsx
new file mode 100644
index 000000000000..56ddd4842b2e
--- /dev/null
+++ b/components/layout/GenericWideLayout.tsx
@@ -0,0 +1,41 @@
+// import AnnouncementHero from '../campaigns/AnnoucementHero';
+import Head from '../Head';
+import Row from './Row';
+
+interface IGenericWideLayoutProps {
+ title: string;
+ description: string;
+ image: string;
+ children?: React.ReactNode;
+ wide?: boolean;
+}
+
+/**
+ * @description The generic layout with the content
+ * @param props.title - The title of the page
+ * @param props.description - The description of the page
+ * @param props.image - The image of the page
+ * @param props.children - The content of the page
+ * @param props.wide - The width of the page
+ */
+export default function GenericWideLayout({
+ title,
+ description,
+ image,
+ children
+ // wide = true
+}: IGenericWideLayoutProps) {
+ if (!title || !description || !image) {
+ throw new Error('Props `title`, `description`, and `image` are required at GenericLayout component.');
+ }
+
+ return (
+ <>
+
+
+ {/* */}
+ {children}
+
+ >
+ );
+}
diff --git a/components/layout/Layout.tsx b/components/layout/Layout.tsx
new file mode 100644
index 000000000000..ee5fe2390fd8
--- /dev/null
+++ b/components/layout/Layout.tsx
@@ -0,0 +1,60 @@
+import { useRouter } from 'next/router';
+
+import type { IPosts } from '@/types/post';
+
+import BlogContext from '../../context/BlogContext';
+import { getAllPosts, getPostBySlug } from '../../utils/api';
+import BlogLayout from './BlogLayout';
+// import DocsLayout from './DocsLayout';
+import GenericPostLayout from './GenericPostLayout';
+
+interface ILayoutProps {
+ children: React.ReactNode;
+}
+
+/**
+ * @description The layout with the content
+ * @param props.children - The content of the layout
+ */
+export default function Layout({ children }: ILayoutProps): JSX.Element {
+ const { pathname } = useRouter();
+ const posts = getAllPosts();
+ // const allDocPosts = posts.docs.filter((p) => p.slug.startsWith('/docs/'));
+
+ if (pathname.startsWith('/docs')) {
+ // const post = getDocBySlug(posts.docs as IPost[], pathname);
+
+ return <>>;
+ //
+ //
+ // {children}
+ //
+ //
+ // );
+ }
+ if (pathname.startsWith('/blog/')) {
+ const post = getPostBySlug(pathname, 'blog');
+
+ return (
+