From 36cfebc7030e7b622fdfd909f8496554359d8407 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 00:51:34 +0200 Subject: [PATCH 1/8] Draft a comparison page --- .../docs/src/app/gateway/comparison/page.tsx | 100 ++++++++++++++++++ .../index.tsx | 2 +- .../app/gateway/gateway-hero-decoration.tsx | 19 ++++ packages/web/docs/src/app/gateway/page.tsx | 54 ++++------ 4 files changed, 140 insertions(+), 35 deletions(-) create mode 100644 packages/web/docs/src/app/gateway/comparison/page.tsx create mode 100644 packages/web/docs/src/app/gateway/gateway-hero-decoration.tsx diff --git a/packages/web/docs/src/app/gateway/comparison/page.tsx b/packages/web/docs/src/app/gateway/comparison/page.tsx new file mode 100644 index 0000000000..a7be11e697 --- /dev/null +++ b/packages/web/docs/src/app/gateway/comparison/page.tsx @@ -0,0 +1,100 @@ +import { ReactElement } from 'react'; +import { Metadata } from 'next'; +import { + CallToAction, + GetYourAPIGameRightSection, + Hero, + HeroLogo, + HiveGatewayIcon, +} from '@theguild/components'; +import { CommunitySection } from '../../../components/community-section'; +import { CompanyTestimonialsSection } from '../../../components/company-testimonials'; +import { ErrorBoundary } from '../../../components/error-boundary'; +import { LandingPageContainer } from '../../../components/landing-page-container'; +import { TrustedBySection } from '../../../components/trusted-by-section'; +import { metadata as rootMetadata } from '../../layout'; +import { FederationCompatibleBenchmarksSection } from '../federation-compatible-benchmarks'; +import { GatewayHeroDecoration } from '../gateway-hero-decoration'; + +// TODO: [comparison].tsx - statically generate based on json tables + +const description = + 'See why teams choose a fully open-source gateway instead of other closed solutions'; + +export const metadata: Metadata = { + title: 'Hive Gateway vs Apollo Router', + description, + alternates: { + // to remove leading slash + canonical: '.', + }, + openGraph: rootMetadata.openGraph, +}; + +export default function ComparisonPage(): ReactElement { + const comparison = { + name: 'Apollo Router', + }; + + return ( + + + + + } + className="bg-beige-100 mx-4 max-sm:mt-2 md:mx-6" + heading={`Hive Gateway vs ${comparison.name}`} + checkmarks={['Fully open source', 'No vendor lock-in', 'Can be self-hosted!']} + text={description} + > + + Get Started + + + GitHub + + + + + + + + + + + + + + + + + + + + {/* todo: smaller Community-driven open-source section */} + + + + ); +} diff --git a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx index c39793a9fe..fe1bcc74f7 100644 --- a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx +++ b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx @@ -39,7 +39,7 @@ export function FederationCompatibleBenchmarksSection({
- +
diff --git a/packages/web/docs/src/app/gateway/gateway-hero-decoration.tsx b/packages/web/docs/src/app/gateway/gateway-hero-decoration.tsx new file mode 100644 index 0000000000..07ccba9caa --- /dev/null +++ b/packages/web/docs/src/app/gateway/gateway-hero-decoration.tsx @@ -0,0 +1,19 @@ +import { ReactNode } from 'react'; +import { DecorationIsolation, HiveGatewayIcon } from '@theguild/components'; + +export function GatewayHeroDecoration({ children }: { children: ReactNode }) { + return ( + + + + + {children} + + + ); +} diff --git a/packages/web/docs/src/app/gateway/page.tsx b/packages/web/docs/src/app/gateway/page.tsx index 96763df545..284768f18b 100644 --- a/packages/web/docs/src/app/gateway/page.tsx +++ b/packages/web/docs/src/app/gateway/page.tsx @@ -15,6 +15,7 @@ import { metadata as rootMetadata } from '../layout'; import { CloudNativeSection } from './cloud-native-section'; import { FederationCompatibleBenchmarksSection } from './federation-compatible-benchmarks'; import { GatewayFeatureTabs } from './gateway-feature-tabs'; +import { GatewayHeroDecoration } from './gateway-hero-decoration'; import GatewayLandingFAQ from './gateway-landing-faq.mdx'; import { LetsGetAdvancedSection } from './lets-get-advanced-section'; import { OrchestrateYourWay } from './orchestrate-your-way'; @@ -50,7 +51,25 @@ export default function HiveGatewayPage() { GitHub - + + + + + + + + + + + + @@ -75,36 +94,3 @@ export default function HiveGatewayPage() { ); } - -function GatewayHeroDecoration() { - return ( - - - - - - - - - - - - - - - - - ); -} From 3a4aac5bb60ed3e7729bd10dc5154f0a0e808993 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 01:29:53 +0200 Subject: [PATCH 2/8] Make the table look good --- .../docs/src/app/gateway/comparison/page.tsx | 48 +++++++++++++++++++ .../src/components/pricing/plans-table.tsx | 20 +------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/packages/web/docs/src/app/gateway/comparison/page.tsx b/packages/web/docs/src/app/gateway/comparison/page.tsx index a7be11e697..8f2fb96ebb 100644 --- a/packages/web/docs/src/app/gateway/comparison/page.tsx +++ b/packages/web/docs/src/app/gateway/comparison/page.tsx @@ -15,12 +15,35 @@ import { TrustedBySection } from '../../../components/trusted-by-section'; import { metadata as rootMetadata } from '../../layout'; import { FederationCompatibleBenchmarksSection } from '../federation-compatible-benchmarks'; import { GatewayHeroDecoration } from '../gateway-hero-decoration'; +import { ComparisonTable } from './comparison-table'; // TODO: [comparison].tsx - statically generate based on json tables const description = 'See why teams choose a fully open-source gateway instead of other closed solutions'; +const logos = { + apollo: ( + + + + + + + + + + + + ), +}; + export const metadata: Metadata = { title: 'Hive Gateway vs Apollo Router', description, @@ -83,6 +106,31 @@ export default function ComparisonPage(): ReactElement { + , + }, + ]} + sections={[ + { + title: 'Features Set 1', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + rows: [ + { feature: 'Feature 1', values: [false, true] }, + { feature: 'Feature 2', values: [false, true] }, + { feature: 'Feature 3', values: [false, true] }, + { feature: 'Feature 4', values: [true, false] }, + { feature: 'Feature ...', values: [true, false] }, + ], + }, + ]} + /> ); } - -interface TableSubheaderRowProps { - icon: ReactNode; - title: string; - description: ReactNode; -} -function TableSubheaderRow({ icon, title, description }: TableSubheaderRowProps) { - return ( - - - - ); -} From 06fdb402d433f56ad51a0fb0616b12f949dd37de Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 01:32:23 +0200 Subject: [PATCH 3/8] Add "." to "vs" --- .../gateway/comparison/comparison-table.tsx | 117 ++++++++++++++++++ .../docs/src/app/gateway/comparison/page.tsx | 4 +- .../pricing/table-subheader-row.tsx | 22 ++++ 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 packages/web/docs/src/app/gateway/comparison/comparison-table.tsx create mode 100644 packages/web/docs/src/components/pricing/table-subheader-row.tsx diff --git a/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx b/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx new file mode 100644 index 0000000000..99b103cc64 --- /dev/null +++ b/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx @@ -0,0 +1,117 @@ +'use client'; + +import { ReactNode } from 'react'; +import { cn, Heading, ComparisonTable as Table } from '@theguild/components'; +import { CheckmarkIcon, XIcon } from '../../../components/icons'; +import { NestedSticky } from '../../../components/nested-sticky'; +import { TableSubheaderRow } from '../../../components/pricing/table-subheader-row'; + +const NO = ; +const YES = ; + +export type ComparisonColumn = { + name: string; + icon?: ReactNode; +}; + +export type ComparisonSection = { + title: string; + description: ReactNode; + icon?: ReactNode; + rows: ComparisonRow[]; +}; + +export type ComparisonRow = { + feature: string; + values: (ReactNode | boolean)[]; +}; + +export type ComparisonTableProps = { + className?: string; + columns: ComparisonColumn[]; + sections: ComparisonSection[]; +}; + +export function ComparisonTable({ className, columns, sections }: ComparisonTableProps) { + return ( +
+ + Hive Gateway allows you to do so much more. On your own terms. + +

+ Part of the Hive ecosystem, Hive Gateway is a fully-fledged solution that you can easily + tailor to your needs. +

+ +
+ +
+
Compare features
+ {columns.map(column => ( +
+
+ {column.icon &&
{column.icon}
} +
{column.name}
+
+
+ ))} +
+
+ +
-
- {icon} - {title} -
-

{description}

-
+ + + + {columns.map((column, i) => ( + + ))} + + + + {sections.map((section, sectionIndex) => ( + <> + + {section.rows.map((row, rowIndex) => ( + + + {row.feature} + + {row.values.map((value, columnIndex) => ( + + {typeof value === 'boolean' ? (value ? YES : NO) : value} + + ))} + + ))} + + ))} + +
Compare features{column.name}
+
+ + ); +} + +function ComparisonTableCell({ children, className }: { children: ReactNode; className?: string }) { + return ( + &:last-child]:rounded-tr-3xl max-md:[.subheader+tr>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-tr-3xl [.subheader+tr>&]:border-t [.subheader+tr>&]:first:rounded-tl-3xl md:[tr:is(:has(+.subheader),:last-child)>&:last-child]:rounded-br-3xl max-md:[tr:is(:has(+.subheader),:last-child)>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-br-3xl [tr:is(:last-child,:has(+.subheader))>&]:first:rounded-bl-3xl', + className, + )} + > + {children} + + ); +} diff --git a/packages/web/docs/src/app/gateway/comparison/page.tsx b/packages/web/docs/src/app/gateway/comparison/page.tsx index 8f2fb96ebb..0577d6c3ff 100644 --- a/packages/web/docs/src/app/gateway/comparison/page.tsx +++ b/packages/web/docs/src/app/gateway/comparison/page.tsx @@ -45,7 +45,7 @@ const logos = { }; export const metadata: Metadata = { - title: 'Hive Gateway vs Apollo Router', + title: 'Hive Gateway vs. Apollo Router', description, alternates: { // to remove leading slash @@ -68,7 +68,7 @@ export default function ComparisonPage(): ReactElement { } className="bg-beige-100 mx-4 max-sm:mt-2 md:mx-6" - heading={`Hive Gateway vs ${comparison.name}`} + heading={`Hive Gateway vs. ${comparison.name}`} checkmarks={['Fully open source', 'No vendor lock-in', 'Can be self-hosted!']} text={description} > diff --git a/packages/web/docs/src/components/pricing/table-subheader-row.tsx b/packages/web/docs/src/components/pricing/table-subheader-row.tsx new file mode 100644 index 0000000000..ccc68b6e78 --- /dev/null +++ b/packages/web/docs/src/components/pricing/table-subheader-row.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { ReactNode } from 'react'; + +export interface TableSubheaderRowProps { + icon: ReactNode; + title: string; + description: ReactNode; +} +export function TableSubheaderRow({ icon, title, description }: TableSubheaderRowProps) { + return ( + + +
+ {icon} + {title} +
+

{description}

+ + + ); +} From de42471170cfd5a03a1cb1c4a7787fb1cf9599b3 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 22:35:44 +0200 Subject: [PATCH 4/8] Put the comparison in .json file --- .../[comparison]/apollo-router.json | 22 +++ .../gateway/comparison/[comparison]/page.tsx | 143 +++++++++++++++++ .../gateway/comparison/comparison-table.tsx | 10 +- .../docs/src/app/gateway/comparison/page.tsx | 148 ------------------ .../src/app/gateway/orchestrate-your-way.tsx | 28 ++-- .../web/docs/src/app/gateway/other-logos.tsx | 25 +++ packages/web/docs/src/app/layout.tsx | 1 + 7 files changed, 208 insertions(+), 169 deletions(-) create mode 100644 packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json create mode 100644 packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx delete mode 100644 packages/web/docs/src/app/gateway/comparison/page.tsx create mode 100644 packages/web/docs/src/app/gateway/other-logos.tsx diff --git a/packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json b/packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json new file mode 100644 index 0000000000..0e3a62158c --- /dev/null +++ b/packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json @@ -0,0 +1,22 @@ +{ + "name": "Apollo Router", + "logo": "apollo", + "sections": [ + { + "title": "Features Set 1", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "rows": [ + { "feature": "Feature 1", "values": [false, true] }, + { "feature": "Feature 2", "values": [false, true] }, + { "feature": "Feature 3", "values": [false, true] }, + { "feature": "Feature 4", "values": [true, false] }, + { "feature": "Feature ...", "values": [true, false] } + ] + }, + { + "title": "Features Set 2", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "rows": [{ "feature": "Feature 1", "values": [false, true] }] + } + ] +} diff --git a/packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx b/packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx new file mode 100644 index 0000000000..68520936aa --- /dev/null +++ b/packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx @@ -0,0 +1,143 @@ +import { readdir, readFile } from 'node:fs/promises'; +import path from 'node:path'; +import { Metadata } from 'next'; +import { + CallToAction, + GetYourAPIGameRightSection, + Hero, + HeroLogo, + HiveGatewayIcon, + NextPageProps, +} from '@theguild/components'; +import { CommunitySection } from '../../../../components/community-section'; +import { CompanyTestimonialsSection } from '../../../../components/company-testimonials'; +import { ErrorBoundary } from '../../../../components/error-boundary'; +import { LandingPageContainer } from '../../../../components/landing-page-container'; +import { TrustedBySection } from '../../../../components/trusted-by-section'; +import { metadata as rootMetadata } from '../../../layout'; +import { FederationCompatibleBenchmarksSection } from '../../federation-compatible-benchmarks'; +import { GatewayHeroDecoration } from '../../gateway-hero-decoration'; +import { otherLogos } from '../../other-logos'; +import { ComparisonSection, ComparisonTable } from '../comparison-table'; + +const DESCRIPTION = + 'See why teams choose a fully open-source gateway instead of other closed solutions'; + +export default async function ComparisonPage(props: NextPageProps<'comparison'>) { + const dirname = import.meta.url.split('/').slice(2, -1).join('/'); + const file = path.join(dirname, `${(await props.params).comparison}.json`); + + const comparison = JSON.parse( + await readFile(file, 'utf-8'), + ) as Comparison; /* we don't really need to parse this because it's a static build */ + + const Logo = otherLogos[comparison.logo as keyof typeof otherLogos]; + + return ( + + + + + } + className="bg-beige-100 mx-4 max-sm:mt-2 md:mx-6" + heading={`Hive Gateway vs. ${comparison.name}`} + checkmarks={['Fully open source', 'No vendor lock-in', 'Can be self-hosted!']} + text={DESCRIPTION} + > + + Get Started + + + GitHub + + + + + + + + + + + + + + + + , + }, + { + name: 'Hive Gateway', + icon: , + }, + ]} + sections={comparison.sections} + /> + + + + + {/* todo: smaller Community-driven open-source section */} + + + + ); +} + +export async function generateStaticParams() { + const dirname = import.meta.url.split('/').slice(2, -1).join('/'); + const dir = await readdir(dirname); + const jsonFiles = dir.filter(file => file.endsWith('.json')); + return jsonFiles.map(file => ({ comparison: file.replace('.json', '') })); +} + +export async function generateMetadata({ params }: NextPageProps<'comparison'>) { + const dirname = import.meta.url.split('/').slice(2, -1).join('/'); + const file = path.join(dirname, `${(await params).comparison}.json`); + + const comparison = JSON.parse( + await readFile(file, 'utf-8'), + ) as Comparison; /* we don't really need to parse this because it's a static build */ + + return { + title: `Hive Gateway vs. ${comparison.name}`, + description: DESCRIPTION, + alternates: { + // to remove leading slash + canonical: '.', + }, + openGraph: rootMetadata.openGraph, + }; +} + +interface Comparison { + name: string; + logo: string; + sections: ComparisonSection[]; +} diff --git a/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx b/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx index 99b103cc64..a29f474781 100644 --- a/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx +++ b/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx @@ -47,14 +47,16 @@ export function ComparisonTable({ className, columns, sections }: ComparisonTabl
-
Compare features
+
+ Compare features +
{columns.map(column => (
{column.icon &&
{column.icon}
} -
{column.name}
+
{column.name}
))} @@ -107,7 +109,7 @@ function ComparisonTableCell({ children, className }: { children: ReactNode; cla return ( &:last-child]:rounded-tr-3xl max-md:[.subheader+tr>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-tr-3xl [.subheader+tr>&]:border-t [.subheader+tr>&]:first:rounded-tl-3xl md:[tr:is(:has(+.subheader),:last-child)>&:last-child]:rounded-br-3xl max-md:[tr:is(:has(+.subheader),:last-child)>&:not(:first-child,:has(+td[aria-hidden=false]))]:rounded-br-3xl [tr:is(:last-child,:has(+.subheader))>&]:first:rounded-bl-3xl', + 'border-beige-400 border-b border-r p-4 first:border-l first:font-medium max-sm:min-w-[97px] max-sm:text-sm max-sm:first:w-full sm:w-1/3 sm:py-6 [&:not(:first-child)]:border-l-0 [&:not(:first-child)]:text-center [&:not(:first-child)]:text-sm [&:not(:first-child)]:text-green-800 [.subheader+tr>&:last-child]:rounded-tr-3xl [.subheader+tr>&]:border-t [.subheader+tr>&]:first:rounded-tl-3xl [tr:is(:has(+.subheader),:last-child)>&:last-child]:rounded-br-3xl [tr:is(:last-child,:has(+.subheader))>&]:first:rounded-bl-3xl', className, )} > diff --git a/packages/web/docs/src/app/gateway/comparison/page.tsx b/packages/web/docs/src/app/gateway/comparison/page.tsx deleted file mode 100644 index 0577d6c3ff..0000000000 --- a/packages/web/docs/src/app/gateway/comparison/page.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { ReactElement } from 'react'; -import { Metadata } from 'next'; -import { - CallToAction, - GetYourAPIGameRightSection, - Hero, - HeroLogo, - HiveGatewayIcon, -} from '@theguild/components'; -import { CommunitySection } from '../../../components/community-section'; -import { CompanyTestimonialsSection } from '../../../components/company-testimonials'; -import { ErrorBoundary } from '../../../components/error-boundary'; -import { LandingPageContainer } from '../../../components/landing-page-container'; -import { TrustedBySection } from '../../../components/trusted-by-section'; -import { metadata as rootMetadata } from '../../layout'; -import { FederationCompatibleBenchmarksSection } from '../federation-compatible-benchmarks'; -import { GatewayHeroDecoration } from '../gateway-hero-decoration'; -import { ComparisonTable } from './comparison-table'; - -// TODO: [comparison].tsx - statically generate based on json tables - -const description = - 'See why teams choose a fully open-source gateway instead of other closed solutions'; - -const logos = { - apollo: ( - - - - - - - - - - - - ), -}; - -export const metadata: Metadata = { - title: 'Hive Gateway vs. Apollo Router', - description, - alternates: { - // to remove leading slash - canonical: '.', - }, - openGraph: rootMetadata.openGraph, -}; - -export default function ComparisonPage(): ReactElement { - const comparison = { - name: 'Apollo Router', - }; - - return ( - - - - - } - className="bg-beige-100 mx-4 max-sm:mt-2 md:mx-6" - heading={`Hive Gateway vs. ${comparison.name}`} - checkmarks={['Fully open source', 'No vendor lock-in', 'Can be self-hosted!']} - text={description} - > - - Get Started - - - GitHub - - - - - - - - - - - - - - - - , - }, - ]} - sections={[ - { - title: 'Features Set 1', - description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - rows: [ - { feature: 'Feature 1', values: [false, true] }, - { feature: 'Feature 2', values: [false, true] }, - { feature: 'Feature 3', values: [false, true] }, - { feature: 'Feature 4', values: [true, false] }, - { feature: 'Feature ...', values: [true, false] }, - ], - }, - ]} - /> - - - - - {/* todo: smaller Community-driven open-source section */} - - - - ); -} diff --git a/packages/web/docs/src/app/gateway/orchestrate-your-way.tsx b/packages/web/docs/src/app/gateway/orchestrate-your-way.tsx index 5a572a5ade..636e7e8957 100644 --- a/packages/web/docs/src/app/gateway/orchestrate-your-way.tsx +++ b/packages/web/docs/src/app/gateway/orchestrate-your-way.tsx @@ -1,5 +1,6 @@ import { ReactNode } from 'react'; import { Anchor, Heading, InfoCard } from '@theguild/components'; +import { ApolloLogo } from './other-logos'; export function OrchestrateYourWay({ className, ...rest }: React.HTMLAttributes) { return ( @@ -39,8 +40,16 @@ export function OrchestrateYourWay({ className, ...rest }: React.HTMLAttributes<
    - } text="Apollo Federation V1" /> - } text="Apollo Federation V2" /> + } + text="Apollo Federation V1" + /> + } + text="Apollo Federation V2" + /> } @@ -97,21 +106,6 @@ function LinkCard({ href, text, logo }: { href: string; text: ReactNode; logo: R ); } -function ApolloLogo() { - return ( - - - - - ); -} - function StitchingLogo() { return ( diff --git a/packages/web/docs/src/app/gateway/other-logos.tsx b/packages/web/docs/src/app/gateway/other-logos.tsx new file mode 100644 index 0000000000..d34e32b531 --- /dev/null +++ b/packages/web/docs/src/app/gateway/other-logos.tsx @@ -0,0 +1,25 @@ +export function ApolloLogo(props: React.SVGProps) { + return ( + + + + + ); +} + +export const otherLogos = { + apollo: ApolloLogo, +}; diff --git a/packages/web/docs/src/app/layout.tsx b/packages/web/docs/src/app/layout.tsx index f6ae1f45a3..d759eecb6f 100644 --- a/packages/web/docs/src/app/layout.tsx +++ b/packages/web/docs/src/app/layout.tsx @@ -68,6 +68,7 @@ export default async function HiveDocsLayout({ children }: { children: ReactNode '/ecosystem', '/partners', '/gateway', + '/gateway/comparison/apollo-router', ]; return ( From 3bd84c4233a9c06e3e493f3378479c58d061bce6 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 22:47:17 +0200 Subject: [PATCH 5/8] Move to the path pattern Saihaj proposed in Figma --- .../gateway}/[comparison]/apollo-router.json | 0 .../gateway}/[comparison]/page.tsx | 19 +++++++++---------- .../gateway}/comparison-table.tsx | 0 packages/web/docs/src/app/layout.tsx | 3 ++- 4 files changed, 11 insertions(+), 11 deletions(-) rename packages/web/docs/src/app/{gateway/comparison => compare/gateway}/[comparison]/apollo-router.json (100%) rename packages/web/docs/src/app/{gateway/comparison => compare/gateway}/[comparison]/page.tsx (86%) rename packages/web/docs/src/app/{gateway/comparison => compare/gateway}/comparison-table.tsx (100%) diff --git a/packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json b/packages/web/docs/src/app/compare/gateway/[comparison]/apollo-router.json similarity index 100% rename from packages/web/docs/src/app/gateway/comparison/[comparison]/apollo-router.json rename to packages/web/docs/src/app/compare/gateway/[comparison]/apollo-router.json diff --git a/packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx similarity index 86% rename from packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx rename to packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx index 68520936aa..e6c8e7ba59 100644 --- a/packages/web/docs/src/app/gateway/comparison/[comparison]/page.tsx +++ b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx @@ -1,6 +1,10 @@ import { readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; -import { Metadata } from 'next'; +import { CommunitySection } from '#components/community-section'; +import { CompanyTestimonialsSection } from '#components/company-testimonials'; +import { ErrorBoundary } from '#components/error-boundary'; +import { LandingPageContainer } from '#components/landing-page-container'; +import { TrustedBySection } from '#components/trusted-by-section'; import { CallToAction, GetYourAPIGameRightSection, @@ -9,15 +13,10 @@ import { HiveGatewayIcon, NextPageProps, } from '@theguild/components'; -import { CommunitySection } from '../../../../components/community-section'; -import { CompanyTestimonialsSection } from '../../../../components/company-testimonials'; -import { ErrorBoundary } from '../../../../components/error-boundary'; -import { LandingPageContainer } from '../../../../components/landing-page-container'; -import { TrustedBySection } from '../../../../components/trusted-by-section'; +import { FederationCompatibleBenchmarksSection } from '../../../gateway/federation-compatible-benchmarks'; +import { GatewayHeroDecoration } from '../../../gateway/gateway-hero-decoration'; +import { otherLogos } from '../../../gateway/other-logos'; import { metadata as rootMetadata } from '../../../layout'; -import { FederationCompatibleBenchmarksSection } from '../../federation-compatible-benchmarks'; -import { GatewayHeroDecoration } from '../../gateway-hero-decoration'; -import { otherLogos } from '../../other-logos'; import { ComparisonSection, ComparisonTable } from '../comparison-table'; const DESCRIPTION = @@ -132,7 +131,7 @@ export async function generateMetadata({ params }: NextPageProps<'comparison'>) // to remove leading slash canonical: '.', }, - openGraph: rootMetadata.openGraph, + openGraph: rootMetadata!.openGraph, }; } diff --git a/packages/web/docs/src/app/gateway/comparison/comparison-table.tsx b/packages/web/docs/src/app/compare/gateway/comparison-table.tsx similarity index 100% rename from packages/web/docs/src/app/gateway/comparison/comparison-table.tsx rename to packages/web/docs/src/app/compare/gateway/comparison-table.tsx diff --git a/packages/web/docs/src/app/layout.tsx b/packages/web/docs/src/app/layout.tsx index d759eecb6f..121a728750 100644 --- a/packages/web/docs/src/app/layout.tsx +++ b/packages/web/docs/src/app/layout.tsx @@ -21,6 +21,7 @@ import '../easing-functions.css'; import '../mermaid.css'; import { NarrowPages } from './narrow-pages'; + export const metadata = getDefaultMetadata({ productName: PRODUCTS.HIVE.name, websiteName: 'Hive', @@ -68,7 +69,7 @@ export default async function HiveDocsLayout({ children }: { children: ReactNode '/ecosystem', '/partners', '/gateway', - '/gateway/comparison/apollo-router', + '/compare/gateway/apollo-router', ]; return ( From 13b4a031206a569465a5412f083c3cb6a35ccddc Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 23:07:49 +0200 Subject: [PATCH 6/8] Format --- packages/web/docs/src/app/gateway/page.tsx | 1 - packages/web/docs/src/app/layout.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/web/docs/src/app/gateway/page.tsx b/packages/web/docs/src/app/gateway/page.tsx index 284768f18b..c823ec6d78 100644 --- a/packages/web/docs/src/app/gateway/page.tsx +++ b/packages/web/docs/src/app/gateway/page.tsx @@ -1,7 +1,6 @@ import { Metadata } from 'next'; import { CallToAction, - DecorationIsolation, ExploreMainProductCards, FrequentlyAskedQuestions, Hero, diff --git a/packages/web/docs/src/app/layout.tsx b/packages/web/docs/src/app/layout.tsx index 121a728750..6d4f91e201 100644 --- a/packages/web/docs/src/app/layout.tsx +++ b/packages/web/docs/src/app/layout.tsx @@ -21,7 +21,6 @@ import '../easing-functions.css'; import '../mermaid.css'; import { NarrowPages } from './narrow-pages'; - export const metadata = getDefaultMetadata({ productName: PRODUCTS.HIVE.name, websiteName: 'Hive', From 078d8798502df0b3306278d05c93397b00c52f4d Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 23:40:02 +0200 Subject: [PATCH 7/8] Add missing React key --- .../app/compare/gateway/[comparison]/page.tsx | 17 ++++++++++------- .../app/compare/gateway/comparison-table.tsx | 9 ++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx index e6c8e7ba59..57f4cef5d8 100644 --- a/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx +++ b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx @@ -1,5 +1,5 @@ import { readdir, readFile } from 'node:fs/promises'; -import path from 'node:path'; +import { dirname, join } from 'node:path'; import { CommunitySection } from '#components/community-section'; import { CompanyTestimonialsSection } from '#components/company-testimonials'; import { ErrorBoundary } from '#components/error-boundary'; @@ -23,8 +23,10 @@ const DESCRIPTION = 'See why teams choose a fully open-source gateway instead of other closed solutions'; export default async function ComparisonPage(props: NextPageProps<'comparison'>) { - const dirname = import.meta.url.split('/').slice(2, -1).join('/'); - const file = path.join(dirname, `${(await props.params).comparison}.json`); + const file = join( + dirname(new URL(import.meta.url).pathname), + `${(await props.params).comparison}.json`, + ); const comparison = JSON.parse( await readFile(file, 'utf-8'), @@ -110,15 +112,16 @@ export default async function ComparisonPage(props: NextPageProps<'comparison'>) } export async function generateStaticParams() { - const dirname = import.meta.url.split('/').slice(2, -1).join('/'); - const dir = await readdir(dirname); + const dir = await readdir(dirname(new URL(import.meta.url).pathname)); const jsonFiles = dir.filter(file => file.endsWith('.json')); return jsonFiles.map(file => ({ comparison: file.replace('.json', '') })); } export async function generateMetadata({ params }: NextPageProps<'comparison'>) { - const dirname = import.meta.url.split('/').slice(2, -1).join('/'); - const file = path.join(dirname, `${(await params).comparison}.json`); + const file = join( + dirname(new URL(import.meta.url).pathname), + `${(await params).comparison}.json`, + ); const comparison = JSON.parse( await readFile(file, 'utf-8'), diff --git a/packages/web/docs/src/app/compare/gateway/comparison-table.tsx b/packages/web/docs/src/app/compare/gateway/comparison-table.tsx index a29f474781..16c3616b52 100644 --- a/packages/web/docs/src/app/compare/gateway/comparison-table.tsx +++ b/packages/web/docs/src/app/compare/gateway/comparison-table.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ReactNode } from 'react'; +import { Fragment, ReactNode } from 'react'; import { cn, Heading, ComparisonTable as Table } from '@theguild/components'; import { CheckmarkIcon, XIcon } from '../../../components/icons'; import { NestedSticky } from '../../../components/nested-sticky'; @@ -77,15 +77,14 @@ export function ComparisonTable({ className, columns, sections }: ComparisonTabl {sections.map((section, sectionIndex) => ( - <> + {section.rows.map((row, rowIndex) => ( -
    +
    {row.feature} @@ -96,7 +95,7 @@ export function ComparisonTable({ className, columns, sections }: ComparisonTabl ))}
    ))} - +
    ))}
    From 84fef1582aade5fbd756311d5c4c4f2bb6635ad4 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 8 Apr 2025 23:50:40 +0200 Subject: [PATCH 8/8] Replace URL-encoded characters in dirname --- .../app/compare/gateway/[comparison]/page.tsx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx index 57f4cef5d8..7a09ab71e1 100644 --- a/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx +++ b/packages/web/docs/src/app/compare/gateway/[comparison]/page.tsx @@ -19,17 +19,16 @@ import { otherLogos } from '../../../gateway/other-logos'; import { metadata as rootMetadata } from '../../../layout'; import { ComparisonSection, ComparisonTable } from '../comparison-table'; +const __dirname = dirname(new URL(import.meta.url).pathname) + .replace('%5B', '[') + .replace('%5D', ']'); + const DESCRIPTION = 'See why teams choose a fully open-source gateway instead of other closed solutions'; export default async function ComparisonPage(props: NextPageProps<'comparison'>) { - const file = join( - dirname(new URL(import.meta.url).pathname), - `${(await props.params).comparison}.json`, - ); - const comparison = JSON.parse( - await readFile(file, 'utf-8'), + await readFile(join(__dirname, `${(await props.params).comparison}.json`), 'utf-8'), ) as Comparison; /* we don't really need to parse this because it's a static build */ const Logo = otherLogos[comparison.logo as keyof typeof otherLogos]; @@ -112,16 +111,13 @@ export default async function ComparisonPage(props: NextPageProps<'comparison'>) } export async function generateStaticParams() { - const dir = await readdir(dirname(new URL(import.meta.url).pathname)); + const dir = await readdir(__dirname); const jsonFiles = dir.filter(file => file.endsWith('.json')); return jsonFiles.map(file => ({ comparison: file.replace('.json', '') })); } export async function generateMetadata({ params }: NextPageProps<'comparison'>) { - const file = join( - dirname(new URL(import.meta.url).pathname), - `${(await params).comparison}.json`, - ); + const file = join(__dirname, `${(await params).comparison}.json`); const comparison = JSON.parse( await readFile(file, 'utf-8'),