Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2edf3e1
v0 for glossary
p6l-richard Sep 12, 2024
9c15e50
add terms page /w basic terms navigation
p6l-richard Sep 13, 2024
fa310bc
`<TermsNavigationDesktop>` & `<TermsNavigationMobile>`
p6l-richard Sep 13, 2024
9977359
WIP: add desktop navigation
p6l-richard Oct 1, 2024
bfb49f5
feat(glossary): implement glossary page and related components
p6l-richard Oct 19, 2024
fdad2d6
Merge branch 'main' into richard/glossary
p6l-richard Oct 19, 2024
5f1dce3
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 19, 2024
edf246b
remove twitter image from glossary
p6l-richard Oct 21, 2024
2121207
Merge branch 'richard/glossary' of https://github.com/p6l-richard/unk…
p6l-richard Oct 21, 2024
dcc4b7d
Merge remote-tracking branch 'origin/main' into richard/glossary
p6l-richard Oct 21, 2024
06eb9bb
remove `api-design` entry
p6l-richard Oct 21, 2024
74bea63
fix build by removing opengraph image and the image from glossary ent…
p6l-richard Oct 21, 2024
7a066ec
empoty states, search functionality, updated glossary cards
p6l-richard Oct 23, 2024
9144569
remove `template` components in the glossary
p6l-richard Oct 23, 2024
c91fe55
Merge branch 'main' into richard/glossary
p6l-richard Oct 23, 2024
fd27677
Merge branch 'main' of https://github.com/unkeyed/unkey into richard/…
p6l-richard Oct 23, 2024
bb42d07
Merge branch 'richard/glossary' of https://github.com/p6l-richard/unk…
p6l-richard Oct 23, 2024
4d872bd
Merge branch 'main' of https://github.com/unkeyed/unkey into richard/…
p6l-richard Oct 23, 2024
967bd7c
Update apps/www/content/glossary/mime-types.mdx
p6l-richard Oct 23, 2024
312075b
clean up
p6l-richard Oct 23, 2024
3c25467
last change
p6l-richard Oct 23, 2024
5055dea
fix types
p6l-richard Oct 25, 2024
5817f3e
remove `reviewedby`
p6l-richard Oct 25, 2024
50536e2
`pnpm fmt`
p6l-richard Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions apps/www/app/glossary/[slug]/faq.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";

export function FAQ(props: {
epigraph?: string;
title: string;
description: string;
items: Array<{ question: string; answer: string }>;
}) {
return (
<>
<div className="text-center space-y-4 pb-6 mx-auto">
<h3 className="not-prose blog-heading-gradient text-left font-medium tracking-tight text-4xl">
{props.title}
</h3>
<p className="font-medium leading-7 not-prose text-white/70 lg:text-xl text-sm md:text-base py-6 max-w-sm md:max-w-md lg:max-w-xl xl:max-w-4xl text-left">
{props.description}
</p>
</div>
<div className="mx-auto md:max-w-[800px]">
<Accordion type="single" collapsible className="w-full">
{props.items.map((item) => (
<AccordionItem value={item.question} key={item.question}>
<AccordionTrigger className="justify-between space-x-2">
{item.question}
</AccordionTrigger>
<AccordionContent>{item.answer}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</>
);
}
283 changes: 283 additions & 0 deletions apps/www/app/glossary/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
import { CTA } from "@/components/cta";
import { Frame } from "@/components/frame";

import TermsStepperMobile from "@/components/glossary/terms-stepper-mobile";
import { MDX } from "@/components/mdx-content";
import { TopLeftShiningLight, TopRightShiningLight } from "@/components/svg/background-shiny";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { MeteorLinesAngular } from "@/components/ui/meteorLines";
import { cn } from "@/lib/utils";
import { allGlossaries } from "content-collections";
import { Zap } from "lucide-react";
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { FAQ } from "./faq";
import Takeaways from "./takeaways";
import TermsRolodexDesktop from "@/components/glossary/terms-rolodex-desktop";
import { FilterableCommand } from "@/components/glossary/search";

export const generateStaticParams = async () =>
allGlossaries.map((term) => ({
slug: term.slug,
}));

export function generateMetadata({
params,
}: {
params: { slug: string };
}): Metadata {
const term = allGlossaries.find((term) => term.slug === params.slug);
if (!term) {
notFound();
}
return {
title: `${term.title} | Unkey Glossary`,
description: term.description,
openGraph: {
title: `${term.title} | Unkey Glossary`,
description: term.description,
url: `https://unkey.com/glossary/${term.slug}`,
siteName: "unkey.com",
type: "article",
},
twitter: {
card: "summary_large_image",
title: `${term.title} | Unkey Glossary`,
description: term.description,
site: "@unkeydev",
creator: "@unkeydev",
},
icons: {
shortcut: "/images/landing/unkey.png",
},
};
}
Comment on lines +25 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance metadata generation with safer URL handling and path validation.

Consider these improvements for better robustness:

  1. Use URL constructor for safer URL handling
  2. Validate the icons path exists

Apply this diff:

   return {
     title: `${term.title} | Unkey Glossary`,
     description: term.description,
     openGraph: {
       title: `${term.title} | Unkey Glossary`,
       description: term.description,
-      url: `https://unkey.com/glossary/${term.slug}`,
+      url: new URL(`/glossary/${term.slug}`, 'https://unkey.com').toString(),
       siteName: "unkey.com",
       type: "article",
     },
     // ... rest of the metadata
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function generateMetadata({
params,
}: {
params: { slug: string };
}): Metadata {
const term = allGlossaries.find((term) => term.slug === params.slug);
if (!term) {
notFound();
}
return {
title: `${term.title} | Unkey Glossary`,
description: term.description,
openGraph: {
title: `${term.title} | Unkey Glossary`,
description: term.description,
url: `https://unkey.com/glossary/${term.slug}`,
siteName: "unkey.com",
type: "article",
},
twitter: {
card: "summary_large_image",
title: `${term.title} | Unkey Glossary`,
description: term.description,
site: "@unkeydev",
creator: "@unkeydev",
},
icons: {
shortcut: "/images/landing/unkey.png",
},
};
}
export function generateMetadata({
params,
}: {
params: { slug: string };
}): Metadata {
const term = allGlossaries.find((term) => term.slug === params.slug);
if (!term) {
notFound();
}
return {
title: `${term.title} | Unkey Glossary`,
description: term.description,
openGraph: {
title: `${term.title} | Unkey Glossary`,
description: term.description,
url: new URL(`/glossary/${term.slug}`, 'https://unkey.com').toString(),
siteName: "unkey.com",
type: "article",
},
twitter: {
card: "summary_large_image",
title: `${term.title} | Unkey Glossary`,
description: term.description,
site: "@unkeydev",
creator: "@unkeydev",
},
icons: {
shortcut: "/images/landing/unkey.png",
},
};
}


const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) => {
const term = allGlossaries.find((term) => term.slug === params.slug);
if (!term) {
Comment on lines +57 to +59
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using type assertion for better type safety.

The current implementation might return undefined if the term is not found. Consider using type assertion with find for better type safety.

Apply this diff:

- const term = allGlossaries.find((term) => term.slug === params.slug);
+ const term = allGlossaries.find((term) => term.slug === params.slug) ?? notFound();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) => {
const term = allGlossaries.find((term) => term.slug === params.slug);
if (!term) {
const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) => {
const term = allGlossaries.find((term) => term.slug === params.slug) ?? notFound();
if (!term) {

notFound();
}

const relatedTerms: {
slug: string;
term: string;
tldr: string;
}[] = [];
return (
<>
<div className="container pt-48 mx-auto sm:overflow-hidden md:overflow-visible scroll-smooth">
<div>
<TopLeftShiningLight className="hidden h-full -z-40 sm:block" />
</div>
<div className="w-full h-full overflow-hidden -z-20">
<MeteorLinesAngular
number={1}
xPos={0}
speed={10}
delay={5}
className="overflow-hidden"
/>
<MeteorLinesAngular
number={1}
xPos={0}
speed={10}
delay={0}
className="overflow-hidden"
/>
<MeteorLinesAngular
number={1}
xPos={100}
speed={10}
delay={7}
className="overflow-hidden md:hidden"
/>
<MeteorLinesAngular
number={1}
xPos={100}
speed={10}
delay={2}
className="overflow-hidden md:hidden"
/>
<MeteorLinesAngular
number={1}
xPos={200}
speed={10}
delay={7}
className="hidden overflow-hidden md:block"
/>
<MeteorLinesAngular
number={1}
xPos={200}
speed={10}
delay={2}
className="hidden overflow-hidden md:block"
/>
<MeteorLinesAngular
number={1}
xPos={400}
speed={10}
delay={5}
className="hidden overflow-hidden lg:block"
/>
<MeteorLinesAngular
number={1}
xPos={400}
speed={10}
delay={0}
className="hidden overflow-hidden lg:block"
/>
</div>
<div className="overflow-hidden -z-40">
<TopRightShiningLight />
</div>
<div className="w-full">
<div className="mb-24 grid grid-cols-1 gap-4 md:gap-8 pb-24 lg:grid-cols-[15rem_1fr] xl:grid-cols-[15rem_1fr_15rem]">
{/* Left Sidebar */}
<div className="block">
<div className="sticky top-24 flex flex-col">
<p className="w-full mb-4 font-semibold text-left blog-heading-gradient">
Find a term
</p>
<FilterableCommand
placeholder="Search"
className="rounded-lg mb-4 border-[.75px] border-white/20 lg:w-[232px]"
terms={allGlossaries}
/>
<div className="flex-grow">
<p className="w-full my-4 font-semibold text-left blog-heading-gradient">Terms</p>
<TermsRolodexDesktop
className="flex-grow hidden lg:block"
terms={allGlossaries.map((term) => ({ slug: term.slug, title: term.title }))}
/>
<TermsStepperMobile
className="flex-grow lg:hidden"
terms={allGlossaries.map((term) => ({ slug: term.slug, title: term.title }))}
/>
</div>
</div>
</div>
{/* Main Content */}
<div>
<div className="prose sm:prose-sm md:prose-md sm:mx-6">
<div className="flex items-center gap-5 p-0 m-0 mb-8 text-xl font-medium leading-8">
<Link href="/glossary">
<span className="text-transparent bg-gradient-to-r bg-clip-text from-white to-white/60">
Glossary
</span>
</Link>
<span className="text-white/40">/</span>
<span className="text-transparent capitalize bg-gradient-to-r bg-clip-text from-white to-white/60">
{term.term}
</span>
</div>
<h1 className="not-prose blog-heading-gradient text-left font-medium tracking-tight text-4xl">
{term.h1}
</h1>
<p className="mt-8 text-lg font-medium leading-8 not-prose text-white/60 lg:text-xl">
{term.intro}
</p>
</div>
<div className="mt-12 sm:mx-6">
<Takeaways takeaways={term.takeaways} term={term.term} />
</div>
<div className="mt-12 prose-sm md:prose-md text-white/60 sm:mx-6 prose-strong:text-white/90 prose-code:text-white/80 prose-code:bg-white/10 prose-code:px-2 prose-code:py-1 prose-code:border-white/20 prose-code:rounded-md prose-pre:p-0 prose-pre:m-0 prose-pre:leading-6">
<MDX code={term.mdx} />
</div>
<div className="mt-12 sm:mx-6">
<FAQ
items={[
{
// provide some FAQs for questions & answers about mime types:
question: "What is a mime type?",
answer:
"A mime type is a standard way to describe the format of a file. It is used to identify the type of data contained in a file, such as an image, a video, or a document. Mime types are essential for web browsers to correctly display and process different types of files.",
},
{
question: "What is the difference between a mime type and a file extension?",
answer:
"A mime type is a standard way to describe the format of a file. It is used to identify the type of data contained in a file, such as an image, a video, or a document. Mime types are essential for web browsers to correctly display and process different types of files. A file extension is a suffix added to a file name to indicate its type. It is used to help users identify the type of file and to help applications determine how to open or process the file.",
},
{
question: "Which mime types are supported by Unkey?",
answer:
"Unkey supports a wide range of mime types, including text, image, audio, video, and application-specific types. The full list of supported mime types can be found in the Unkey documentation.",
},
]}
title={`Questions & Answers about ${term.term}`}
description={`We answer common questions about ${term.term}.`}
epigraph="FAQ"
/>
</div>
</div>
{/* Right Sidebar */}
<div className="hidden xl:block">
<div className="sticky top-24 space-y-8">
{term.tableOfContents?.length !== 0 && (
<div className="not-prose">
<h3 className="text-lg font-semibold text-white mb-4">Contents</h3>
<ul className="space-y-2">
{term.tableOfContents.map((heading) => (
<li key={`#${heading?.slug}`}>
<Link
href={`#${heading?.slug}`}
className={cn("text-white/60 hover:text-white", {
"text-sm": heading?.level && heading.level > 2,
"ml-4": heading?.level && heading.level === 3,
"ml-8": heading?.level && heading.level === 4,
})}
>
{heading?.text}
</Link>
</li>
))}
</ul>
</div>
)}
{/* Related Blogs */}
<div>
<h3 className="text-lg font-semibold text-white mb-4">Related Terms</h3>
<div className="flex flex-col gap-4">
{relatedTerms.length > 0 ? (
relatedTerms.map((relatedTerm) => (
<Link
href={`/glossary/${relatedTerm.slug}`}
key={relatedTerm.slug}
className="block"
>
<Card className="w-full bg-white/5 shadow-[0_0_10px_rgba(255,255,255,0.1)] rounded-xl overflow-hidden relative border-white/20">
<CardHeader>
<Frame size="sm">
<div className="p-4 rounded-md space-y-2">
<h3 className="text-sm font-semibold flex items-center text-white">
<Zap className="mr-2 h-5 w-5" /> TL;DR
</h3>
<p className="text-sm text-white/80">{relatedTerm.tldr}</p>
</div>
</Frame>
</CardHeader>
<CardContent>
<h4 className="text-md font-semibold text-white mb-2">
{relatedTerm.term}
</h4>
</CardContent>
</Card>
</Link>
))
) : (
<p className="text-sm text-white/50">No related terms found.</p>
)}
</div>
</div>
</div>
</div>
</div>
<CTA />
</div>
</div>
</>
);
};

export default GlossaryTermWrapper;
Loading