From c606420fe76984ecf33d5b842b284232e975555a Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 24 Apr 2023 15:10:49 +0400 Subject: [PATCH] feat: update to Next 13.3 --- .eslintrc.json | 11 +- README.md | 17 +- app/(auth)/login/page.tsx | 8 +- app/(auth)/register/page.tsx | 10 +- app/(dashboard)/dashboard/billing/loading.tsx | 5 +- app/(dashboard)/dashboard/billing/page.tsx | 54 +- app/(dashboard)/dashboard/layout.tsx | 10 +- app/(dashboard)/dashboard/loading.tsx | 2 +- app/(dashboard)/dashboard/page.tsx | 35 +- .../dashboard/settings/loading.tsx | 6 +- app/(docs)/docs/[[...slug]]/page.tsx | 5 +- app/(docs)/docs/layout.tsx | 2 +- app/(docs)/guides/[...slug]/page.tsx | 10 +- app/(docs)/guides/page.tsx | 10 +- app/(docs)/layout.tsx | 4 +- app/(editor)/editor/[postId]/loading.tsx | 2 +- app/(editor)/editor/[postId]/not-found.tsx | 6 +- app/(marketing)/[...slug]/page.tsx | 11 +- app/(marketing)/blog/[...slug]/page.tsx | 33 +- app/(marketing)/blog/page.tsx | 12 +- app/(marketing)/layout.tsx | 11 +- app/(marketing)/page.tsx | 179 +- app/(marketing)/pricing/page.tsx | 20 +- app/api/auth/[...nextauth]/route.ts | 7 + pages/api/og.tsx => app/api/og/route.tsx | 19 +- app/api/posts/[postId]/route.ts | 93 + app/api/posts/route.ts | 92 + app/api/users/[userId]/route.ts | 50 + app/api/users/stripe/route.ts | 61 + .../api/webhooks/stripe/route.ts | 23 +- app/layout.tsx | 46 +- app/opengraph-image.jpg | Bin 0 -> 114800 bytes app/robots.ts | 10 + assets/fonts/CalSans-SemiBold.ttf | Bin 0 -> 148964 bytes assets/fonts/CalSans-SemiBold.woff | Bin 0 -> 52504 bytes assets/fonts/CalSans-SemiBold.woff2 | Bin 0 -> 40932 bytes components/billing-form.tsx | 29 +- components/callout.tsx | 1 - components/card-skeleton.tsx | 17 + components/editor.tsx | 13 +- components/empty-placeholder.tsx | 4 +- components/header.tsx | 6 +- components/icons.tsx | 7 +- components/main-nav.tsx | 6 +- components/{card.tsx => mdx-card.tsx} | 6 +- components/{mdx.tsx => mdx-components.tsx} | 36 +- components/mdx-head.tsx | 55 - components/mobile-nav.tsx | 2 +- components/mode-toggle.tsx | 43 + components/nav.tsx | 4 +- components/page-header.tsx | 6 +- components/pager.tsx | 6 +- components/post-create-button.tsx | 10 +- components/post-item.tsx | 5 +- components/post-operations.tsx | 8 +- components/search.tsx | 9 +- components/sidebar-nav.tsx | 13 +- components/site-footer.tsx | 36 +- components/theme-provider.tsx | 9 + components/toc.tsx | 6 +- components/ui/accordion.tsx | 9 +- components/ui/alert-dialog.tsx | 22 +- components/ui/alert.tsx | 59 + components/ui/avatar.tsx | 2 +- components/ui/badge.tsx | 36 + components/ui/button.tsx | 20 +- components/ui/calendar.tsx | 64 + components/ui/card.tsx | 130 +- components/ui/checkbox.tsx | 4 +- components/ui/command.tsx | 25 +- components/ui/context-menu.tsx | 18 +- components/ui/dialog.tsx | 18 +- components/ui/dropdown-menu.tsx | 21 +- components/ui/hover-card.tsx | 2 +- components/ui/input.tsx | 5 +- components/ui/label.tsx | 13 +- components/ui/menubar.tsx | 22 +- components/ui/navigation-menu.tsx | 12 +- components/ui/popover.tsx | 2 +- components/ui/progress.tsx | 4 +- components/ui/radio-group.tsx | 4 +- components/ui/scroll-area.tsx | 2 +- components/ui/select.tsx | 29 +- components/ui/separator.tsx | 2 +- components/ui/sheet.tsx | 230 + components/ui/skeleton.tsx | 14 +- components/ui/slider.tsx | 6 +- components/ui/switch.tsx | 4 +- components/ui/tabs.tsx | 10 +- components/ui/textarea.tsx | 2 +- components/ui/toast.tsx | 13 +- components/ui/toaster.tsx | 3 +- components/ui/toggle.tsx | 4 +- components/ui/tooltip.tsx | 5 +- {hooks => components/ui}/use-toast.ts | 5 +- components/user-account-nav.tsx | 3 +- components/user-auth-form.tsx | 10 +- components/user-avatar.tsx | 2 +- components/user-name-form.tsx | 31 +- config/marketing.ts | 8 +- content/docs/documentation/index.mdx | 2 +- content/docs/index.mdx | 2 +- content/pages/privacy.mdx | 8 +- content/pages/terms.mdx | 2 + contentlayer.config.js | 4 +- lib/api-middlewares/with-authentication.ts | 16 - lib/api-middlewares/with-current-user.ts | 32 - lib/api-middlewares/with-methods.ts | 11 - lib/api-middlewares/with-post.ts | 39 - lib/api-middlewares/with-validation.ts | 24 - lib/session.ts | 6 +- package.json | 152 +- pages/.gitkeep | 0 pages/api/auth/[...nextauth].ts | 6 - pages/api/posts/[postId].ts | 61 - pages/api/posts/index.ts | 91 - pages/api/users/[userId].ts | 47 - pages/api/users/stripe.ts | 64 - pnpm-lock.yaml | 6037 +++++++++-------- prettier.config.js | 6 +- public/robots.txt | 3 - styles/editor.css | 61 + styles/globals.css | 78 + tailwind.config.js | 54 +- 124 files changed, 4791 insertions(+), 4091 deletions(-) create mode 100644 app/api/auth/[...nextauth]/route.ts rename pages/api/og.tsx => app/api/og/route.tsx (94%) create mode 100644 app/api/posts/[postId]/route.ts create mode 100644 app/api/posts/route.ts create mode 100644 app/api/users/[userId]/route.ts create mode 100644 app/api/users/stripe/route.ts rename pages/api/webhooks/stripe.ts => app/api/webhooks/stripe/route.ts (77%) create mode 100644 app/opengraph-image.jpg create mode 100644 app/robots.ts create mode 100644 assets/fonts/CalSans-SemiBold.ttf create mode 100644 assets/fonts/CalSans-SemiBold.woff create mode 100644 assets/fonts/CalSans-SemiBold.woff2 create mode 100644 components/card-skeleton.tsx rename components/{card.tsx => mdx-card.tsx} (75%) rename components/{mdx.tsx => mdx-components.tsx} (74%) delete mode 100644 components/mdx-head.tsx create mode 100644 components/mode-toggle.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/ui/alert.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/sheet.tsx rename {hooks => components/ui}/use-toast.ts (98%) delete mode 100644 lib/api-middlewares/with-authentication.ts delete mode 100644 lib/api-middlewares/with-current-user.ts delete mode 100644 lib/api-middlewares/with-methods.ts delete mode 100644 lib/api-middlewares/with-post.ts delete mode 100644 lib/api-middlewares/with-validation.ts delete mode 100644 pages/.gitkeep delete mode 100644 pages/api/auth/[...nextauth].ts delete mode 100644 pages/api/posts/[postId].ts delete mode 100644 pages/api/posts/index.ts delete mode 100644 pages/api/users/[userId].ts delete mode 100644 pages/api/users/stripe.ts delete mode 100644 public/robots.txt create mode 100644 styles/editor.css diff --git a/.eslintrc.json b/.eslintrc.json index b62ff2b..0ba73ae 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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" + } + ] } diff --git a/README.md b/README.md index 74c40a4..040a609 100644 --- a/README.md +++ b/README.md @@ -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). @@ -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** @@ -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 @@ -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? diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 036cafb..501384c 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -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 = { @@ -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" )} > <> @@ -32,12 +32,12 @@ export default function LoginPage() {

Welcome back

-

+

Enter your email to sign in to your account

-

+

Login -

+
@@ -30,12 +30,12 @@ export default function RegisterPage() {

Create an account

-

+

Enter your email below to create your account

-

+

By clicking continue, you agree to our{" "}

- - +
) diff --git a/app/(dashboard)/dashboard/billing/page.tsx b/app/(dashboard)/dashboard/billing/page.tsx index 8630a15..0a601e5 100644 --- a/app/(dashboard)/dashboard/billing/page.tsx +++ b/app/(dashboard)/dashboard/billing/page.tsx @@ -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", @@ -38,38 +46,30 @@ export default async function BillingPage() { heading="Billing" text="Manage billing and your subscription plan." /> -
+
+ + + This is a demo app. + + Taxonomy app is a demo app using a Stripe test environment. You can + find a list of test card numbers on the{" "} + + Stripe docs + + . + + - - - Note - - -

- Taxonomy app is a demo app using a Stripe test environment.{" "} - - You can test the upgrade and won't be charged. - -

-

- You can find a list of test card numbers on the{" "} - - Stripe docs - - . -

-
-
) diff --git a/app/(dashboard)/dashboard/layout.tsx b/app/(dashboard)/dashboard/layout.tsx index 4893d10..71a90d6 100644 --- a/app/(dashboard)/dashboard/layout.tsx +++ b/app/(dashboard)/dashboard/layout.tsx @@ -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 { @@ -20,9 +21,9 @@ export default async function DashboardLayout({ } return ( -
-
-
+
+
+
-
+
@@ -41,6 +42,7 @@ export default async function DashboardLayout({ {children}
+
) } diff --git a/app/(dashboard)/dashboard/loading.tsx b/app/(dashboard)/dashboard/loading.tsx index 57f2969..72bd402 100644 --- a/app/(dashboard)/dashboard/loading.tsx +++ b/app/(dashboard)/dashboard/loading.tsx @@ -9,7 +9,7 @@ export default function DashboardLoading() { -
+
diff --git a/app/(dashboard)/dashboard/page.tsx b/app/(dashboard)/dashboard/page.tsx index ef847e5..5f4fc6e 100644 --- a/app/(dashboard)/dashboard/page.tsx +++ b/app/(dashboard)/dashboard/page.tsx @@ -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, @@ -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 ( @@ -50,7 +42,7 @@ export default async function DashboardPage() {
{posts?.length ? ( -
+
{posts.map((post) => ( ))} @@ -62,12 +54,7 @@ export default async function DashboardPage() { You don't have any posts yet. Start creating content. - + )}
diff --git a/app/(dashboard)/dashboard/settings/loading.tsx b/app/(dashboard)/dashboard/settings/loading.tsx index 613a5f5..8f9eacb 100644 --- a/app/(dashboard)/dashboard/settings/loading.tsx +++ b/app/(dashboard)/dashboard/settings/loading.tsx @@ -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 ( @@ -10,8 +11,7 @@ export default function DashboardSettingsLoading() { text="Manage account and website settings." />
- - +
) diff --git a/app/(docs)/docs/[[...slug]]/page.tsx b/app/(docs)/docs/[[...slug]]/page.tsx index 7e26ef6..051d113 100644 --- a/app/(docs)/docs/[[...slug]]/page.tsx +++ b/app/(docs)/docs/[[...slug]]/page.tsx @@ -2,10 +2,11 @@ import { notFound } from "next/navigation" import { allDocs } from "contentlayer/generated" import { getTableOfContents } from "@/lib/toc" -import { Mdx } from "@/components/mdx" +import { Mdx } from "@/components/mdx-components" import { DocsPageHeader } from "@/components/page-header" import { DocsPager } from "@/components/pager" import { DashboardTableOfContents } from "@/components/toc" + import "@/styles/mdx.css" import { Metadata } from "next" @@ -92,7 +93,7 @@ export default async function DocPage({ params }: DocPageProps) {
-
+
diff --git a/app/(docs)/docs/layout.tsx b/app/(docs)/docs/layout.tsx index 2b08491..38d92a5 100644 --- a/app/(docs)/docs/layout.tsx +++ b/app/(docs)/docs/layout.tsx @@ -8,7 +8,7 @@ interface DocsLayoutProps { export default function DocsLayout({ children }: DocsLayoutProps) { return (
-