blog #1
3
.env
3
.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
|
|
@ -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",
|
||||
|
|
|
@ -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 (
|
||||
<div className="b-news-page">
|
||||
<div className="b-inner">
|
||||
<h1 className="b-news-page__title">6 learnings from Shivpuri to Silicon Valley</h1>
|
||||
<div className="news-item__badge">Leadership & Management</div>
|
||||
<div className="b-news-page__text">
|
||||
{`news id ${params.blogId}`}<br />
|
||||
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.
|
||||
</div>
|
||||
<div className="b-news-page__image">
|
||||
<img className="" src="/images/news1.png" alt="" />
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src="/images/author.png" alt="" />
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">Sonali Garg</div>
|
||||
<div className="news-item__info__date">February 6th, 2023</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news-page__inner">
|
||||
<h2 className="title-h2">
|
||||
This is not about layoffs, it's about living with whatever life throws at you..
|
||||
</h2>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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.
|
||||
</p>
|
||||
<p className="b-news-page__text">
|
||||
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!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -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<BlogPostPageParams[]> {
|
||||
const blogPosts = await fetchBlogPosts({ preview: false })
|
||||
|
||||
return blogPosts.map((post) => ({ slug: post.slug }))
|
||||
}
|
||||
function renderWidget (widget: Widget) {
|
||||
switch (widget.type){
|
||||
case 'widgetParagraph':
|
||||
return (
|
||||
<>
|
||||
<h2 className="title-h2">
|
||||
{widget.widget.subTitle}
|
||||
</h2>
|
||||
<RichText document={widget.widget.body} />
|
||||
</>
|
||||
)
|
||||
case 'widgetMedia':
|
||||
return (
|
||||
<img src={widget.widget.file?.src}/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="b-news-page">
|
||||
<div className="b-inner">
|
||||
<h1 className="b-news-page__title">{item.title}</h1>
|
||||
<div className="news-item__badge">{item.category}</div>
|
||||
<div className="b-news-page__text">
|
||||
|
||||
</div>
|
||||
<div className="b-news-page__image">
|
||||
<img className="" src="/images/news1.png" alt="" />
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src={item.author.avatar.src} alt="" />
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">{item.author?.name}</div>
|
||||
<div className="news-item__info__date">{item.createdAt}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news-page__inner">
|
||||
{item.body.map(renderWidget)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -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 (
|
||||
<div className="b-news">
|
||||
<div className="b-news__header">
|
||||
<div className="b-inner">
|
||||
<h1 className="title-h1">
|
||||
Mentorship, Career <br/>
|
||||
Development & Coaching.
|
||||
</h1>
|
||||
<div className="wrap-text">
|
||||
<p className="">The ins-and-outs of building a career in tech, gaining <br/> experience</p>
|
||||
<p className="">from a mentor, and getting your feet wet with coaching.</p>
|
||||
</div>
|
||||
<div className="b-news__header__img">
|
||||
<img className="" src="/images/news-top.png" alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news__filter ">
|
||||
<div className="b-inner">
|
||||
<div className="wrap-filter">
|
||||
{
|
||||
cats.map((cat, i)=>(
|
||||
<Link key={'blogCat'+i} href={'/'+params.locale+'/blog/category/'+cat.slug} className={"filter-item"+(cat.slug === params.slug ? ' active' : '')}>{cat.title}</Link>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news__result-list">
|
||||
<div className="b-inner">
|
||||
<div className="news-list">
|
||||
{data.map((item, i) => (
|
||||
<li key={'blogPost'+i} className="list-sidebar__item">
|
||||
<Link href={'/'+params.locale+'/blog/'+item.slug} className="news-item">
|
||||
<div className="news-item__image">
|
||||
<img className="" src={item.listImage?.src} alt={item.listImage?.alt}/>
|
||||
</div>
|
||||
<div className="news-item__inner">
|
||||
<div className="">
|
||||
<div className="news-item__title">
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="news-item__badge">{item.category}</div>
|
||||
<div className="news-item__text">
|
||||
{item.excerpt}
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src={item.author.avatar.src} alt=""/>
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">{item.author.name}</div>
|
||||
<div className="news-item__info__date">{item.createdAt}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt=""/>
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt=""/>
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -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 (
|
||||
<div className="b-news">
|
||||
<div className="b-news__header">
|
||||
<div className="b-inner">
|
||||
<h1 className="title-h1">
|
||||
Mentorship, Career <br />
|
||||
Mentorship, Career <br/>
|
||||
Development & Coaching.
|
||||
</h1>
|
||||
<div className="wrap-text">
|
||||
<p className="">The ins-and-outs of building a career in tech, gaining <br /> experience</p>
|
||||
<p className="">The ins-and-outs of building a career in tech, gaining <br/> experience</p>
|
||||
<p className="">from a mentor, and getting your feet wet with coaching.</p>
|
||||
</div>
|
||||
<div className="b-news__header__img">
|
||||
<img className="" src="/images/news-top.png" alt="" />
|
||||
<img className="" src="/images/news-top.png" alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news__filter ">
|
||||
<div className="b-inner">
|
||||
<div className="wrap-filter">
|
||||
<a href="#" className="filter-item">Leadership & Management</a>
|
||||
<a href="#" className="filter-item">Professional Development</a>
|
||||
<a href="#" className="filter-item">Research & Insights</a>
|
||||
<a href="#" className="filter-item">Well-Being</a>
|
||||
<a href="#" className="filter-item">Diversity & Inclusion</a>
|
||||
<a href="#" className="filter-item">Culture</a>
|
||||
<a href="#" className="filter-item">Sales</a>
|
||||
<a href="#" className="filter-item">Collaboration</a>
|
||||
<a href="#" className="filter-item">Hiring</a>
|
||||
<a href="#" className="filter-item active">BBuddy product</a>
|
||||
<a href="#" className="filter-item">Customer Stories</a>
|
||||
<a href="#" className="filter-item">Coaching</a>
|
||||
{
|
||||
cats.map((cat, i)=>(
|
||||
<Link href={'category/'+cat.slug} className="filter-item">{cat.title}</Link>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b-news__result-list">
|
||||
<div className="b-inner">
|
||||
<div className="news-list">
|
||||
<a href="#" className="news-item">
|
||||
{data.map((item, i) => (
|
||||
<li key={'blog'+i} className="list-sidebar__item">
|
||||
<Link href={`${item.slug}`} className="news-item">
|
||||
<div className="news-item__image">
|
||||
<img className="" src="/images/news.png" alt="" />
|
||||
<img className="" src={item.listImage?.src} alt={item.listImage?.alt}/>
|
||||
</div>
|
||||
<div className="news-item__inner">
|
||||
<div className="">
|
||||
<div className="news-item__title">
|
||||
6 learnings from Shivpuri to Silicon Valley
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="news-item__badge">Leadership & Management</div>
|
||||
<div className="news-item__badge">{item.category}</div>
|
||||
<div className="news-item__text">
|
||||
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.
|
||||
{item.excerpt}
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src="/images/author.png" alt="" />
|
||||
<img className="" src={item.author.avatar.src} alt=""/>
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">Sonali Garg</div>
|
||||
<div className="news-item__info__date">February 6th, 2023</div>
|
||||
<div className="news-item__info__name">{item.author.name}</div>
|
||||
<div className="news-item__info__date">{item.createdAt}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
<img className="" src="/images/heart-outline.svg" alt=""/>
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
<img className="" src="/images/share-social.svg" alt=""/>
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" className="news-item">
|
||||
<div className="news-item__image">
|
||||
<img className="" src="/images/news.png" alt="" />
|
||||
</div>
|
||||
<div className="news-item__inner">
|
||||
<div className="">
|
||||
<div className="news-item__title">
|
||||
6 learnings from Shivpuri to Silicon Valley
|
||||
</div>
|
||||
<div className="news-item__badge">Leadership & Management</div>
|
||||
<div className="news-item__text">
|
||||
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.
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src="/images/author.png" alt="" />
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">Sonali Garg</div>
|
||||
<div className="news-item__info__date">February 6th, 2023</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" className="news-item">
|
||||
<div className="news-item__image">
|
||||
<img className="" src="/images/news.png" alt="" />
|
||||
</div>
|
||||
<div className="news-item__inner">
|
||||
<div className="">
|
||||
<div className="news-item__title">
|
||||
6 learnings from Shivpuri to Silicon Valley
|
||||
</div>
|
||||
<div className="news-item__badge">Leadership & Management</div>
|
||||
<div className="news-item__text">
|
||||
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.
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src="/images/author.png" alt="" />
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">Sonali Garg</div>
|
||||
<div className="news-item__info__date">February 6th, 2023</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" className="news-item">
|
||||
<div className="news-item__image">
|
||||
<img className="" src="/images/news.png" alt="" />
|
||||
</div>
|
||||
<div className="news-item__inner">
|
||||
<div className="">
|
||||
<div className="news-item__title">
|
||||
6 learnings from Shivpuri to Silicon Valley
|
||||
</div>
|
||||
<div className="news-item__badge">Leadership & Management</div>
|
||||
<div className="news-item__text">
|
||||
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.
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info">
|
||||
<div className="news-item__info__author">
|
||||
<img className="" src="/images/author.png" alt="" />
|
||||
<div className="news-item__info__author__inner">
|
||||
<div className="news-item__info__name">Sonali Garg</div>
|
||||
<div className="news-item__info__date">February 6th, 2023</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="news-item__info__counter">
|
||||
<div className="news-item__info__like">
|
||||
<img className="" src="/images/heart-outline.svg" alt="" />
|
||||
165
|
||||
</div>
|
||||
<div className="news-item__info__share">
|
||||
<img className="" src="/images/share-social.svg" alt="" />
|
||||
Share
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,16 @@
|
|||
import { parseContentfulContentImage } from './contentImage'
|
||||
import {Author, AuthorEntry} from "../../types/author";
|
||||
|
||||
|
||||
|
||||
|
||||
export function parseContentfulAuthor(authorEntry?: AuthorEntry<any, any>): Author | null {
|
||||
if (!authorEntry) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
name: authorEntry.fields.name || '',
|
||||
avatar: parseContentfulContentImage(authorEntry.fields.avatar),
|
||||
}
|
||||
}
|
|
@ -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<undefined, string>//Entry<BlogPostSkeleton, undefined, string>
|
||||
type widgetEnum = WidgetParagraph | WidgetMedia
|
||||
export type Widget = {
|
||||
widget: widgetEnum
|
||||
type: string
|
||||
}
|
||||
type WidgetEntry = WidgetMediaEntry<undefined, string> | WidgetParagraph<undefined, string>
|
||||
function parseWidgets(entries?: Array<WidgetEntry>): Array<Widget> | 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<BlogPost[]> {
|
||||
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<BlogPostSkeleton>(query)
|
||||
|
||||
return blogPostsResult.items.map((blogPostEntry) => parseContentfulBlogPost(blogPostEntry) as BlogPost)
|
||||
}
|
||||
|
||||
interface FetchBlogPostOptions {
|
||||
slug: string
|
||||
preview: boolean
|
||||
}
|
||||
export async function fetchBlogPost({ slug, preview }: FetchBlogPostOptions): Promise<BlogPost | null> {
|
||||
const contentful = contentfulClient({ preview })
|
||||
|
||||
const blogPostsResult = await contentful.getEntries<BlogPostSkeleton>({
|
||||
content_type: 'blogPost',
|
||||
'fields.slug': slug,
|
||||
include: 2,
|
||||
})
|
||||
|
||||
return parseContentfulBlogPost(blogPostsResult.items[0])
|
||||
}
|
|
@ -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<undefined, string>
|
||||
|
||||
|
||||
|
||||
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<BlogPostCategory[]> {
|
||||
const contentful = contentfulClient({ preview })
|
||||
|
||||
const results = await contentful.getEntries<BlogPostCategorySkeleton>({
|
||||
content_type: 'blogPostCategory',
|
||||
order: ['fields.title'],
|
||||
})
|
||||
|
||||
return results.items.map((entry) => parseContentfulBlogPostCategory(entry) as BlogPostCategory)
|
||||
}
|
|
@ -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<undefined, string> | { 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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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<AuthorFields, 'author'>
|
||||
export type AuthorEntry<Modifiers extends ChainModifiers, Locales extends LocaleCode> = Entry<
|
||||
AuthorSkeleton,
|
||||
Modifiers,
|
||||
Locales
|
||||
>
|
|
@ -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<WidgetMediaSkeleton | WidgetParagraphSkeleton>
|
||||
}
|
||||
|
||||
export interface BlogPostFields extends BlogPostFields{
|
||||
body: Array<WidgetMediaSkeleton | WidgetParagraphSkeleton>
|
||||
}
|
||||
|
||||
export interface BlogPost {
|
||||
title: string
|
||||
slug: string
|
||||
excerpt: string
|
||||
listImage: ContentImage | null
|
||||
author: Author | null
|
||||
category: string
|
||||
createdAt: string
|
||||
body: Array<WidgetMedia | WidgetParagraph>
|
||||
}
|
||||
|
||||
export type BlogPostSkeleton = EntrySkeletonType<BlogPostFields, 'blogPost'>
|
||||
export type BlogPostEntry<Modifiers extends ChainModifiers, Locales extends LocaleCode> = Entry<
|
||||
BlogPostSkeleton,
|
||||
Modifiers,
|
||||
Locales
|
||||
>
|
|
@ -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<BlogPostCategoryFields, 'blogPostCategory'>
|
||||
export type BlogPostCategoryEntry<Modifiers extends ChainModifiers, Locales extends LocaleCode> = Entry<
|
||||
BlogPostCategorySkeleton,
|
||||
Modifiers,
|
||||
Locales
|
||||
>
|
|
@ -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<WidgetMediaFields, 'WidgetMedia'>
|
||||
export type WidgetMediaEntry<Modifiers extends ChainModifiers, Locales extends LocaleCode> = Entry<
|
||||
WidgetMediaSkeleton,
|
||||
Modifiers,
|
||||
Locales
|
||||
>
|
|
@ -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<WidgetParagraphFields, 'WidgetParagraph'>
|
||||
export type WidgetParagraphEntry<Modifiers extends ChainModifiers, Locales extends LocaleCode> = Entry<
|
||||
WidgetParagraphSkeleton,
|
||||
Modifiers,
|
||||
Locales
|
||||
>
|
Loading…
Reference in New Issue