Skip to content

Commit

Permalink
feat: contentlayer (shadcn-ui#22)
Browse files Browse the repository at this point in the history
* feat: implement blog and doc sites

* fix: opacity for disabled menu items
  • Loading branch information
shadcn authored Nov 17, 2022
1 parent 8f13cb8 commit 9ad4315
Show file tree
Hide file tree
Showing 101 changed files with 5,280 additions and 949 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ yarn-error.log*
next-env.d.ts

.vscode
.contentlayer
4 changes: 1 addition & 3 deletions app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import "styles/globals.css"

interface AuthLayoutProps {
children: React.ReactNode
}

export default function RootLayout({ children }: AuthLayoutProps) {
export default function AuthLayout({ children }: AuthLayoutProps) {
return <div className="min-h-screen">{children}</div>
}
8 changes: 4 additions & 4 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Link from "next/link"

import { Icons } from "@/components/icons"
import { UserAuthForm } from "@/components/user-auth-form"
import { UserAuthForm } from "@/components/dashboard/user-auth-form"

export default function LoginPage() {
return (
<div className="container flex h-screen w-screen flex-col items-center justify-center">
<Link
href="/"
className="absolute top-4 left-4 inline-flex items-center justify-center rounded-lg border border-transparent bg-transparent py-2 px-3 text-center text-sm font-medium text-slate-900 hover:border-slate-100 hover:bg-slate-100 focus:z-10 focus:outline-none focus:ring-4 focus:ring-slate-200 md:top-8 md:left-8"
className="absolute top-4 left-4 inline-flex items-center justify-center rounded-lg border border-transparent bg-transparent py-2 px-3 text-center text-sm font-medium text-slate-900 hover:border-slate-200 hover:bg-slate-100 focus:z-10 focus:outline-none focus:ring-4 focus:ring-slate-200 md:top-8 md:left-8"
>
<>
<Icons.chevronLeft className="mr-2 h-4 w-4" />
Expand All @@ -19,12 +19,12 @@ export default function LoginPage() {
<div className="flex flex-col space-y-2 text-center">
<Icons.logo className="mx-auto h-6 w-6" />
<h1 className="text-2xl font-bold">Welcome back</h1>
<p className="text-sm text-slate-500">
<p className="text-sm text-slate-600">
Enter your email to sign in to your account
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-slate-500">
<p className="px-8 text-center text-sm text-slate-600">
<Link href="/register" className="underline hover:text-brand">
Don&apos;t have an account? Sign Up
</Link>
Expand Down
8 changes: 4 additions & 4 deletions app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Link from "next/link"

import { Icons } from "@/components/icons"
import { UserAuthForm } from "@/components/user-auth-form"
import { UserAuthForm } from "@/components/dashboard/user-auth-form"

export default function RegisterPage() {
return (
<div className="container grid h-screen w-screen flex-col items-center justify-center lg:max-w-none lg:grid-cols-2 lg:px-0">
<Link
href="/login"
className="absolute top-4 right-4 inline-flex items-center justify-center rounded-lg border border-transparent bg-transparent py-2 px-3 text-center text-sm font-medium text-slate-900 hover:border-slate-100 hover:bg-slate-100 focus:z-10 focus:outline-none focus:ring-4 focus:ring-slate-200 md:top-8 md:right-8"
className="absolute top-4 right-4 inline-flex items-center justify-center rounded-lg border border-transparent bg-transparent py-2 px-3 text-center text-sm font-medium text-slate-900 hover:border-slate-200 hover:bg-slate-100 focus:z-10 focus:outline-none focus:ring-4 focus:ring-slate-200 md:top-8 md:right-8"
>
Login
</Link>
Expand All @@ -18,12 +18,12 @@ export default function RegisterPage() {
<div className="flex flex-col space-y-2 text-center">
<Icons.logo className="mx-auto h-6 w-6" />
<h1 className="text-2xl font-bold">Create an account</h1>
<p className="text-sm text-slate-500">
<p className="text-sm text-slate-600">
Enter your email below to create your account
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-slate-500">
<p className="px-8 text-center text-sm text-slate-600">
By clicking continue, you agree to our{" "}
<Link href="/terms" className="underline hover:text-brand">
Terms of Service
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { notFound } from "next/navigation"
import Link from "next/link"

import { getCurrentUser } from "@/lib/session"
import { DashboardNav } from "@/components/dashboard-nav"
import { UserAccountNav } from "@/components/user-account-nav"
import { DashboardNav } from "@/components/dashboard/nav"
import { UserAccountNav } from "@/components/dashboard/user-account-nav"
import { Icons } from "@/components/icons"

interface DashboardLayoutProps {
Expand Down
8 changes: 4 additions & 4 deletions app/(dashboard)/dashboard/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DashboardHeader } from "@/components/dashboard-header"
import { DashboardShell } from "@/components/dashboard-shell"
import { PostCreateButton } from "@/components/post-create-button"
import { PostItem } from "@/components/post-item"
import { DashboardHeader } from "@/components/dashboard/header"
import { DashboardShell } from "@/components/dashboard/shell"
import { PostCreateButton } from "@/components/dashboard/post-create-button"
import { PostItem } from "@/components/dashboard/post-item"

export default function DashboardLoading() {
return (
Expand Down
10 changes: 5 additions & 5 deletions app/(dashboard)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { db } from "@/lib/db"
import { getCurrentUser } from "@/lib/session"
import { User } from "@prisma/client"
import { authOptions } from "@/lib/auth"
import { DashboardHeader } from "@/components/dashboard-header"
import { PostCreateButton } from "@/components/post-create-button"
import { DashboardShell } from "@/components/dashboard-shell"
import { PostItem } from "@/components/post-item"
import { EmptyPlaceholder } from "@/components/empty-placeholder"
import { DashboardHeader } from "@/components/dashboard/header"
import { PostCreateButton } from "@/components/dashboard/post-create-button"
import { DashboardShell } from "@/components/dashboard/shell"
import { PostItem } from "@/components/dashboard/post-item"
import { EmptyPlaceholder } from "@/components/dashboard/empty-placeholder"

async function getPostsForUser(userId: User["id"]) {
return await db.post.findMany({
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/dashboard/settings/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DashboardHeader } from "@/components/dashboard-header"
import { DashboardShell } from "@/components/dashboard-shell"
import { DashboardHeader } from "@/components/dashboard/header"
import { DashboardShell } from "@/components/dashboard/shell"
import { Card } from "@/ui/card"

export default function DashboardSettingsLoading() {
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { redirect } from "next/navigation"

import { getCurrentUser } from "@/lib/session"
import { authOptions } from "@/lib/auth"
import { DashboardHeader } from "@/components/dashboard-header"
import { DashboardShell } from "@/components/dashboard-shell"
import { UserNameForm } from "@/components/user-name-form"
import { DashboardHeader } from "@/components/dashboard/header"
import { DashboardShell } from "@/components/dashboard/shell"
import { UserNameForm } from "@/components/dashboard/user-name-form"

export default async function SettingsPage() {
const user = await getCurrentUser()
Expand Down
5 changes: 5 additions & 0 deletions app/(docs)/docs/[[...slug]]/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import MdxHead from "@/components/docs/mdx-head"

export default function Head({ params }) {
return <MdxHead params={params} />
}
50 changes: 50 additions & 0 deletions app/(docs)/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { notFound } from "next/navigation"
import { allDocs } from "contentlayer/generated"

import { getTableOfContents } from "@/lib/toc"
import { Mdx } from "@/components/docs/mdx"
import { DashboardTableOfContents } from "@/components/docs/toc"
import { DocsPageHeader } from "@/components/docs/page-header"
import { DocsPager } from "@/components/docs/pager"
import "@/styles/mdx.css"

interface DocPageProps {
params: {
slug: string[]
}
}

export async function generateStaticParams(): Promise<
DocPageProps["params"][]
> {
return allDocs.map((doc) => ({
slug: doc.slugAsParams.split("/"),
}))
}

export default async function DocPage({ params }: DocPageProps) {
const slug = params?.slug?.join("/") || ""
const doc = allDocs.find((doc) => doc.slugAsParams === slug)

if (!doc) {
notFound()
}

const toc = await getTableOfContents(doc.body.raw)

return (
<main className="relative py-6 lg:gap-10 lg:py-10 xl:grid xl:grid-cols-[1fr_300px]">
<div className="mx-auto w-full min-w-0">
<DocsPageHeader heading={doc.title} text={doc.description} />
<Mdx code={doc.body.code} />
<hr className="my-4 border-slate-200 md:my-6" />
<DocsPager doc={doc} />
</div>
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10">
<DashboardTableOfContents toc={toc} />
</div>
</div>
</main>
)
}
17 changes: 17 additions & 0 deletions app/(docs)/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { docsConfig } from "@/config/docs"
import { DocsSidebarNav } from "@/components/docs/sidebar-nav"

interface DocsLayoutProps {
children: React.ReactNode
}

export default function DocsLayout({ children }: DocsLayoutProps) {
return (
<div className="flex-1 md:grid md:grid-cols-[220px_1fr] md:gap-6 lg:grid-cols-[240px_1fr] lg:gap-10">
<aside className="fixed top-14 z-30 hidden h-[calc(100vh-3.5rem)] w-full flex-shrink-0 overflow-y-auto border-r border-r-slate-100 py-6 pr-2 md:sticky md:block lg:py-10">
<DocsSidebarNav items={docsConfig.sidebarNav} />
</aside>
{children}
</div>
)
}
5 changes: 5 additions & 0 deletions app/(docs)/guides/[...slug]/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import MdxHead from "@/components/docs/mdx-head"

export default function Head({ params }) {
return <MdxHead params={params} />
}
60 changes: 60 additions & 0 deletions app/(docs)/guides/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Link from "next/link"
import { notFound } from "next/navigation"

import { allGuides } from "contentlayer/generated"

import { getTableOfContents } from "@/lib/toc"
import { Mdx } from "@/components/docs/mdx"
import { DashboardTableOfContents } from "@/components/docs/toc"
import { DocsPageHeader } from "@/components/docs/page-header"
import { Icons } from "@/components/icons"
import "@/styles/mdx.css"

interface GuidePageProps {
params: {
slug: string[]
}
}

export async function generateStaticParams(): Promise<
GuidePageProps["params"][]
> {
return allGuides.map((guide) => ({
slug: guide.slugAsParams.split("/"),
}))
}

export default async function GuidePage({ params }: GuidePageProps) {
const slug = params?.slug?.join("/")
const guide = allGuides.find((guide) => guide.slugAsParams === slug)

if (!guide) {
notFound()
}

const toc = await getTableOfContents(guide.body.raw)

return (
<main className="relative py-6 lg:grid lg:grid-cols-[1fr_300px] lg:gap-10 lg:py-10 xl:gap-20">
<div>
<DocsPageHeader heading={guide.title} text={guide.description} />
<Mdx code={guide.body.code} />
<hr className="my-4 border-slate-200" />
<div className="flex justify-center py-6 lg:py-10">
<Link
href="/guides"
className="mb-4 inline-flex items-center justify-center text-sm font-medium text-slate-600 hover:text-slate-900"
>
<Icons.chevronLeft className="mr-2 h-4 w-4" />
See all guides
</Link>
</div>
</div>
<div className="hidden text-sm lg:block">
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10">
<DashboardTableOfContents toc={toc} />
</div>
</div>
</main>
)
}
7 changes: 7 additions & 0 deletions app/(docs)/guides/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface GuidesLayoutProps {
children: React.ReactNode
}

export default function GuidesLayout({ children }: GuidesLayoutProps) {
return <div className="mx-auto max-w-5xl">{children}</div>
}
59 changes: 59 additions & 0 deletions app/(docs)/guides/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Link from "next/link"
import { compareDesc } from "date-fns"
import { allGuides } from "contentlayer/generated"

import { DocsPageHeader } from "@/components/docs/page-header"
import { formatDate } from "@/lib/utils"

export default function GuidesPage() {
const guides = allGuides
.filter((guide) => guide.published)
.sort((a, b) => {
return compareDesc(new Date(a.date), new Date(b.date))
})

return (
<div className="py-6 lg:py-10">
<DocsPageHeader
heading="Guides"
text="This section includes end-to-end guides for developing Next.js 13 apps."
/>
{guides?.length ? (
<div className="grid gap-4 md:grid-cols-2 md:gap-6">
{guides.map((guide) => (
<article
key={guide._id}
className="group relative rounded-lg border border-slate-200 bg-white p-6 shadow-md transition-shadow hover:shadow-lg"
>
{guide.featured && (
<span className="absolute top-4 right-4 rounded-full bg-slate-100 px-3 py-1 text-xs font-medium">
Featured
</span>
)}
<div className="flex flex-col justify-between space-y-4">
<div className="space-y-2">
<h2 className="text-xl font-medium tracking-tight text-slate-900">
{guide.title}
</h2>
{guide.description && (
<p className="text-slate-700">{guide.description}</p>
)}
</div>
{guide.date && (
<p className="text-sm text-slate-600">
{formatDate(guide.date)}
</p>
)}
</div>
<Link href={guide.slug} className="absolute inset-0">
<span className="sr-only">View</span>
</Link>
</article>
))}
</div>
) : (
<p>No guides published.</p>
)}
</div>
)
}
46 changes: 46 additions & 0 deletions app/(docs)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Link from "next/link"

import { siteConfig } from "@/config/site"
import { docsConfig } from "@/config/docs"
import { Icons } from "@/components/icons"
import { MainNav } from "@/components/main-nav"
import { DocsSearch } from "@/components/docs/search"
import { SiteFooter } from "@/components/site-footer"
import { DocsSidebarNav } from "@/components/docs/sidebar-nav"

interface DocsLayoutProps {
children: React.ReactNode
}

export default function DocsLayout({ children }: DocsLayoutProps) {
return (
<div className="flex min-h-screen flex-col">
<header className="sticky top-0 z-40 w-full border-b border-b-slate-200 bg-white">
<div className="container flex h-16 items-center space-x-4 sm:justify-between sm:space-x-0">
<MainNav items={docsConfig.mainNav}>
<DocsSidebarNav items={docsConfig.sidebarNav} />
</MainNav>
<div className="flex flex-1 items-center space-x-4 sm:justify-end">
<div className="flex-1 sm:flex-grow-0">
<DocsSearch />
</div>
<nav className="flex space-x-4">
<Link
href={siteConfig.links.github}
target="_blank"
rel="noreferrer"
>
<div className="flex h-7 w-7 items-center justify-center rounded-full bg-slate-900 text-slate-50 hover:bg-slate-600">
<Icons.gitHub className="h-4 w-4 fill-white" />
<span className="sr-only">GitHub</span>
</div>
</Link>
</nav>
</div>
</div>
</header>
<div className="container flex-1">{children}</div>
<SiteFooter />
</div>
)
}
Loading

0 comments on commit 9ad4315

Please sign in to comment.