From ed756d0646e74073419fcce9bf769f1a078451a5 Mon Sep 17 00:00:00 2001 From: dzfelix Date: Fri, 16 Aug 2024 14:43:07 +0300 Subject: [PATCH] blog contentful --- .env | 3 + package.json | 2 + src/app/[locale]/blog/[blogId]/page.tsx | 150 ------------ src/app/[locale]/blog/[slug]/page.tsx | 87 +++++++ .../[locale]/blog/category/[slug]/page.tsx | 101 ++++++++ src/app/[locale]/blog/page.tsx | 228 +++++------------- src/lib/contentful/RichText.tsx | 16 ++ src/lib/contentful/authors.ts | 16 ++ src/lib/contentful/blogPosts.ts | 99 ++++++++ src/lib/contentful/blogPostsCategories.ts | 36 +++ src/lib/contentful/contentImage.ts | 27 +++ src/lib/contentful/contentfulClient.ts | 28 +++ src/types/author.ts | 20 ++ src/types/blogPost.ts | 39 +++ src/types/blogPostCategory.ts | 20 ++ src/types/blogWidgets/widgetMedia.ts | 20 ++ src/types/blogWidgets/widgetParagraph.ts | 20 ++ 17 files changed, 589 insertions(+), 323 deletions(-) delete mode 100644 src/app/[locale]/blog/[blogId]/page.tsx create mode 100644 src/app/[locale]/blog/[slug]/page.tsx create mode 100644 src/app/[locale]/blog/category/[slug]/page.tsx create mode 100644 src/lib/contentful/RichText.tsx create mode 100644 src/lib/contentful/authors.ts create mode 100644 src/lib/contentful/blogPosts.ts create mode 100644 src/lib/contentful/blogPostsCategories.ts create mode 100644 src/lib/contentful/contentImage.ts create mode 100644 src/lib/contentful/contentfulClient.ts create mode 100644 src/types/author.ts create mode 100644 src/types/blogPost.ts create mode 100644 src/types/blogPostCategory.ts create mode 100644 src/types/blogWidgets/widgetMedia.ts create mode 100644 src/types/blogWidgets/widgetParagraph.ts diff --git a/.env b/.env index 91a7cbb..509d982 100644 --- a/.env +++ b/.env @@ -1,3 +1,6 @@ NEXT_PUBLIC_SERVER_BASE_URL=https://api.bbuddy.expert/api NEXT_PUBLIC_AGORA_APPID=ed90c9dc42634e5687d4e2e0766b363f +CONTENTFUL_SPACE_ID = voxpxjq7y7vf +CONTENTFUL_ACCESS_TOKEN = s99GWKfpDKkNwiEJ3pN7US_tmqsGvDlaex-sOJwpzuc +CONTENTFUL_PREVIEW_ACCESS_TOKEN = Z9WOKpLDbKNj7xVOmT_VXYNLH0AZwISFvQsq0PQlHfE \ No newline at end of file diff --git a/package.json b/package.json index fdfb4c7..4b6b281 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,13 @@ "@ant-design/cssinjs": "^1.18.1", "@ant-design/icons": "^5.2.6", "@ant-design/nextjs-registry": "^1.0.0", + "@contentful/rich-text-react-renderer": "^15.22.9", "agora-rtc-react": "^2.1.0", "agora-rtc-sdk-ng": "^4.20.2", "antd": "^5.12.1", "antd-img-crop": "^4.21.0", "axios": "^1.6.5", + "contentful": "^10.13.3", "dayjs": "^1.11.10", "lodash": "^4.17.21", "next": "14.0.3", diff --git a/src/app/[locale]/blog/[blogId]/page.tsx b/src/app/[locale]/blog/[blogId]/page.tsx deleted file mode 100644 index 131333a..0000000 --- a/src/app/[locale]/blog/[blogId]/page.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react'; -import type { Metadata } from 'next'; -import { notFound } from 'next/navigation'; - -export const metadata: Metadata = { - title: 'Bbuddy - Blog item', - description: 'Bbuddy desc blog item' -}; - -export function generateStaticParams() { - return [{ blogId: 'news-1' }, { blogId: 'news-2' }]; -} - -export default function BlogItem({ params }: { params: { blogId: string } }) { - if (!params?.blogId) notFound(); - - return ( -
-
-

6 learnings from Shivpuri to Silicon Valley

-
Leadership & Management
-
- {`news id ${params.blogId}`}
- I’m excited to kick off this series of newsletters where I’ll be sharing my experiences, learnings, - and best practices which helped me to grow both in my personal and professional life. My hope is to - give back to the community and help anyone connect directly with me who may have got impacted with - recent layoffs, dealing with immigration challenges. -
-
- -
-
-
- -
-
Sonali Garg
-
February 6th, 2023
-
-
-
-
- - 165 -
-
- - Share -
-
-
-
-

- This is not about layoffs, it's about living with whatever life throws at you.. -

-

- Over the past few months, as the macro-economic events have unfolded, I have heard voices filled - with anxiety, helplessness and general lack of confidence to deal with this ambiguity from my - mentees, colleagues, friends and family. I was laid off from Meta last November and I firmly - believe this is nothing but a bump in the road that might seem like a steep climb in the - short-term. I may not have all the answers but this has inspired me to share my story. If you - are looking for a sob story, you can stop reading now. Ever wondered what it takes for a girl - born into a conservative family in a small sleepy town in India, who lost one of her parents at - age 17, earned her living while pursuing engineering, moved to the UK by herself and ended up - working in big tech in Silicon valley? My goal with this series of posts is to inspire and share - my mental models that helped me throughout my professional and personal life. -

-

- After completing my engineering, I started my career at a small software company in Bhopal and - then worked for TCS(Tata Consultancy Services), one of the largest IT-outsourcing companies in - the world for almost 5 years. Over the past 14 years, I have worked for big tech companies like - Meta (Facebook) and Google, wore multiple hats, led strategic programs, scaled multi - billion-dollar businesses, built teams and helped achieve business operational excellence. - Throughout my career, I’ve dealt with several challenges from execution to scale to building a - high performance team. A lot of my early struggles were about how to assimilate in a new - culture, create a network in a new environment, earn trust, create and nurture work - relationships into fruitful friendships and so on. -

-

- I was born in a conservative family in a small town called ‘Shivpuri’, also known as ‘Mini - Kashmir’ because of its natural beauty. My father was a civil engineer working on Madikheda Dam - on Sindh river and was a strict disciplinarian. He was gone from dawn to dusk and was always - focused. My mother was a teacher in a school that was about 30 kms from our home. We (me and my - sister) would often be left with neighbors to be taken care of and this led us to become - independent at an early age. Our otherwise slow paced, simple life with only a few families - around in the government quarters that were set up to support construction of the dam was filled - with natural beauty, wildlife and a community of close friends. Our lives were balanced and - while my parents worked hard to provide basic needs, we were satisfied. There were only a few - schools with Hindi being the prevalent language as the medium of teaching. There were no - colleges for advanced studies and most girls did not go to college often married off by their - 18th birthday. Generally speaking, we had a joyous childhood with just the basics. While most - folks we interacted with were not highly educated nor ambitious, earned lower middle class - salaries and lacked exposure to the outside world but there was plenty to learn from them. - People had learnt to stick together in good and bad times. They embodied the old school - qualities of hard work, dedication and commitment. Be willing to give it all- hard work, - dedication and commitment. -

-

- In 2003, my father passed away suddenly and we found ourselves in crisis. My mother was a - teacher and she did not have time to deal with her grief. Rather, she was struggling to garner - support to get transferred to a school in Bhopal, capital of Madhya Pradesh to be closer to our - maternal grandparents. As we uprooted ourselves from Shivpuri to Bhopal, one of my father’s - loyal friends came to help load the moving truck. While he had nothing to gain out of us, he - continued to serve us until the last day in Shivpuri. Remember, in crisis your team matters more - than any other time. Advocate for them ruthlessly in good and bad times, they will come through - in crisis. -

-

- Eventually we found our footing, my mother’s job was transferred to a local school in Bhopal and - I got admission in a government engineering college. My sister was still attending high school - and both of us were teaching tuition classes to middle school students in the evenings to make - ends meet. I also started a tiffin service for a few out of town students while attending - college to pay for my transportation and cost of supplies. We refused to give up. Persevere when - all else fails. -

-

- Our 5 years went by quickly in Bhopal as we worked towards improving our financial situation and - I completed my Bachelors in Computer Science. This was the time I first stepped out to live in a - metropolitan city, Mumbai for my job at TCS. This was a paradigm shift from Bhopal and I was - blown away to meet so many talented folks in Mumbai. In my head, I did not belong in this place. - I had imposter syndrome and felt like an outsider trying to make it in a new city. Most people I - met were fluent in more than 1 language, well-dressed, communicated openly and with confidence, - and presented themselves well. I was always in a dilemma when it came to adopting values. It - took me a while to adjust to it but I was still not confident about my work and communication - while my hard skills that I learnt in engineering were top notch. I kept questioning my - abilities but persisted. This was not the first time I was out of my comfort zone. Persist, when - in discomfort. -

-

- I worked with multiple global companies who were clients of TCS and was presented an opportunity - to move to Scotland, UK for an year to work for GE, who was also a client. This was my first - opportunity to explore a different culture, food, music, languages etc. I remember working on my - english when in Mumbai, in preparation for my UK trip. It was really difficult to understand the - accent in the UK, even though language was not a barrier. I still remember certain words would - just not get across no matter how hard some of my colleagues tried and they would end up using - signs to convey. Be prepared, opportunities come to those who are prepared. -

-

- In 2013, I came to the US on a dependent visa after marriage and quickly realized the curse of - H4 visa. I paved my path by going back to school at UC Berkeley and then jumped back into - building my career from scratch. While working in the US over the past years, I realized college - degrees with good grades and certifications definitely help you to get your foot in the door but - are not enough to be successful in your career. As I was again starting from scratch in a new - culture, determined to do whatever it takes, having done this a few times before, it doesn’t - scare me as much. Never be afraid to start from zero again! -

-
-
-
- ); -}; diff --git a/src/app/[locale]/blog/[slug]/page.tsx b/src/app/[locale]/blog/[slug]/page.tsx new file mode 100644 index 0000000..d1573dc --- /dev/null +++ b/src/app/[locale]/blog/[slug]/page.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import type { Metadata } from 'next'; +import { draftMode } from 'next/headers' +import { notFound } from 'next/navigation'; +import {fetchBlogPost, fetchBlogPosts, Widget} from "../../../../lib/contentful/blogPosts"; +import Util from "node:util"; +import RichText from "../../../../lib/contentful/RichText"; + +export const metadata: Metadata = { + title: 'Bbuddy - Blog item', + description: 'Bbuddy desc blog item' +}; + +interface BlogPostPageParams { + slug: string +} + +interface BlogPostPageProps { + params: BlogPostPageParams +} + +export async function generateStaticParams(): Promise { + const blogPosts = await fetchBlogPosts({ preview: false }) + + return blogPosts.map((post) => ({ slug: post.slug })) +} +function renderWidget (widget: Widget) { + switch (widget.type){ + case 'widgetParagraph': + return ( + <> +

+ {widget.widget.subTitle} +

+ + + ) + case 'widgetMedia': + return ( + + ) + } +} + +export default async function BlogItem({params}: { params: BlogPostPageParams }) { + const item = await fetchBlogPost({slug: params.slug, preview: draftMode().isEnabled }) + console.log('BLOG POST') + console.log(Util.inspect(item, {showHidden: false, depth: null, colors: true})) + if (!item) notFound(); + + return ( +
+
+

{item.title}

+
{item.category}
+
+ +
+
+ +
+
+
+ +
+
{item.author?.name}
+
{item.createdAt}
+
+
+
+
+ + 165 +
+
+ + Share +
+
+
+
+ {item.body.map(renderWidget)} +
+
+
+ ); +}; diff --git a/src/app/[locale]/blog/category/[slug]/page.tsx b/src/app/[locale]/blog/category/[slug]/page.tsx new file mode 100644 index 0000000..696247c --- /dev/null +++ b/src/app/[locale]/blog/category/[slug]/page.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import type { Metadata } from 'next'; +import { draftMode } from 'next/headers' +import {unstable_setRequestLocale} from "next-intl/server"; +import Link from "next/link"; +import {fetchBlogPosts} from "../../../../../lib/contentful/blogPosts"; +import {fetchBlogPostCategories} from "../../../../../lib/contentful/blogPostsCategories"; + +export const metadata: Metadata = { + title: 'Bbuddy - Blog', + description: 'Bbuddy desc blog' +}; + +interface BlogPostPageParams { + slug: string + locale: string +} +interface BlogPostPageProps { + params: BlogPostPageParams +} + +export default async function Blog({params}: { params: BlogPostPageParams }) { + unstable_setRequestLocale(params.locale); + const data = await fetchBlogPosts({ preview: draftMode().isEnabled, locale: params.locale, category: params.slug }) + const cats = await fetchBlogPostCategories(false) + return ( +
+
+
+

+ Mentorship, Career
+ Development & Coaching. +

+
+

The ins-and-outs of building a career in tech, gaining
experience

+

from a mentor, and getting your feet wet with coaching.

+
+
+ +
+
+
+
+
+
+ { + cats.map((cat, i)=>( + {cat.title} + )) + } +
+
+
+
+
+
+ {data.map((item, i) => ( +
  • + +
    + {item.listImage?.alt}/ +
    +
    +
    +
    + {item.title} +
    +
    {item.category}
    +
    + {item.excerpt} +
    +
    +
    +
    + +
    +
    {item.author.name}
    +
    {item.createdAt}
    +
    +
    +
    +
    + + 165 +
    +
    + + Share +
    +
    +
    +
    + +
  • + ))} +
    +
    +
    +
    + ); +} diff --git a/src/app/[locale]/blog/page.tsx b/src/app/[locale]/blog/page.tsx index 20f2989..3ce264d 100644 --- a/src/app/[locale]/blog/page.tsx +++ b/src/app/[locale]/blog/page.tsx @@ -1,210 +1,92 @@ import React from 'react'; import type { Metadata } from 'next'; +import * as Util from "node:util"; +import {fetchBlogPosts} from "../../../lib/contentful/blogPosts"; +import {unstable_setRequestLocale} from "next-intl/server"; +import Link from "next/link"; +import {fetchBlogPostCategories} from "../../../lib/contentful/blogPostsCategories"; export const metadata: Metadata = { title: 'Bbuddy - Blog', description: 'Bbuddy desc blog' }; -export default function Blog() { + + +export default async function Blog({ params: { locale } }: { params: { locale: string } }) { + unstable_setRequestLocale(locale); + const data = await fetchBlogPosts(false, locale) + const cats = await fetchBlogPostCategories(false) return (

    - Mentorship, Career
    + Mentorship, Career
    Development & Coaching.

    -

    The ins-and-outs of building a career in tech, gaining
    experience

    +

    The ins-and-outs of building a career in tech, gaining
    experience

    from a mentor, and getting your feet wet with coaching.

    - +
    - -
    - -
    -
    -
    -
    - 6 learnings from Shivpuri to Silicon Valley + {data.map((item, i) => ( +
  • + +
    + {item.listImage?.alt}/
    -
    Leadership & Management
    -
    - I’m excited to kick off this series of newsletters where I’ll be sharing my - experiences, - learnings, and best practices which helped me to grow both in my personal and - professional life. My hope is to give back to the community and help anyone - connect directly with me who may have got impacted with recent layoffs, - dealing with immigration challenges. -
    -
  • -
    -
    - -
    -
    Sonali Garg
    -
    February 6th, 2023
    +
    +
    +
    + {item.title} +
    +
    {item.category}
    +
    + {item.excerpt} +
    +
    +
    +
    + +
    +
    {item.author.name}
    +
    {item.createdAt}
    +
    +
    +
    +
    + + 165 +
    +
    + + Share +
    +
    -
    -
    - - 165 -
    -
    - - Share -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - 6 learnings from Shivpuri to Silicon Valley -
    -
    Leadership & Management
    -
    - I’m excited to kick off this series of newsletters where I’ll be sharing my - experiences, - learnings, and best practices which helped me to grow both in my personal and - professional life. My hope is to give back to the community and help anyone - connect directly with me who may have got impacted with recent layoffs, - dealing with immigration challenges. -
    -
    -
    -
    - -
    -
    Sonali Garg
    -
    February 6th, 2023
    -
    -
    -
    -
    - - 165 -
    -
    - - Share -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - 6 learnings from Shivpuri to Silicon Valley -
    -
    Leadership & Management
    -
    - I’m excited to kick off this series of newsletters where I’ll be sharing my - experiences, - learnings, and best practices which helped me to grow both in my personal and - professional life. My hope is to give back to the community and help anyone - connect directly with me who may have got impacted with recent layoffs, - dealing with immigration challenges. -
    -
    -
    -
    - -
    -
    Sonali Garg
    -
    February 6th, 2023
    -
    -
    -
    -
    - - 165 -
    -
    - - Share -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - 6 learnings from Shivpuri to Silicon Valley -
    -
    Leadership & Management
    -
    - I’m excited to kick off this series of newsletters where I’ll be sharing my - experiences, - learnings, and best practices which helped me to grow both in my personal and - professional life. My hope is to give back to the community and help anyone - connect directly with me who may have got impacted with recent layoffs, - dealing with immigration challenges. -
    -
    -
    -
    - -
    -
    Sonali Garg
    -
    February 6th, 2023
    -
    -
    -
    -
    - - 165 -
    -
    - - Share -
    -
    -
    -
    -
    + + + ))}
    diff --git a/src/lib/contentful/RichText.tsx b/src/lib/contentful/RichText.tsx new file mode 100644 index 0000000..597d70b --- /dev/null +++ b/src/lib/contentful/RichText.tsx @@ -0,0 +1,16 @@ +import { Document as RichTextDocument } from '@contentful/rich-text-types' +import { documentToReactComponents } from '@contentful/rich-text-react-renderer' + +type RichTextProps = { + document: RichTextDocument | null +} + +function RichText({ document }: RichTextProps) { + if (!document) { + return null + } + + return <>{documentToReactComponents(document)} +} + +export default RichText \ No newline at end of file diff --git a/src/lib/contentful/authors.ts b/src/lib/contentful/authors.ts new file mode 100644 index 0000000..57a2f30 --- /dev/null +++ b/src/lib/contentful/authors.ts @@ -0,0 +1,16 @@ +import { parseContentfulContentImage } from './contentImage' +import {Author, AuthorEntry} from "../../types/author"; + + + + +export function parseContentfulAuthor(authorEntry?: AuthorEntry): Author | null { + if (!authorEntry) { + return null + } + + return { + name: authorEntry.fields.name || '', + avatar: parseContentfulContentImage(authorEntry.fields.avatar), + } +} \ No newline at end of file diff --git a/src/lib/contentful/blogPosts.ts b/src/lib/contentful/blogPosts.ts new file mode 100644 index 0000000..6fa939b --- /dev/null +++ b/src/lib/contentful/blogPosts.ts @@ -0,0 +1,99 @@ +import { Entry } from 'contentful' +import contentfulClient from './contentfulClient' +import { parseContentfulContentImage } from './contentImage' +import {BlogPost, BlogPostEntry, BlogPostSkeleton} from "../../types/blogPost"; +import {parseContentfulAuthor} from "./authors"; +import dayjs from "dayjs"; +import {WidgetMedia, WidgetMediaEntry} from "../../types/blogWidgets/widgetMedia"; +import {WidgetParagraph} from "../../types/blogWidgets/widgetParagraph"; +import entry from "next/dist/server/typescript/rules/entry"; +import Util from "node:util"; + +type PostEntry = BlogPostEntry//Entry +type widgetEnum = WidgetParagraph | WidgetMedia +export type Widget = { + widget: widgetEnum + type: string +} +type WidgetEntry = WidgetMediaEntry | WidgetParagraph +function parseWidgets(entries?: Array): Array | null { + if (!entries || !entries.length) { + return null + } + return entries.map((entry: WidgetEntry) => { + const wType = entry.sys.contentType.sys.id + let wObj = {} as widgetEnum + switch (wType){ + case 'widgetParagraph': + wObj = { + subTitle: entry.fields.subTitle, + body: entry.fields.body + } + break + case 'widgetMedia': + wObj = { + decription: entry.fields.decription || '', + file: parseContentfulContentImage(entry.fields.file) + } + break + } + return { + type: wType, + widget: wObj + } + }) +} + +export function parseContentfulBlogPost(entry?: PostEntry): BlogPost | null { + if (!entry) { + return null + } + + return { + title: entry.fields.title || '', + slug: entry.fields.slug, + excerpt: entry.fields.excerpt || '', + listImage: parseContentfulContentImage(entry.fields.listImage), + author: parseContentfulAuthor(entry.fields.author), + createdAt: dayjs(entry.sys.createdAt).format('MMM DD, YYYY'), + category: entry.fields.category.fields.title, + body: parseWidgets(entry.fields.body) || [] + } +} + +interface FetchBlogPostsOptions { + preview: boolean + local?: string + category?: string +} +export async function fetchBlogPosts({ preview, category }: FetchBlogPostsOptions): Promise { + const contentful = contentfulClient({ preview }) + const query = { + content_type: 'blogPost', + select: ['fields.title', 'fields.excerpt', 'fields.author', 'fields.listImage', 'fields.author', 'fields.category', 'sys.createdAt', 'fields.slug'], + order: ['sys.createdAt'], + } + if (category){ + query['fields.category.fields.slug'] = category + query['fields.category.sys.contentType.sys.id']='blogPostCategory' + } + const blogPostsResult = await contentful.getEntries(query) + + return blogPostsResult.items.map((blogPostEntry) => parseContentfulBlogPost(blogPostEntry) as BlogPost) +} + +interface FetchBlogPostOptions { + slug: string + preview: boolean +} +export async function fetchBlogPost({ slug, preview }: FetchBlogPostOptions): Promise { + const contentful = contentfulClient({ preview }) + + const blogPostsResult = await contentful.getEntries({ + content_type: 'blogPost', + 'fields.slug': slug, + include: 2, + }) + + return parseContentfulBlogPost(blogPostsResult.items[0]) +} \ No newline at end of file diff --git a/src/lib/contentful/blogPostsCategories.ts b/src/lib/contentful/blogPostsCategories.ts new file mode 100644 index 0000000..84006a3 --- /dev/null +++ b/src/lib/contentful/blogPostsCategories.ts @@ -0,0 +1,36 @@ +import { Entry } from 'contentful' +import contentfulClient from './contentfulClient' +import { parseContentfulContentImage } from './contentImage' +import {BlogPost, BlogPostEntry, BlogPostSkeleton} from "../../types/blogPost"; +import {parseContentfulAuthor} from "./authors"; +import dayjs from "dayjs"; +import {BlogPostCategory, BlogPostCategoryEntry, BlogPostCategorySkeleton} from "../../types/blogPostCategory"; + +type ListEntry = BlogPostCategoryEntry + + + +export function parseContentfulBlogPostCategory(entry?: ListEntry): BlogPostCategory | null { + if (!entry) { + return null + } + return { + title: entry.fields.title || '', + slug: entry.fields.slug || '' + } +} + +interface FetchListOptions { + preview: boolean + local?: string +} +export async function fetchBlogPostCategories({ preview }: FetchListOptions): Promise { + const contentful = contentfulClient({ preview }) + + const results = await contentful.getEntries({ + content_type: 'blogPostCategory', + order: ['fields.title'], + }) + + return results.items.map((entry) => parseContentfulBlogPostCategory(entry) as BlogPostCategory) +} diff --git a/src/lib/contentful/contentImage.ts b/src/lib/contentful/contentImage.ts new file mode 100644 index 0000000..3048548 --- /dev/null +++ b/src/lib/contentful/contentImage.ts @@ -0,0 +1,27 @@ +import { Asset, AssetLink } from 'contentful' + +export interface ContentImage { + src: string + alt: string + width: number + height: number +} + +export function parseContentfulContentImage( + asset?: Asset | { sys: AssetLink } +): ContentImage | null { + if (!asset) { + return null + } + + if (!('fields' in asset)) { + return null + } + + return { + src: asset.fields.file?.url || '', + alt: asset.fields.description || '', + width: asset.fields.file?.details.image?.width || 0, + height: asset.fields.file?.details.image?.height || 0, + } +} \ No newline at end of file diff --git a/src/lib/contentful/contentfulClient.ts b/src/lib/contentful/contentfulClient.ts new file mode 100644 index 0000000..61fcb2e --- /dev/null +++ b/src/lib/contentful/contentfulClient.ts @@ -0,0 +1,28 @@ +import { createClient } from 'contentful' + +const { CONTENTFUL_SPACE_ID, CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_PREVIEW_ACCESS_TOKEN } = process.env + +// This is the standard Contentful client. It fetches +// content that has been published. +const client = createClient({ + space: CONTENTFUL_SPACE_ID!, + accessToken: CONTENTFUL_ACCESS_TOKEN!, +}) + +// This is a Contentful client that's been configured +// to fetch drafts and unpublished content. +const previewClient = createClient({ + space: CONTENTFUL_SPACE_ID!, + accessToken: CONTENTFUL_PREVIEW_ACCESS_TOKEN!, + host: 'preview.contentful.com', +}) + +// This little helper will let us switch between the two +// clients easily: +export default function contentfulClient({ preview = false }) { + if (preview) { + return previewClient + } + + return client +} \ No newline at end of file diff --git a/src/types/author.ts b/src/types/author.ts new file mode 100644 index 0000000..05c7eac --- /dev/null +++ b/src/types/author.ts @@ -0,0 +1,20 @@ +import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful' +import {BlogPostFields} from "./blogPost"; +import {ContentImage} from "../lib/contentful/contentImage"; + +export interface AuthorFields { + name: EntryFieldTypes.Symbol + avatar: EntryFieldTypes.AssetLink +} + +export interface Author { + name: string + avatar: ContentImage | null +} + +export type AuthorSkeleton = EntrySkeletonType +export type AuthorEntry = Entry< + AuthorSkeleton, + Modifiers, + Locales +> \ No newline at end of file diff --git a/src/types/blogPost.ts b/src/types/blogPost.ts new file mode 100644 index 0000000..c4776c4 --- /dev/null +++ b/src/types/blogPost.ts @@ -0,0 +1,39 @@ +import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful' +import {Author, AuthorSkeleton} from "./author"; +import {ContentImage} from "../lib/contentful/contentImage"; +import {BlogPostCategorySkeleton} from "./blogPostCategory"; +import {WidgetMedia, WidgetMediaSkeleton} from "./blogWidgets/widgetMedia"; +import {WidgetParagraph, WidgetParagraphSkeleton} from "./blogWidgets/widgetParagraph"; + +export interface BlogPostFields { + title?: EntryFieldTypes.Symbol + slug: EntryFieldTypes.Symbol + excerpt: EntryFieldTypes.Symbol + listImage?: EntryFieldTypes.AssetLink + author?: AuthorSkeleton + category: BlogPostCategorySkeleton + createdAt?: EntryFieldTypes.Date + body?: Array +} + +export interface BlogPostFields extends BlogPostFields{ + body: Array +} + +export interface BlogPost { + title: string + slug: string + excerpt: string + listImage: ContentImage | null + author: Author | null + category: string + createdAt: string + body: Array +} + +export type BlogPostSkeleton = EntrySkeletonType +export type BlogPostEntry = Entry< + BlogPostSkeleton, + Modifiers, + Locales +> \ No newline at end of file diff --git a/src/types/blogPostCategory.ts b/src/types/blogPostCategory.ts new file mode 100644 index 0000000..65567df --- /dev/null +++ b/src/types/blogPostCategory.ts @@ -0,0 +1,20 @@ +import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful' +import {BlogPostFields} from "./blogPost"; +import {ContentImage} from "../lib/contentful/contentImage"; + +export interface BlogPostCategoryFields { + title: EntryFieldTypes.Symbol + slug: EntryFieldTypes.Symbol +} + +export interface BlogPostCategory { + title: string + slug: string +} + +export type BlogPostCategorySkeleton = EntrySkeletonType +export type BlogPostCategoryEntry = Entry< + BlogPostCategorySkeleton, + Modifiers, + Locales +> \ No newline at end of file diff --git a/src/types/blogWidgets/widgetMedia.ts b/src/types/blogWidgets/widgetMedia.ts new file mode 100644 index 0000000..172ae23 --- /dev/null +++ b/src/types/blogWidgets/widgetMedia.ts @@ -0,0 +1,20 @@ +import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful' +import {BlogPostFields} from "./blogPost"; +import {ContentImage} from "../lib/contentful/contentImage"; + +export interface WidgetMediaFields { + decription?: EntryFieldTypes.Symbol + file?: EntryFieldTypes.AssetLink +} + +export interface WidgetMedia { + file: ContentImage | null + decription: string | null +} + +export type WidgetMediaSkeleton = EntrySkeletonType +export type WidgetMediaEntry = Entry< + WidgetMediaSkeleton, + Modifiers, + Locales +> \ No newline at end of file diff --git a/src/types/blogWidgets/widgetParagraph.ts b/src/types/blogWidgets/widgetParagraph.ts new file mode 100644 index 0000000..0d363cd --- /dev/null +++ b/src/types/blogWidgets/widgetParagraph.ts @@ -0,0 +1,20 @@ +import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful' +import {BlogPostFields} from "./blogPost"; +import {ContentImage} from "../lib/contentful/contentImage"; +import { Document as RichTextDocument } from '@contentful/rich-text-types' +export interface WidgetParagraphFields { + subTitle?: EntryFieldTypes.Symbol + body?: EntryFieldTypes.RichText +} + +export interface WidgetParagraph { + subTitle: string + body: RichTextDocument | null +} + +export type WidgetParagraphSkeleton = EntrySkeletonType +export type WidgetParagraphEntry = Entry< + WidgetParagraphSkeleton, + Modifiers, + Locales +> \ No newline at end of file