Skip to content

Commit

Permalink
feat(475): Redesign Landing Page for the Blog (#488)
Browse files Browse the repository at this point in the history
* feat(475): Redesign Landing Page for the Blog

* fix: changes

* chore: cleanup code

* mobile adjustments

* chore: prettier

* feat(475): Redesign Landing Page for the Blog

* fix: changes

* chore: cleanup code

* mobile adjustments

* chore: prettier

* changes

* changes

* changes

* changes

* changesx

* changes

* type annotate docusaurus config

* changes

* fix: minor changes

---------

Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
  • Loading branch information
neo773 and amitksingh1490 authored Oct 4, 2024
1 parent 7032eb6 commit 71b2ebb
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ yarn-error.log*
src/*/*.json
.vscode/
.gitpod.yml
cypress/screenshots
cypress/screenshots
9 changes: 5 additions & 4 deletions blog/bff-case-study-2024-08-30.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ hide_table_of_contents: true
slug: dream11-graphql-case-study
image: /images/blog/dream11-graphql-case-study.png
category: Case Study
featured: true
tags:
[
GraphQL,
Expand All @@ -13,14 +14,14 @@ tags:
Scalability,
]
authors:
- name: Amit Singh
title: Head of Growth and Strategy @ Tailcall | Ex Director of Engineering @ Dream11
url: https://github.com/amitksingh1490
image_url: https://avatars.githubusercontent.com/u/23661702?v=5
- name: Tushar Mathur
title: CEO @ Tailcall | | Ex VP of Engineering @ Dream11
url: https://github.com/tusharmath
image_url: https://avatars.githubusercontent.com/u/194482?v=4
- name: Amit Singh
title: Head of Growth and Strategy @ Tailcall | Ex Director of Engineering @ Dream11
url: https://github.com/amitksingh1490
image_url: https://avatars.githubusercontent.com/u/23661702?v=5
---

**Picture this:**
Expand Down
1 change: 1 addition & 0 deletions blog/tailcall-n+1-working-2024-08-04.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ authors:
description: A deep dive into the implementation details of the N+1 tracker
slug: tailcall-n+1-identification-algorithm
image: /images/blog/tailcall-n+1-identification-algorithm.png
featured: true
---

As a developer working with GraphQL, you're likely familiar with the concept of N+1 issues. If not, you're in for a treat - check out our [N+1 guide!](/docs/graphql-n-plus-one-problem-solved-tailcall)
Expand Down
23 changes: 14 additions & 9 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {themes as prismThemes} from "prism-react-renderer"
import type * as Preset from "@docusaurus/preset-classic"
import prismTheme from "./src/theme/CodeBlock/theme"
import type {Config} from "@docusaurus/types"

const title = "Tailcall"
const organization = "tailcallhq"
Expand Down Expand Up @@ -76,8 +77,12 @@ export default {
i18n: {
defaultLocale: "en",
locales: ["en"],
localeConfigs: {
en: {
label: "English",
},
},
},

presets: [
[
"classic",
Expand Down Expand Up @@ -239,7 +244,7 @@ export default {
routeBasePath: "blog",
include: ["**/*.{md,mdx}"],
exclude: ["**/_*.{js,jsx,ts,tsx,md,mdx}", "**/_*/**", "**/*.test.{js,jsx,ts,tsx}", "**/__tests__/**"],
postsPerPage: 10,
postsPerPage: "ALL",
blogListComponent: "@theme/BlogListPage",
blogPostComponent: "@theme/BlogPostPage",
blogTagsListComponent: "@theme/BlogTagsListPage",
Expand All @@ -266,16 +271,16 @@ export default {
sidebarPath: require.resolve("./graphql/sidebar.ts"),
},
],
async function myPlugin() {
async function tailwindPlugin() {
return {
name: "docusaurus-tailwindcss",
configurePostCss(postcssOptions: {[key: string]: any}) {
// Appends TailwindCSS and AutoPrefixer.
postcssOptions.plugins.push(require("tailwindcss"))
postcssOptions.plugins.push(require("autoprefixer"))
return postcssOptions
configurePostCss(postcssOptions) {
return {
...postcssOptions,
plugins: [...postcssOptions.plugins, require("tailwindcss"), require("autoprefixer")],
}
},
}
},
],
}
} satisfies Config
29 changes: 29 additions & 0 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@
@tailwind components;
@tailwind utilities;

@layer utilities {
.border-b-solid {
border-bottom-style: solid;
}
.border-t-solid {
border-top-style: solid;
}
.border-l-solid {
border-left-style: solid;
}
.border-r-solid {
border-right-style: solid;
}
.border-x-solid {
border-left-style: solid;
border-right-style: solid;
}
.border-y-solid {
border-top-style: solid;
border-bottom-style: solid;
}
}

:root {
/* Font and Dimension Tokens */
--ifm-font-family-base: "Space Grotesk", sans-serif;
Expand Down Expand Up @@ -698,3 +721,9 @@ span.token.tag.script.language-javascript {
height: 100%;
border: 0;
}

@media (min-width: 768px) {
.border-right {
border-right: 1px solid #e7e7e7;
}
}
15 changes: 15 additions & 0 deletions src/theme/BlogAuthor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react"

interface AuthorDisplayProps {
author: {
name?: string
imageURL?: string
}
}

export const BlogAuthor: React.FC<AuthorDisplayProps> = ({author}) => (
<div className="mt-4 flex items-center">
<img src={author.imageURL} alt={author.name} className="mr-2 size-6 rounded-full" />
<span className="font-medium text-black">{author.name}</span>
</div>
)
43 changes: 43 additions & 0 deletions src/theme/BlogCategories/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type {Props} from "@theme/BlogListPage"
import clsx from "clsx"
import React, {useMemo} from "react"

interface BlogCategoriesProps {
items: Props["items"]
onCategoryClick: (category: string) => void
activeCategory: string | null
}

export function BlogCategories({items, onCategoryClick, activeCategory}: BlogCategoriesProps): JSX.Element {
const categories = useMemo(() => {
const categoryCounts: Record<string, number> = {All: items.length}
items.map((item) => {
const category = item.content.metadata.frontMatter.category as string
if (typeof category === "string") {
categoryCounts[category] = (categoryCounts[category] || 0) + 1
}
})
return categoryCounts
}, [items])

return (
<div className="mb-4 flex items-center space-x-4 border-b border-gray-200">
{Object.entries(categories).map(([name, count]) => (
<div
aria-role="button"
aria-label={`${name} (${count})`}
key={name}
onClick={() => onCategoryClick(name === activeCategory ? "All" : name)}
className={clsx(
"text-sm cursor-pointer appearance-none border-none bg-transparent px-1 font-medium",
activeCategory === name
? "font-medium text-gray-900 border-b-solid border-b-2 border-black"
: "text-gray-500 hover:text-gray-700",
)}
>
{name}
</div>
))}
</div>
)
}
29 changes: 29 additions & 0 deletions src/theme/BlogFeaturedPosts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react"
import Link from "@docusaurus/Link"
import type {Props} from "@theme/BlogListPage"
import {BlogAuthor} from "../BlogAuthor"

function BlogFeaturedPosts({items}: {items: Props["items"]}): JSX.Element {
return (
<div className="space-y-4">
<h2 className="text-2xl mb-4 font-bold">Featured Posts</h2>
{items.map((post, index) => (
<Link
to={post.content.metadata.permalink}
key={index}
className="flex flex-col gap-4 text-black !no-underline hover:text-black"
>
<div className="flex items-center gap-2">
{post.content.metadata.authors[0] && <BlogAuthor author={post.content.metadata.authors[0]} />}
</div>
<div>
<div className="text-lg font-semibold">{post.content.metadata.title}</div>
<p className="text-sm mt-1 text-tailCall-light-600">{post.content.metadata.description}</p>
</div>
</Link>
))}
</div>
)
}

export default BlogFeaturedPosts
26 changes: 12 additions & 14 deletions src/theme/BlogLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import React from "react"
import React, {useEffect, useState} from "react"
import clsx from "clsx"
import Layout from "@theme/Layout"
import BlogRecentPosts from "../BlogRecentPosts"
import {isBlogPost} from "@site/src/utils"
import {useLocation} from "@docusaurus/router"
import type {Props} from "@theme/BlogLayout"

export default function BlogLayout(props: Props): JSX.Element {
const {sidebar, toc, children, ...layoutProps} = props
const hasSidebar = sidebar && sidebar.items.length > 0
const [isBlogPostPage, setIsBlogPostPage] = useState(false)
const location = useLocation()

useEffect(() => {
setIsBlogPostPage(isBlogPost())
}, [location.pathname])

return (
<Layout {...layoutProps}>
<div className="container my-8">
<div className="row justify-center">
<main
className={clsx("col", {
"col--7": hasSidebar,
"col--9 col--offset-1": !hasSidebar,
})}
>
{children}
</main>
{toc && <div className="col col--2">{toc}</div>}
</div>
<div className="container mx-auto my-8 flex flex-col items-center">
<div className={clsx("w-full", isBlogPostPage ? "lg:w-7/12" : "md:w-full")}>{children}</div>
{toc && <div className="w-full md:w-3/12">{toc}</div>}
</div>
<BlogRecentPosts sidebar={sidebar} />
</Layout>
Expand Down
77 changes: 77 additions & 0 deletions src/theme/BlogListPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from "react"
import clsx from "clsx"

import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import {PageMetadata, HtmlClassNameProvider, ThemeClassNames} from "@docusaurus/theme-common"
import BlogLayout from "@theme/BlogLayout"
import BlogListPaginator from "@theme/BlogListPaginator"
import SearchMetadata from "@theme/SearchMetadata"
import type {Props} from "@theme/BlogListPage"
import BlogListPageStructuredData from "@theme/BlogListPage/StructuredData"
import BlogFeaturedPosts from "../BlogFeaturedPosts"
import BlogPostList from "../BlogPostList"
import {BlogCategories} from "../BlogCategories"
import {useBlogPosts} from "@site/src/utils/hooks/useBlogPosts"
import {FrontMatter} from "@theme/BlogPostPage"

function BlogListPageMetadata(props: Props): JSX.Element {
const {metadata} = props
const {
siteConfig: {title: siteTitle},
} = useDocusaurusContext()
const {blogDescription, blogTitle, permalink} = metadata
const isBlogOnlyMode = permalink === "/"
const title = isBlogOnlyMode ? siteTitle : blogTitle
return (
<>
<PageMetadata title={title} description={blogDescription} />
<SearchMetadata tag="blog_posts_list" />
</>
)
}

function LoadMoreButton({handleLoadMore}: {handleLoadMore: () => void}): JSX.Element {
return (
<div className="flex justify-center">
<button
onClick={handleLoadMore}
className="mt-4 h-12 cursor-pointer rounded-lg border-2 border-solid border-tailCall-border-dark-100 bg-transparent px-4 py-2 font-space-grotesk text-title-tiny font-bold text-black"
>
Load more blogs
</button>
</div>
)
}

function BlogListPageContent({metadata, items, sidebar}: Props): JSX.Element {
const {activeCategory, visibleItems, filteredItems, handleCategoryClick, handleLoadMore} = useBlogPosts(items)
const featuredItems = items.filter((post) => (post.content.frontMatter as FrontMatter & {featured: boolean}).featured)

return (
<BlogLayout sidebar={sidebar}>
<div className="flex flex-col md:flex-row items-start w-full">
<div className={clsx("w-full md:w-9/12 md:pr-6", featuredItems.length == 0 ? "md:w-full" : "border-right")}>
<BlogCategories items={items} onCategoryClick={handleCategoryClick} activeCategory={activeCategory} />
<BlogPostList items={filteredItems.slice(0, visibleItems)} />
{visibleItems < filteredItems.length && <LoadMoreButton handleLoadMore={handleLoadMore} />}
<BlogListPaginator metadata={metadata} />
</div>
{featuredItems.length > 0 ? (
<div className="w-full md:w-3/12 hidden md:block md:pl-6">
<BlogFeaturedPosts items={featuredItems} />
</div>
) : null}
</div>
</BlogLayout>
)
}

export default function BlogListPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider className={clsx(ThemeClassNames.wrapper.blogPages, ThemeClassNames.page.blogListPage)}>
<BlogListPageMetadata {...props} />
<BlogListPageStructuredData {...props} />
<BlogListPageContent {...props} />
</HtmlClassNameProvider>
)
}
10 changes: 3 additions & 7 deletions src/theme/BlogPostItem/Header/Back/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import Link from "@docusaurus/Link"
import {ArrowLeft} from "lucide-react"
import React from "react"

export default function BlogBackButton(): JSX.Element {
return (
<div
className="flex items-center gap-2 my-8 cursor-pointer"
onClick={() => {
window.history.back()
}}
>
<Link to="/blog" className="flex items-center gap-2 my-8 cursor-pointer !no-underline" onClick={() => {}}>
<ArrowLeft size={24} color="black" />
<span className="text-content-small text-tailCall-light-600">Back to Blogs</span>
</div>
</Link>
)
}
Loading

0 comments on commit 71b2ebb

Please sign in to comment.