diff --git a/packages/web/docs/package.json b/packages/web/docs/package.json index 07afd6a97df..7f3d4d6da1b 100644 --- a/packages/web/docs/package.json +++ b/packages/web/docs/package.json @@ -16,7 +16,7 @@ "@radix-ui/react-icons": "1.3.2", "@radix-ui/react-tabs": "1.1.2", "@radix-ui/react-tooltip": "1.1.6", - "@theguild/components": "9.3.4", + "@theguild/components": "9.5.0", "date-fns": "4.1.0", "next": "15.2.4", "react": "19.0.0", diff --git a/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx b/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx index 40d0e5f34c3..ce9771207e4 100644 --- a/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx +++ b/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx @@ -1,6 +1,6 @@ --- title: Hive Platform Achieves SOC-2 Type II Certification -tags: [security, cloud, hive, platform, compliance] +tags: [security, cloud, graphql-hive, platform, compliance] authors: dotan date: 2025-03-25 description: diff --git a/packages/web/docs/src/app/blog/(posts)/layout.tsx b/packages/web/docs/src/app/blog/(posts)/layout.tsx index ff4c4e49795..b7ff0c6576e 100644 --- a/packages/web/docs/src/app/blog/(posts)/layout.tsx +++ b/packages/web/docs/src/app/blog/(posts)/layout.tsx @@ -1,7 +1,8 @@ -import { cn, HiveLayoutConfig } from '@theguild/components'; +import { cn, GetYourAPIGameRightSection, HiveLayoutConfig } from '@theguild/components'; import { LandingPageContainer } from '../../../components/landing-page-container'; -import '../../hive-prose-styles.css'; import { BlogPostHeader } from '../components/blog-post-layout/blog-post-header'; +import { SimilarPosts } from '../components/blog-post-layout/similar-posts'; +import '../../hive-prose-styles.css'; const MAIN_CONTENT = 'main-content'; @@ -13,11 +14,13 @@ export default function BlogPostLayout({ children }: { children: React.ReactNode
div>:first-child]:hidden [&_main>p:first-of-type]:text-2xl/8', + 'mx-auto flex *:!pl-2 sm:*:!ml-auto sm:*:!pl-0 [&>div>:first-child]:hidden [&_main>p:first-of-type]:text-xl/8 md:[&_main>p:first-of-type]:text-2xl/8', )} > {children}
+ + ); } diff --git a/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx index c118ba51811..7b1e1dc6566 100644 --- a/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx +++ b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx @@ -20,7 +20,7 @@ export function BlogPostHeader({ className }: { className?: string }) { {image && }
{title} @@ -47,7 +47,7 @@ export function BlogPostHeader({ className }: { className?: string }) { authors: Array.isArray(authors) ? authors : [authors], date, }} - className="mt-4" + className="mt-4 max-sm:justify-start" />
diff --git a/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/client.tsx b/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/client.tsx new file mode 100644 index 00000000000..5869f5bdd83 --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/client.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { useFrontmatter } from '#components/use-frontmatter'; +import { BlogFrontmatter, BlogPostFile } from '../../../blog-types'; +import { BlogCard } from '../../blog-card'; + +export function SimilarPostsClient({ sortedPosts }: { sortedPosts: BlogPostFile[] }) { + const { frontmatter } = useFrontmatter(); + const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [frontmatter.tags]; + + const postsToShow = []; + const intersectedTags: string[] = []; + + for (let i = 0; i < sortedPosts.length && postsToShow.length < 2; i++) { + const post = sortedPosts[i]; + if (post.frontMatter.title !== frontmatter.title) { + const postTags = Array.isArray(post.frontMatter.tags) + ? post.frontMatter.tags + : [post.frontMatter.tags]; + + const tagsInCommon = postTags.filter(tag => tags.includes(tag)); + if (tagsInCommon.length > 0) { + postsToShow.push(post); + intersectedTags.push(tagsInCommon[0]); + } + } + } + + if (postsToShow.length === 0) { + // We need the CSS in the server component to know we didn't find any similar posts. + return ; + } + + return postsToShow.map((post, i) => ( + + )); +} diff --git a/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/index.tsx b/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/index.tsx new file mode 100644 index 00000000000..e82ee60c78b --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-post-layout/similar-posts/index.tsx @@ -0,0 +1,43 @@ +import { ArrowIcon, cn, Heading } from '@theguild/components'; +import { getPageMap } from '@theguild/components/server'; +import { isBlogPost } from '../../../blog-types'; +import { SimilarPostsClient } from './client'; + +export async function SimilarPosts({ className }: { className?: string }) { + // We're overfetching all posts here, because Nextra doesn't allow us + // to get the current post frontmatter on the server, and we need it to + // filter the similar posts. This could be worked around by moving the + // code to a remark/rehype plugin layer, but it seems like an overkill. + // We can optimize this later. + const [_meta, _indexPage, ...pageMap] = await getPageMap('/blog'); + const sortedPosts = pageMap + .filter(isBlogPost) + .sort((a, b) => new Date(b.frontMatter.date).getTime() - new Date(a.frontMatter.date).getTime()) + .slice( + 0, + // This is an assumption that 100 posts is enough to find similar ones. + // Worst case we'll not render the section if there's no similar post. + 100, + ); + + return ( +
+
+
+ + Explore + + +
+

Dive deeper into related topics.

+
+ +
+ ); +} diff --git a/packages/web/docs/src/app/gateway/lets-get-advanced-section.tsx b/packages/web/docs/src/app/gateway/lets-get-advanced-section.tsx index 5f543aba425..1957c5d8a8d 100644 --- a/packages/web/docs/src/app/gateway/lets-get-advanced-section.tsx +++ b/packages/web/docs/src/app/gateway/lets-get-advanced-section.tsx @@ -15,6 +15,7 @@ export function LetsGetAdvancedSection({ className, ...rest }: React.HTMLAttribu
    } heading="GraphQL Subscriptions" // moved to the last place, because it's the shortest @@ -33,7 +34,7 @@ export function LetsGetAdvancedSection({ className, ...rest }: React.HTMLAttribu Documentation - } heading="@defer and @stream Support"> + } heading="@defer and @stream Support"> Allows more efficient data loading patterns, improving user interface responsiveness and system performance.
    @@ -45,7 +46,7 @@ export function LetsGetAdvancedSection({ className, ...rest }: React.HTMLAttribu Documentation - } heading="Request Batching"> + } heading="Request Batching"> Reduces network overhead by enabling multiple GraphQL operations in a single HTTP request, enhancing data retrieval efficiency.
    @@ -57,7 +58,7 @@ export function LetsGetAdvancedSection({ className, ...rest }: React.HTMLAttribu Documentation - } heading="Demand Control"> + } heading="Demand Control"> Facilitates efficient management of API resources by setting limits on query complexity and execution depth, tailored for high-demand cloud environments.
    diff --git a/packages/web/docs/src/app/layout.tsx b/packages/web/docs/src/app/layout.tsx index e0b19678c69..f6ae1f45a33 100644 --- a/packages/web/docs/src/app/layout.tsx +++ b/packages/web/docs/src/app/layout.tsx @@ -97,11 +97,7 @@ export default async function HiveDocsLayout({ children }: { children: ReactNode icon: , children: 'Case Studies', }, - { - href: 'https://the-guild.dev/graphql/hive/blog', - icon: , - children: 'Blog', - }, + { href: '/blog', icon: , children: 'Blog' }, { href: 'https://github.com/graphql-hive/console', icon: , diff --git a/packages/web/docs/src/components/lede.tsx b/packages/web/docs/src/components/lede.tsx index 04740b8641b..f7944abc16e 100644 --- a/packages/web/docs/src/components/lede.tsx +++ b/packages/web/docs/src/components/lede.tsx @@ -3,5 +3,5 @@ import { cn } from '@theguild/components'; export interface LedeProps extends React.HTMLAttributes {} export function Lede(props: LedeProps) { - return
    ; + return
    ; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 903ea48543e..b1fa2f45ba8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2037,8 +2037,8 @@ importers: specifier: 1.1.6 version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@theguild/components': - specifier: 9.3.4 - version: 9.3.4(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.2.4(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0)) + specifier: 9.5.0 + version: 9.5.0(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.2.4(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0)) date-fns: specifier: 4.1.0 version: 4.1.0 @@ -3512,6 +3512,7 @@ packages: '@fastify/vite@6.0.7': resolution: {integrity: sha512-+dRo9KUkvmbqdmBskG02SwigWl06Mwkw8SBDK1zTNH6vd4DyXbRvI7RmJEmBkLouSU81KTzy1+OzwHSffqSD6w==} + bundledDependencies: [] '@floating-ui/core@1.2.6': resolution: {integrity: sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==} @@ -7518,8 +7519,8 @@ packages: resolution: {integrity: sha512-kUiFyUQDiVdpeeL/qwOZAdDZrYFTwqppwNZDxOXcPSKeqGCZe8ajbbN64OAS7VlGMnXWxEEHcjhyFzB9gtHn5w==} hasBin: true - '@theguild/components@9.3.4': - resolution: {integrity: sha512-V0Q70chn0m7VimoAA8KDl5ghrcKjoJqzOTjU8C3k/sNbLJPa1rBSbpWyubWlqTlVGjCtmNsFaRDDbccETlZL8w==} + '@theguild/components@9.5.0': + resolution: {integrity: sha512-NNvaxymlwyX5SQKGSUMDeIZ0nfqoDgwak39sL79wiuEzUmwRU6WAe+QpSDPIC3529Iuf5lAMqi5x6UoPn5yStA==} peerDependencies: '@theguild/tailwind-config': ^0.6.2 next: ^13 || ^14 || ^15.0.0 @@ -23985,7 +23986,7 @@ snapshots: typescript: 4.9.5 yargs: 16.2.0 - '@theguild/components@9.3.4(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.2.4(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))': + '@theguild/components@9.5.0(@theguild/tailwind-config@0.6.3(postcss-import@16.1.0(postcss@8.4.49))(postcss-lightningcss@1.0.1(postcss@8.4.49))(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.7.3))))(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(immer@10.1.1)(next@15.2.4(@babel/core@7.22.9)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.3)(use-sync-external-store@1.2.0(react@19.0.0))': dependencies: '@giscus/react': 3.1.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@next/bundle-analyzer': 15.1.5