Skip to content

Commit

Permalink
feat: update to Next 13.3
Browse files Browse the repository at this point in the history
  • Loading branch information
shadcn committed Apr 24, 2023
1 parent 03ef292 commit c606420
Show file tree
Hide file tree
Showing 124 changed files with 4,738 additions and 4,038 deletions.
11 changes: 9 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@
},
"settings": {
"tailwindcss": {
"callees": ["cn"]
"callees": ["cn"],
"config": "tailwind.config.js"
},
"next": {
"rootDir": true
}
}
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser"
}
]
}
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,18 @@ An open source application built using the new router, server components and eve
> This app is a work in progress. I'm building this in public. You can follow the progress on Twitter [@shadcn](https://twitter.com/shadcn).
> See the roadmap below.
## Demo

![screenshot-2](https://user-images.githubusercontent.com/124599/198038921-2b16b18b-cb4d-44b1-bd1d-6419d4a8d92c.png)

## About this project

Right now, I'm using this project as an experiment to see how a modern app (with features like authentication, subscriptions, API routes, static pages for docs ...etc) would work in Next.js 13 and server components.
This project as an experiment to see how a modern app (with features like authentication, subscriptions, API routes, static pages for docs ...etc) would work in Next.js 13 and server components.

I'll be posting updates and issues here.
**This is not a starter template.**

A few people have asked me to turn this into a starter. I think we could do that once the new features are out of beta.

## Note on Performance

> **Warning**
> This app is using the canary releases for Next.js 13 and React 18. The new router and app dir is still in beta and not production-ready.
> NextAuth.js, which is used for authentication, is also not fully supported in Next.js 13 and RSC.
> This app is using the unstable releases for Next.js 13 and React 18. The new router and app dir is still in beta and not production-ready.
> **Expect some performance hits when testing the dashboard**.
> If you see something broken, you can ping me [@shadcn](https://twitter.com/shadcn).
Expand All @@ -32,6 +27,8 @@ A few people have asked me to turn this into a starter. I think we could do that
- Routing, Layouts, Nested Layouts and Layout Groups
- Data Fetching, Caching and Mutation
- Loading UI
- Route handlers
- Metadata files
- Server and Client Components
- API Routes and Middlewares
- Authentication using **NextAuth.js**
Expand All @@ -51,8 +48,7 @@ A few people have asked me to turn this into a starter. I think we could do that
- [x] ~Subscriptions using Stripe~
- [x] ~Responsive styles~
- [x] ~Add OG image for blog using @vercel/og~
- [ ] Add tests
- [ ] Dark mode
- [x] Dark mode

## Known Issues

Expand All @@ -61,6 +57,7 @@ A list of things not working right now:
1. ~GitHub authentication (use email)~
2. ~[Prisma: Error: ENOENT: no such file or directory, open '/var/task/.next/server/chunks/schema.prisma'](https://github.com/prisma/prisma/issues/16117)~
3. ~[Next.js 13: Client side navigation does not update head](https://github.com/vercel/next.js/issues/42414)~
4. [Cannot use opengraph-image.tsx inside catch-all routes](https://github.com/vercel/next.js/issues/48162)

## Why not tRPC, Turborepo or X?

Expand Down
8 changes: 4 additions & 4 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Metadata } from "next"
import Link from "next/link"

import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { buttonVariants } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import { UserAuthForm } from "@/components/user-auth-form"

export const metadata: Metadata = {
Expand All @@ -18,7 +18,7 @@ export default function LoginPage() {
href="/"
className={cn(
buttonVariants({ variant: "ghost" }),
"absolute top-4 left-4 md:top-8 md:left-8"
"absolute left-4 top-4 md:left-8 md:top-8"
)}
>
<>
Expand All @@ -32,12 +32,12 @@ export default function LoginPage() {
<h1 className="text-2xl font-semibold tracking-tight">
Welcome back
</h1>
<p className="text-sm text-slate-500 dark:text-slate-400">
<p className="text-sm text-muted-foreground">
Enter your email to sign in to your account
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-slate-500 dark:text-slate-400">
<p className="px-8 text-center text-sm text-muted-foreground">
<Link
href="/register"
className="hover:text-brand underline underline-offset-4"
Expand Down
10 changes: 5 additions & 5 deletions app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Link from "next/link"

import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { buttonVariants } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import { UserAuthForm } from "@/components/user-auth-form"

export const metadata = {
Expand All @@ -17,25 +17,25 @@ export default function RegisterPage() {
href="/login"
className={cn(
buttonVariants({ variant: "ghost" }),
"absolute top-4 right-4 md:top-8 md:right-8"
"absolute right-4 top-4 md:right-8 md:top-8"
)}
>
Login
</Link>
<div className="hidden h-full bg-slate-100 lg:block" />
<div className="hidden h-full bg-muted lg:block" />
<div className="lg:p-8">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col space-y-2 text-center">
<Icons.logo className="mx-auto h-6 w-6" />
<h1 className="text-2xl font-semibold tracking-tight">
Create an account
</h1>
<p className="text-sm text-slate-500 dark:text-slate-400">
<p className="text-sm text-muted-foreground">
Enter your email below to create your account
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-slate-500 dark:text-slate-400">
<p className="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
Expand Down
5 changes: 2 additions & 3 deletions app/(dashboard)/dashboard/billing/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CardSkeleton } from "@/components/card-skeleton"
import { DashboardHeader } from "@/components/header"
import { DashboardShell } from "@/components/shell"
import { Card } from "@/components/ui/card"

export default function DashboardBillingLoading() {
return (
Expand All @@ -10,8 +10,7 @@ export default function DashboardBillingLoading() {
text="Manage billing and your subscription plan."
/>
<div className="grid gap-10">
<Card.Skeleton />
<Card.Skeleton />
<CardSkeleton />
</div>
</DashboardShell>
)
Expand Down
54 changes: 27 additions & 27 deletions app/(dashboard)/dashboard/billing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import { authOptions } from "@/lib/auth"
import { getCurrentUser } from "@/lib/session"
import { stripe } from "@/lib/stripe"
import { getUserSubscriptionPlan } from "@/lib/subscription"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { BillingForm } from "@/components/billing-form"
import { DashboardHeader } from "@/components/header"
import { Icons } from "@/components/icons"
import { DashboardShell } from "@/components/shell"
import { Card } from "@/components/ui/card"

export const metadata = {
title: "Billing",
Expand Down Expand Up @@ -38,38 +46,30 @@ export default async function BillingPage() {
heading="Billing"
text="Manage billing and your subscription plan."
/>
<div className="grid gap-10">
<div className="grid gap-8">
<Alert className="!pl-14">
<Icons.warning />
<AlertTitle>This is a demo app.</AlertTitle>
<AlertDescription>
Taxonomy app is a demo app using a Stripe test environment. You can
find a list of test card numbers on the{" "}
<a
href="https://stripe.com/docs/testing#cards"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-8"
>
Stripe docs
</a>
.
</AlertDescription>
</Alert>
<BillingForm
subscriptionPlan={{
...subscriptionPlan,
isCanceled,
}}
/>
<Card>
<Card.Header>
<Card.Title>Note</Card.Title>
</Card.Header>
<Card.Content className="space-y-4 pb-6 text-sm">
<p>
Taxonomy app is a demo app using a Stripe test environment.{" "}
<strong>
You can test the upgrade and won&apos;t be charged.
</strong>
</p>
<p>
You can find a list of test card numbers on the{" "}
<a
href="https://stripe.com/docs/testing#cards"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-8"
>
Stripe docs
</a>
.
</p>
</Card.Content>
</Card>
</div>
</DashboardShell>
)
Expand Down
10 changes: 6 additions & 4 deletions app/(dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { dashboardConfig } from "@/config/dashboard"
import { getCurrentUser } from "@/lib/session"
import { MainNav } from "@/components/main-nav"
import { DashboardNav } from "@/components/nav"
import { SiteFooter } from "@/components/site-footer"
import { UserAccountNav } from "@/components/user-account-nav"

interface DashboardLayoutProps {
Expand All @@ -20,9 +21,9 @@ export default async function DashboardLayout({
}

return (
<div className="mx-auto flex flex-col space-y-6">
<header className="container sticky top-0 z-40 bg-white">
<div className="flex h-16 items-center justify-between border-b border-b-slate-200 py-4">
<div className="flex min-h-screen flex-col space-y-6">
<header className="sticky top-0 z-40 border-b bg-background">
<div className="container flex h-16 items-center justify-between py-4">
<MainNav items={dashboardConfig.mainNav} />
<UserAccountNav
user={{
Expand All @@ -33,14 +34,15 @@ export default async function DashboardLayout({
/>
</div>
</header>
<div className="container grid gap-12 md:grid-cols-[200px_1fr]">
<div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr]">
<aside className="hidden w-[200px] flex-col md:flex">
<DashboardNav items={dashboardConfig.sidebarNav} />
</aside>
<main className="flex w-full flex-1 flex-col overflow-hidden">
{children}
</main>
</div>
<SiteFooter className="border-t" />
</div>
)
}
2 changes: 1 addition & 1 deletion app/(dashboard)/dashboard/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function DashboardLoading() {
<DashboardHeader heading="Posts" text="Create and manage posts.">
<PostCreateButton />
</DashboardHeader>
<div className="divide-y divide-neutral-200 rounded-md border border-slate-200">
<div className="divide-border-200 divide-y rounded-md border">
<PostItem.Skeleton />
<PostItem.Skeleton />
<PostItem.Skeleton />
Expand Down
35 changes: 11 additions & 24 deletions app/(dashboard)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { cache } from "react"
import { redirect } from "next/navigation"
import { User } from "@prisma/client"

import { authOptions } from "@/lib/auth"
import { db } from "@/lib/db"
import { getCurrentUser } from "@/lib/session"
import { cn } from "@/lib/utils"
import { EmptyPlaceholder } from "@/components/empty-placeholder"
import { DashboardHeader } from "@/components/header"
import { PostCreateButton } from "@/components/post-create-button"
import { PostItem } from "@/components/post-item"
import { DashboardShell } from "@/components/shell"
import { buttonVariants } from "@/components/ui/button"

export const metadata = {
title: "Dashboard",
}

const getPostsForUser = cache(async (userId: User["id"]) => {
return await db.post.findMany({
export default async function DashboardPage() {
const user = await getCurrentUser()

if (!user) {
redirect(authOptions?.pages?.signIn || "/login")
}

const posts = await db.post.findMany({
where: {
authorId: userId,
authorId: user.id,
},
select: {
id: true,
Expand All @@ -32,16 +34,6 @@ const getPostsForUser = cache(async (userId: User["id"]) => {
updatedAt: "desc",
},
})
})

export default async function DashboardPage() {
const user = await getCurrentUser()

if (!user) {
redirect(authOptions?.pages?.signIn || "/login")
}

const posts = await getPostsForUser(user.id)

return (
<DashboardShell>
Expand All @@ -50,7 +42,7 @@ export default async function DashboardPage() {
</DashboardHeader>
<div>
{posts?.length ? (
<div className="divide-y divide-neutral-200 rounded-md border border-slate-200">
<div className="divide-y divide-border rounded-md border">
{posts.map((post) => (
<PostItem key={post.id} post={post} />
))}
Expand All @@ -62,12 +54,7 @@ export default async function DashboardPage() {
<EmptyPlaceholder.Description>
You don&apos;t have any posts yet. Start creating content.
</EmptyPlaceholder.Description>
<PostCreateButton
className={cn(
buttonVariants({ variant: "outline" }),
"text-slate-900"
)}
/>
<PostCreateButton variant="outline" />
</EmptyPlaceholder>
)}
</div>
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/dashboard/settings/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Card } from "@/components/ui/card"
import { CardSkeleton } from "@/components/card-skeleton"
import { DashboardHeader } from "@/components/header"
import { DashboardShell } from "@/components/shell"
import { Card } from "@/components/ui/card"

export default function DashboardSettingsLoading() {
return (
Expand All @@ -10,8 +11,7 @@ export default function DashboardSettingsLoading() {
text="Manage account and website settings."
/>
<div className="grid gap-10">
<Card.Skeleton />
<Card.Skeleton />
<CardSkeleton />
</div>
</DashboardShell>
)
Expand Down
Loading

0 comments on commit c606420

Please sign in to comment.