Skip to content

Commit

Permalink
empoty states, search functionality, updated glossary cards
Browse files Browse the repository at this point in the history
  • Loading branch information
p6l-richard committed Oct 23, 2024
1 parent 74bea63 commit 7a066ec
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 437 deletions.
112 changes: 65 additions & 47 deletions apps/www/app/glossary/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { CTA } from "@/components/cta";
import { Frame } from "@/components/frame";
import { SearchInput } from "@/components/glossary/input";
import TermsNavigationDesktop from "@/components/glossary/terms-navigation-desktop";
import TermsNavigationMobile from "@/components/glossary/terms-navigation-mobile";

import TermsStepperMobile from "@/components/glossary/terms-stepper-mobile";
import { MDX } from "@/components/mdx-content";
import { TopLeftShiningLight, TopRightShiningLight } from "@/components/svg/background-shiny";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
Expand All @@ -18,6 +17,8 @@ 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) => ({
Expand Down Expand Up @@ -56,13 +57,35 @@ export function generateMetadata({
};
}

const terms = [
{ slug: "rest-api", title: "REST API" },
{ slug: "graphql", title: "GraphQL" },
{ slug: "oauth2", title: "OAuth 2.0" },
{ slug: "jwt", title: "JSON Web Tokens (JWT)" },
{ slug: "api-gateway", title: "API Gateway" },
{ slug: "rate-limiting", title: "Rate Limiting" },
{ slug: "websockets", title: "WebSockets" },
{ slug: "trpc", title: "TRPC" },
{ slug: "openapi", title: "OpenAPI Specification" },
{ slug: "api-versioning", title: "API Versioning" },
{ slug: "cors", title: "Cross-Origin Resource Sharing (CORS)" },
{ slug: "api-throttling", title: "API Throttling" },
{ slug: "microservices", title: "Microservices Architecture" },
{ slug: "api-security", title: "API Security" },
];

const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) => {
const term = allGlossaries.find((term) => term.slug === `${params.slug}`) as Glossary;
if (!term) {
notFound();
}
const author = authors[term.reviewer];

const relatedTerms: {
slug: string;
term: string;
tldr: string;
}[] = [];
return (
<>
<div className="container pt-48 mx-auto sm:overflow-hidden md:overflow-visible scroll-smooth">
Expand Down Expand Up @@ -138,14 +161,21 @@ const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) =>
<p className="w-full mb-4 font-semibold text-left blog-heading-gradient">
Find a term
</p>
<SearchInput
<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>
<TermsNavigationDesktop className="flex-grow hidden lg:block" />
<TermsNavigationMobile className="flex-grow lg:hidden" />
<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>
Expand Down Expand Up @@ -246,47 +276,35 @@ const GlossaryTermWrapper = async ({ params }: { params: { slug: string } }) =>
<div>
<h3 className="text-lg font-semibold text-white mb-4">Related Terms</h3>
<div className="flex flex-col gap-4">
{[
{
term: "API Keys",
tldr: "Unique identifiers for API access and authentication.",
slug: "api-keys",
},
{
term: "Authentication",
tldr: "Process of verifying the identity of users or systems.",
slug: "authentication",
},
{
term: "Rate Limiting",
tldr: "Controlling the number of requests a client can make to an API.",
slug: "rate-limiting",
},
].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>
))}
{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>
Expand Down
109 changes: 51 additions & 58 deletions apps/www/app/glossary/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,17 @@ import { ChangelogLight } from "@/components/svg/changelog";

import { PrimaryButton } from "@/components/button";
import { Container } from "@/components/container";
import { SearchInput } from "@/components/glossary/input";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Label } from "@/components/ui/label";
import { FilterableCommand } from "@/components/glossary/search";
import { MeteorLinesAngular } from "@/components/ui/meteorLines";
import { Separator } from "@/components/ui/separator";
import { ArrowRight, LogIn, VenetianMask } from "lucide-react";
import { ArrowRight, LogIn } from "lucide-react";
import Link from "next/link";
import { Fragment } from "react";
import { categories, terms } from "./data";
import { allGlossaries, type Glossary } from "@/.content-collections/generated";
import { Zap } from "lucide-react";

export function GlossaryClient() {
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");

const groupedTerms = terms.reduce(
const groupedTerms = allGlossaries.reduce(
(acc, term) => {
const firstLetter = term.title[0].toUpperCase();
if (!acc[firstLetter]) {
Expand All @@ -31,7 +23,7 @@ export function GlossaryClient() {
acc[firstLetter].push(term);
return acc;
},
{} as Record<string, typeof terms>,
{} as Record<string, Array<Glossary>>,
);

return (
Expand Down Expand Up @@ -127,34 +119,11 @@ export function GlossaryClient() {
<h2 className="w-full mb-4 font-semibold text-left blog-heading-gradient">
Find a term
</h2>
<SearchInput
<FilterableCommand
placeholder="Search"
className="rounded-lg mb-4 border-[.75px] border-white/20 lg:w-[232px]"
terms={allGlossaries}
/>
<Accordion type="multiple" className="space-y-2">
{categories.map((category) => (
<Fragment>
{/* {index !== 0 && <Separator className="my-2" orientation="horizontal" />} */}
<AccordionItem value={category.slug}>
<AccordionTrigger className="flex items-center w-full text-left py-2">
<span className="w-6 h-6 rounded-md bg-white/10 flex">{category.icon}</span>
<span className="w-full pl-4 text-sm text-left ">{category.title}</span>
</AccordionTrigger>
<AccordionContent>
<Separator className="my-2" orientation="horizontal" />
<Link
href="/glossary/api-security"
className="flex flex-row items-center px-2 py-1 space-x-3 h-10 space-y-0 duration-150 rounded-md bg-[rgba(255,255,255,0.05)] group hover:bg-[rgba(255,255,255,0.15)] mb-2"
>
<Label className="flex items-center justify-between w-full">
<span className="text-sm font-normal">Blabla</span>
</Label>
</Link>
</AccordionContent>
</AccordionItem>
</Fragment>
))}
</Accordion>
</div>
<div className="col-span-2">
<div className="justify-between flex border-b border-white/10 pb-8 mb-8">
Expand All @@ -176,43 +145,49 @@ export function GlossaryClient() {
<section key={letter} id={letter} className="mb-8 scroll-mt-32">
<h2 className="text-2xl font-semibold mb-4 grid-cols-1">{letter}</h2>
<div className="grid grid-cols-1 gap-8 auto-rows-fr xl:grid-cols-3 md:grid-cols-2 grid-col-1">
{letterTerms.map(({ slug, image, category, title, description }) => (
{letterTerms.map(({ slug, categories, takeaways, term, reviewer }) => (
<Link
key={slug}
href={`/glossary/${slug}`}
className="flex flex-col items-start justify-between h-full overflow-hidden duration-200 border rounded-xl border-white/10 hover:border-white/20"
>
<div className="relative w-full h-full">
<div className="flex items-center justify-center w-full h-full">
<VenetianMask className="w-8 h-8 text-white/60" />
<div className="p-4 rounded-md space-y-2 bg-gradient-to-br from-[rgb(22,22,22)] to-[rgb(0,0,0)] border-b border-white/10">
<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 ">{takeaways.tldr}</p>
</div>
</div>
</div>
<div className="flex flex-col justify-start w-full h-full p-4">
<div>
<div className="flex flex-row justify-start w-full h-full gap-3">
{category !== undefined ? (
<div className="px-2 py-1 text-xs rounded-md bg-[rgb(26,26,26)] text-white/60">
{category}
</div>
) : null}
{categories.length > 0
? categories.map((category) => (
<div
key={category.slug}
className="px-2 py-1 text-xs rounded-md bg-[rgb(26,26,26)] text-white/60"
>
{category}
</div>
))
: null}
</div>
</div>
<div className="flex flex-col items-end content-end justify-end w-full h-full">
<div className="w-full h-12 mt-6">
<h3 className="text-lg font-semibold leading-6 text-left text-white group-hover:text-gray-600 line-clamp-2">
{title}
{term}
</h3>
</div>
<div className="w-full h-12">
<p className="mt-4 mb-6 text-sm leading-6 text-left text-white/60 line-clamp-2">
{description}
</p>
</div>

<div className="flex flex-row w-full h-24 sm:mb-4 md:mb-0">
<div className="content-end justify-end">
{" "}
<ArrowRight className="text-white/40" />
<div className="flex flex-row w-full min-h-18 sm:mb-4 md:mb-0 items-end">
<div className="flex items-center justify-between w-full">
<p className="text-xs leading-6 text-left text-white">
{reviewer}
</p>
<ArrowRight strokeWidth={1.5} className="size-5 text-gray-400" />
</div>
</div>
</div>
Expand All @@ -230,3 +205,21 @@ export function GlossaryClient() {
</div>
);
}

// <Link href={`/glossary/${slug}`} key={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">{takeaways.tldr}</p>
// </div>
// </Frame>
// </CardHeader>
// <CardContent>
// <h4 className="text-md font-semibold text-white mb-2">{term}</h4>
// </CardContent>
// </Card>
// </Link>
4 changes: 4 additions & 0 deletions apps/www/app/glossary/data-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FileJson } from "lucide-react";
import { categories } from "./data";
// note this is a separate client-file to include the icons, so that we can load the typescript .ts file into our content-collection config
export const categoriesWithIcons = [...categories.map((c) => ({ ...c, icon: <FileJson /> }))] as const;
21 changes: 21 additions & 0 deletions apps/www/app/glossary/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from "zod";

// note that this doesn't include the react icons, so that we can load the typescript .ts file into our content-collection config
export const categories = [
{
slug: "api-specification",
title: "API Specification",
description:
"API & Web standards for defining data formats and interactions (e.g. OpenAPI, REST, HTTP Requests, etc.)",
},
] as const;


// Extract slug values to create a union type
type CategorySlug = typeof categories[number]['slug'];

// Create a Zod enum from the CategorySlug type
export const categoryEnum = z.enum(categories.map(c => c.slug) as [CategorySlug, ...Array<CategorySlug>]);

export type CategoryEnum = z.infer<typeof categoryEnum>;

Loading

0 comments on commit 7a066ec

Please sign in to comment.