Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate case studies #2861

Merged
merged 14 commits into from
Apr 24, 2024
259 changes: 259 additions & 0 deletions pages/casestudies/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import Link from 'next/link';
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
import { MDXRemote } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';

import type { ICaseStudy } from '@/types/post';
import { HeadingTypeStyle } from '@/types/typography/Heading';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import CaseTOC from '../../components/CaseTOC';
import GenericLayout from '../../components/layout/GenericLayout';
import { getMDXComponents } from '../../components/MDX';
import Heading from '../../components/typography/Heading';
import Paragraph from '../../components/typography/Paragraph';
import CaseStudiesList from '../../config/case-studies.json';
import { generateCaseStudyContent } from '../../utils/staticHelpers';

interface IndexProps {
casestudy: ICaseStudy;
challenges: MDXRemoteSerializeResult;
solution: MDXRemoteSerializeResult;
usecase: MDXRemoteSerializeResult;
architecture: MDXRemoteSerializeResult;
testing: MDXRemoteSerializeResult;
codegen: MDXRemoteSerializeResult;
schemaStorage: MDXRemoteSerializeResult;
registry: MDXRemoteSerializeResult;
versioning: MDXRemoteSerializeResult;
validation: MDXRemoteSerializeResult;
asyncapiStorage: MDXRemoteSerializeResult;
asyncapiEditing: MDXRemoteSerializeResult;
asyncapiExtensions: MDXRemoteSerializeResult;
asyncapiDocumentation: MDXRemoteSerializeResult;
asyncapiBindings: MDXRemoteSerializeResult;
asyncapiTools: MDXRemoteSerializeResult;
additionalResources: MDXRemoteSerializeResult;
}

const renderContent = (
content: any[],
allComponents: Record<string, React.ComponentType<any>>,
level: number
): JSX.Element[] => {
let typeStyle;

if (level === 0) {
typeStyle = HeadingTypeStyle.lg;
} else if (level === 1) {
typeStyle = HeadingTypeStyle.md;
} else {
typeStyle = HeadingTypeStyle.sm;
}

return content.map((item) => {
return (
<div className='mt-10' key={item.title}>
<Heading
typeStyle={typeStyle}
className='mt-8'
id={item.title
.replace(/<|>|"|\\|\/|=/gi, '')
.replace(/\s/gi, '-')
.toLowerCase()}
>
{item.title}
</Heading>
{item.content && (
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4'>
<MDXRemote {...item.content} components={allComponents} />
</Paragraph>
)}
{item.items && (
<div className='mt-4 items-center'>
<div className='flex flex-wrap gap-2'>
{item.items.map((subItem: string) => (
<span
key={subItem}
className='rounded-md border border-green-600 bg-green-100 p-1 text-center text-xs text-green-600 '
>
{subItem}
</span>
))}
</div>
{item.children && renderContent(item.children, allComponents, level + 1)}
</div>
)}
</div>
);
});
};

/**
* @description Fetches static props for the page.
* @param {object} params - Parameters object containing the id.
* @param {string} params.id - The id of the case study..
*/
export async function getStaticProps({ params }: { params: { id: string } }) {
const data = CaseStudiesList.filter((p: { id: string }) => p.id === params.id);

return {
props: {
casestudy: data[0],
challenges: await serialize(data[0].challenges),
solution: await serialize(data[0].solution),
usecase: await serialize(data[0].asyncapi.usecase),
architecture: await serialize(data[0].technical.architecture),
testing: await serialize(data[0].technical.testing),
codegen: await serialize(data[0].technical.codegen),
schemaStorage: await serialize(data[0].schemas.storage),
registry: await serialize(data[0].schemas.registry),
versioning: await serialize(data[0].schemas.versioning),
validation: await serialize(data[0].schemas.validation),
asyncapiStorage: await serialize(data[0].asyncapi.storage),
asyncapiEditing: await serialize(data[0].asyncapi.editing),
asyncapiExtensions: await serialize(data[0].asyncapi.extensions),
asyncapiDocumentation: await serialize(data[0].asyncapi.documentation),
asyncapiBindings: await serialize(data[0].asyncapi.bindings),
asyncapiTools: await serialize(data[0].asyncapi.tools),
additionalResources: await serialize(data[0].additionalResources)
}
};
}

/**
* @description Retrieves the static paths for the page.
*/
export async function getStaticPaths() {
const paths = CaseStudiesList.map((study: { id: string }) => ({
params: { id: study.id }
}));

return {
paths,
fallback: false
};
}

const Index: React.FC<IndexProps> = ({
casestudy,
challenges,
solution,
architecture,
testing,
codegen,
usecase,
schemaStorage,
registry,
versioning,
validation,
asyncapiStorage,
asyncapiEditing,
asyncapiExtensions,
asyncapiDocumentation,
asyncapiBindings,
asyncapiTools,
additionalResources
}) => {
const image = '/img/social/website-card.png';
const allComponents = getMDXComponents();
const contacts = casestudy.company.contact;

const content = generateCaseStudyContent({
challenges,
solution,
usecase,
architecture,
testing,
codegen,
schemaStorage,
registry,
versioning,
validation,
asyncapiStorage,
asyncapiEditing,
asyncapiExtensions,
asyncapiDocumentation,
asyncapiBindings,
asyncapiTools,
additionalResources,
casestudy
});

return (
<GenericLayout
title='AsyncAPI Case Studies'
description='The home for all case studies related to AsyncAPI.'
image={image}
hideBanner={true}
wide
>
<div className='max-w-screen lg:flex lg:flex-row-reverse lg:justify-between'>
<CaseTOC
toc={content}
cssBreakingPoint='lg'
className='sticky top-20 mt-4 max-h-screen overflow-y-auto bg-blue-100 p-4 lg:top-24 lg:-mr-14 lg:mt-2 lg:max-h-(screen-16) lg:min-w-[265px] lg:max-w-72 lg:flex-1 lg:border-l lg:border-gray-200 lg:bg-transparent lg:pb-8 lg:pt-0'
/>
<div className='case-study px-4 sm:px-6 lg:max-w-[812px] lg:flex-1 xl:max-w-5xl xl:px-0'>
<div className='mt-10 flex flex-col items-center justify-between md:mt-20 md:flex-row'>
<div className='w-full md:w-[65%]'>
<Heading typeStyle={HeadingTypeStyle.xl} className='countdown-text-gradient'>
{casestudy.company.name}
</Heading>
<div className='flex flex-wrap gap-1' id='Contacts'>
{contacts.map((item, index) => (
<div key={index}>
<Heading typeStyle={HeadingTypeStyle.bodyLg}>
<Link
href={item.link}
className='text-md font-medium leading-5 text-gray-900 hover:underline'
target='_blank'
>
<span>
{item.name}
{index !== contacts.length - 1 && ', '}
</span>
</Link>
</Heading>
</div>
))}
</div>
<div className='mt-4 flex flex-wrap gap-2'>
<span className='rounded-md border border-green-600 bg-green-100 p-1 text-center text-xs text-green-600 '>
Industry: {casestudy.company.industry}
</span>
<span className='rounded-md border border-green-600 bg-green-100 p-1 text-center text-xs text-green-600 '>
Customers: {casestudy.company.customers}
</span>
<span className='rounded-md border border-green-600 bg-green-100 p-1 text-center text-xs text-green-600 '>
Revenue: {casestudy.company.revenue}
</span>
</div>
<div className='mt-10'>
<Heading typeStyle={HeadingTypeStyle.bodyLg}>{casestudy.company.description}</Heading>
<Heading className='mt-10' typeStyle={HeadingTypeStyle.bodyLg}>
tl;dr just go and have a look at
<Link
href={`/${casestudy.asyncapi.fullExample}`}
className='ml-2 font-medium text-secondary-500 underline transition duration-300 ease-in-out hover:text-gray-800'
target='_blank'
>
full production-used AsyncAPI document
</Link>
</Heading>
</div>
</div>
<img
src={casestudy.company.logo}
alt={casestudy.company.name}
className='mx-auto mt-5 w-[250px] rounded-lg md:mt-0'
/>
</div>
{renderContent(content, allComponents, 0)}
</div>
</div>
</GenericLayout>
);
};

export default Index;
118 changes: 118 additions & 0 deletions pages/casestudies/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type { ICaseStudies } from '@/types/post';
import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import CaseStudyCard from '../../components/CaseStudyCard';
import GenericLayout from '../../components/layout/GenericLayout';
import Heading from '../../components/typography/Heading';
import Paragraph from '../../components/typography/Paragraph';
import TextLink from '../../components/typography/TextLink';
import AdoptersList from '../../config/adopters.json';
import CaseStudiesList from '../../config/case-studies.json';

interface Resource {
title: string;
link: string;
}

interface Adopter {
companyName: string;
useCase: string;
resources: Resource[];
}

/**
* @description Renders the Case Studies page.
*/
export default function Casestudies() {
const description: string = 'Learn about different case studies based on AsyncAPI spec and related tools.';
const image: string = '/img/social/case-studies.webp';
const title: string = 'Case Studies';

return (
<GenericLayout title={title} description={description} image={image} wide>
<div
className='relative mx-auto max-w-xl px-4 py-10 sm:px-6 lg:max-w-screen-xl lg:px-8'
data-testid='CaseStudy-main'
>
<div className='grid lg:grid-cols-9 lg:gap-8 lg:text-center'>
<div className='col-span-5 col-start-3'>
<Heading level={HeadingLevel.h1} typeStyle={HeadingTypeStyle.lg}>
{title}
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4 max-w-4xl'>
The best way to learn how to use AsyncAPI is not only through documentation that usually is focused on
recommendations and best practices. It is also good to confront with real-life case studies that explain
how people really use AsyncAPI and what are their flows.
</Paragraph>
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4 max-w-4xl'>
Feel free to submit your case study. We have a template for you. For more details
<TextLink href='https://github.com/asyncapi/website/blob/master/README.md#case-studies' target='_blank'>
read our FAQ
</TextLink>
.
</Paragraph>
</div>
</div>
<div data-testid='CaseStudy-card'>
<CaseStudyCard studies={CaseStudiesList as ICaseStudies} />
</div>

<div className='adopters'>
<div className='mt-8 grid lg:grid-cols-9 lg:gap-8 lg:text-center'>
<div className='col-span-5 col-start-3'>
<Heading level={HeadingLevel.h1} typeStyle={HeadingTypeStyle.lg}>
Adopters
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4 max-w-4xl'>
Check out how different companies use AsyncAPI and what problems they solve.
</Paragraph>
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4 max-w-4xl'>
Feel free to{' '}
<a className='underline' href='https://github.com/asyncapi/website/blob/master/config/adopters.yml'>
submit a pull request
</a>{' '}
with information about how your company uses AsyncAPI. We know that writing an official case study might
be time consuming and requires too much internal paper work. Let&apos;s make sure we can at least
capture a use case that is already a great learning information for the community.
</Paragraph>
</div>
</div>
</div>

<center>
<div className='overflow-x-auto'>
<table className='my-8 w-full max-w-full border-collapse border'>
<thead>
<tr>
<th className='border p-2 font-bold'>Company name</th>
<th className='border-2 p-2 font-bold'>Use Case</th>
<th className='border-2 p-2 font-bold'>Resources</th>
</tr>
</thead>
<tbody>
{AdoptersList.map((entry: Adopter, index: number) => (
<tr key={index} data-testid='Adopters'>
<td className='border-2 p-2'>{entry.companyName}</td>
<td className='border-2 p-2'>{entry.useCase}</td>
<td className='border-2 p-2'>
<ul>
{entry.resources.map((resource: Resource, resourceIndex: number) => (
<li className='p-2' key={resourceIndex}>
<a className='text-cyan-500 underline' href={resource.link}>
{resource.title}
</a>
</li>
))}
</ul>
</td>
</tr>
))}
</tbody>
</table>
</div>
</center>
</div>
</GenericLayout>
);
}
Loading