├── .nvmrc ├── .prettierignore ├── public ├── og.jpg ├── favicon.ico ├── images │ ├── hero.png │ ├── avatars │ │ └── shadcn.png │ └── blog │ │ ├── blog-post-1.jpg │ │ ├── blog-post-2.jpg │ │ ├── blog-post-3.jpg │ │ └── blog-post-4.jpg ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── site.webmanifest └── vercel.svg ├── .commitlintrc.json ├── app ├── opengraph-image.jpg ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── _route.ts │ ├── users │ │ ├── [userId] │ │ │ └── route.ts │ │ └── stripe │ │ │ └── route.ts │ ├── webhooks │ │ └── stripe │ │ │ └── route.ts │ └── posts │ │ ├── route.ts │ │ └── [postId] │ │ └── route.ts ├── (auth) │ ├── layout.tsx │ ├── login │ │ └── page.tsx │ └── register │ │ └── page.tsx ├── robots.ts ├── (docs) │ ├── guides │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── [...slug] │ │ │ └── page.tsx │ ├── docs │ │ ├── layout.tsx │ │ └── [[...slug]] │ │ │ └── page.tsx │ └── layout.tsx ├── (editor) │ └── editor │ │ ├── layout.tsx │ │ └── [postId] │ │ ├── loading.tsx │ │ ├── not-found.tsx │ │ └── page.tsx ├── (dashboard) │ └── dashboard │ │ ├── billing │ │ ├── loading.tsx │ │ └── page.tsx │ │ ├── settings │ │ ├── loading.tsx │ │ └── page.tsx │ │ ├── loading.tsx │ │ ├── layout.tsx │ │ └── page.tsx ├── (marketing) │ ├── layout.tsx │ ├── blog │ │ └── page.tsx │ ├── [...slug] │ │ └── page.tsx │ └── pricing │ │ └── page.tsx └── layout.tsx ├── assets └── fonts │ ├── Inter-Bold.ttf │ ├── Inter-Regular.ttf │ ├── CalSans-SemiBold.ttf │ ├── CalSans-SemiBold.woff │ └── CalSans-SemiBold.woff2 ├── .husky ├── commit-msg └── pre-commit ├── content ├── authors │ └── shadcn.mdx ├── docs │ ├── in-progress.mdx │ ├── index.mdx │ └── documentation │ │ ├── index.mdx │ │ ├── code-blocks.mdx │ │ └── components.mdx └── pages │ ├── privacy.mdx │ └── terms.mdx ├── postcss.config.js ├── lib ├── validations │ ├── auth.ts │ ├── user.ts │ ├── og.ts │ └── post.ts ├── exceptions.ts ├── stripe.ts ├── session.ts ├── db.ts ├── utils.ts ├── subscription.ts └── auth.ts ├── prisma ├── migrations │ ├── migration_lock.toml │ ├── 20221118173244_add_stripe_columns │ │ └── migration.sql │ └── 20221021182747_init │ │ └── migration.sql └── schema.prisma ├── pages └── api │ └── auth │ └── [...nextauth].ts ├── components ├── analytics.tsx ├── ui │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── collapsible.tsx │ ├── label.tsx │ ├── textarea.tsx │ ├── separator.tsx │ ├── input.tsx │ ├── progress.tsx │ ├── toaster.tsx │ ├── sonner.tsx │ ├── checkbox.tsx │ ├── slider.tsx │ ├── tooltip.tsx │ ├── switch.tsx │ ├── badge.tsx │ ├── hover-card.tsx │ ├── popover.tsx │ ├── toggle.tsx │ ├── avatar.tsx │ ├── radio-group.tsx │ ├── alert.tsx │ ├── scroll-area.tsx │ ├── resizable.tsx │ ├── toggle-group.tsx │ ├── button.tsx │ ├── tabs.tsx │ ├── card.tsx │ ├── input-otp.tsx │ ├── accordion.tsx │ ├── calendar.tsx │ ├── breadcrumb.tsx │ ├── pagination.tsx │ ├── table.tsx │ └── drawer.tsx ├── theme-provider.tsx ├── shell.tsx ├── card-skeleton.tsx ├── header.tsx ├── page-header.tsx ├── callout.tsx ├── tailwind-indicator.tsx ├── user-avatar.tsx ├── mdx-card.tsx ├── search.tsx ├── post-item.tsx ├── nav.tsx ├── mode-toggle.tsx ├── mobile-nav.tsx ├── pager.tsx ├── sidebar-nav.tsx ├── site-footer.tsx ├── main-nav.tsx ├── empty-placeholder.tsx ├── post-create-button.tsx ├── user-account-nav.tsx ├── icons.tsx ├── billing-form.tsx ├── toc.tsx └── user-name-form.tsx ├── .editorconfig ├── hooks ├── use-mounted.ts └── use-lock-body.ts ├── types ├── next-auth.d.ts └── index.d.ts ├── components.json ├── config ├── marketing.ts ├── site.ts ├── subscriptions.ts ├── dashboard.ts └── docs.ts ├── next.config.mjs ├── .gitignore ├── .eslintrc.json ├── tsconfig.json ├── prettier.config.js ├── styles ├── mdx.css ├── editor.css └── globals.css ├── LICENSE.md ├── middleware.ts ├── .env.example ├── env.mjs ├── tailwind.config.ts ├── README.md └── velite.config.ts /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.18.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .next 4 | build 5 | .contentlayer -------------------------------------------------------------------------------- /public/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/og.jpg -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/hero.png -------------------------------------------------------------------------------- /app/opengraph-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/app/opengraph-image.jpg -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /assets/fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/assets/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/assets/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx pretty-quick --staged 5 | -------------------------------------------------------------------------------- /public/images/avatars/shadcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/avatars/shadcn.png -------------------------------------------------------------------------------- /assets/fonts/CalSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/assets/fonts/CalSans-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/CalSans-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/assets/fonts/CalSans-SemiBold.woff -------------------------------------------------------------------------------- /assets/fonts/CalSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/assets/fonts/CalSans-SemiBold.woff2 -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/images/blog/blog-post-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/blog/blog-post-1.jpg -------------------------------------------------------------------------------- /public/images/blog/blog-post-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/blog/blog-post-2.jpg -------------------------------------------------------------------------------- /public/images/blog/blog-post-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/blog/blog-post-3.jpg -------------------------------------------------------------------------------- /public/images/blog/blog-post-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zce/taxonomy/HEAD/public/images/blog/blog-post-4.jpg -------------------------------------------------------------------------------- /content/authors/shadcn.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: shadcn 3 | avatar: /images/avatars/shadcn.png 4 | twitter: shadcn 5 | --- 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /lib/validations/auth.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const userAuthSchema = z.object({ 4 | email: z.string().email(), 5 | }) 6 | -------------------------------------------------------------------------------- /lib/validations/user.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const userNameSchema = z.object({ 4 | name: z.string().min(3).max(32), 5 | }) 6 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "mysql" -------------------------------------------------------------------------------- /lib/exceptions.ts: -------------------------------------------------------------------------------- 1 | export class RequiresProPlanError extends Error { 2 | constructor(message = "This action requires a pro plan") { 3 | super(message) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/stripe.ts: -------------------------------------------------------------------------------- 1 | import Stripe from "stripe" 2 | 3 | import { env } from "@/env.mjs" 4 | 5 | export const stripe = new Stripe(env.STRIPE_API_KEY, { 6 | typescript: true, 7 | }) 8 | -------------------------------------------------------------------------------- /pages/api/auth/[...nextauth].ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth" 2 | 3 | import { authOptions } from "@/lib/auth" 4 | 5 | // @see ./lib/auth 6 | export default NextAuth(authOptions) 7 | -------------------------------------------------------------------------------- /components/analytics.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Analytics as VercelAnalytics } from "@vercel/analytics/react" 4 | 5 | export function Analytics() { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /lib/validations/og.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const ogImageSchema = z.object({ 4 | heading: z.string(), 5 | type: z.string(), 6 | mode: z.enum(["light", "dark"]).default("dark"), 7 | }) 8 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/_route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth" 2 | 3 | import { authOptions } from "@/lib/auth" 4 | 5 | const handler = NextAuth(authOptions) 6 | 7 | export { handler as GET, handler as POST } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | interface AuthLayoutProps { 2 | children: React.ReactNode 3 | } 4 | 5 | export default function AuthLayout({ children }: AuthLayoutProps) { 6 | return
{children}
7 | } 8 | -------------------------------------------------------------------------------- /app/robots.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from "next" 2 | 3 | export default function robots(): MetadataRoute.Robots { 4 | return { 5 | rules: { 6 | userAgent: "*", 7 | allow: "/", 8 | }, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/(docs)/guides/layout.tsx: -------------------------------------------------------------------------------- 1 | interface GuidesLayoutProps { 2 | children: React.ReactNode 3 | } 4 | 5 | export default function GuidesLayout({ children }: GuidesLayoutProps) { 6 | return
{children}
7 | } 8 | -------------------------------------------------------------------------------- /lib/validations/post.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const postPatchSchema = z.object({ 4 | title: z.string().min(3).max(128).optional(), 5 | 6 | // TODO: Type this properly from editorjs block types? 7 | content: z.any().optional(), 8 | }) 9 | -------------------------------------------------------------------------------- /hooks/use-mounted.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export function useMounted() { 4 | const [mounted, setMounted] = React.useState(false) 5 | 6 | React.useEffect(() => { 7 | setMounted(true) 8 | }, []) 9 | 10 | return mounted 11 | } 12 | -------------------------------------------------------------------------------- /lib/session.ts: -------------------------------------------------------------------------------- 1 | import { getServerSession } from "next-auth/next" 2 | 3 | import { authOptions } from "@/lib/auth" 4 | 5 | export async function getCurrentUser() { 6 | const session = await getServerSession(authOptions) 7 | 8 | return session?.user 9 | } 10 | -------------------------------------------------------------------------------- /app/(editor)/editor/layout.tsx: -------------------------------------------------------------------------------- 1 | interface EditorProps { 2 | children?: React.ReactNode 3 | } 4 | 5 | export default function EditorLayout({ children }: EditorProps) { 6 | return ( 7 |
8 | {children} 9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /content/docs/in-progress.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Not Implemented 3 | description: This page is in progress. 4 | --- 5 | 6 | 7 | 8 | This site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn). 9 | 10 | 11 | -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /types/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import { User } from "next-auth" 2 | import { JWT } from "next-auth/jwt" 3 | 4 | type UserId = string 5 | 6 | declare module "next-auth/jwt" { 7 | interface JWT { 8 | id: UserId 9 | } 10 | } 11 | 12 | declare module "next-auth" { 13 | interface Session { 14 | user: User & { 15 | id: UserId 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { ThemeProvider as NextThemesProvider } from "next-themes" 5 | import { ThemeProviderProps } from "next-themes/dist/types" 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children} 9 | } 10 | -------------------------------------------------------------------------------- /components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /hooks/use-lock-body.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | // @see https://usehooks.com/useLockBodyScroll. 4 | export function useLockBody() { 5 | React.useLayoutEffect((): (() => void) => { 6 | const originalStyle: string = window.getComputedStyle( 7 | document.body 8 | ).overflow 9 | document.body.style.overflow = "hidden" 10 | return () => (document.body.style.overflow = originalStyle) 11 | }, []) 12 | } 13 | -------------------------------------------------------------------------------- /components/shell.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | interface DashboardShellProps extends React.HTMLAttributes {} 6 | 7 | export function DashboardShell({ 8 | children, 9 | className, 10 | ...props 11 | }: DashboardShellProps) { 12 | return ( 13 |
14 | {children} 15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /config/marketing.ts: -------------------------------------------------------------------------------- 1 | import { MarketingConfig } from "types" 2 | 3 | export const marketingConfig: MarketingConfig = { 4 | mainNav: [ 5 | { 6 | title: "Features", 7 | href: "/#features", 8 | }, 9 | { 10 | title: "Pricing", 11 | href: "/pricing", 12 | }, 13 | { 14 | title: "Blog", 15 | href: "/blog", 16 | }, 17 | { 18 | title: "Documentation", 19 | href: "/docs", 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /lib/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client" 2 | 3 | declare global { 4 | // eslint-disable-next-line no-var 5 | var cachedPrisma: PrismaClient 6 | } 7 | 8 | let prisma: PrismaClient 9 | if (process.env.NODE_ENV === "production") { 10 | prisma = new PrismaClient() 11 | } else { 12 | if (!global.cachedPrisma) { 13 | global.cachedPrisma = new PrismaClient() 14 | } 15 | prisma = global.cachedPrisma 16 | } 17 | 18 | export const db = prisma 19 | -------------------------------------------------------------------------------- /config/site.ts: -------------------------------------------------------------------------------- 1 | import { SiteConfig } from "types" 2 | 3 | export const siteConfig: SiteConfig = { 4 | name: "Taxonomy", 5 | description: 6 | "An open source application built using the new router, server components and everything new in Next.js 13.", 7 | url: "https://tx.shadcn.com", 8 | ogImage: "https://tx.shadcn.com/og.jpg", 9 | links: { 10 | twitter: "https://twitter.com/shadcn", 11 | github: "https://github.com/shadcn/taxonomy", 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Taxonomy", 3 | "short_name": "Taxonomy", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /config/subscriptions.ts: -------------------------------------------------------------------------------- 1 | import { SubscriptionPlan } from "types" 2 | import { env } from "@/env.mjs" 3 | 4 | export const freePlan: SubscriptionPlan = { 5 | name: "Free", 6 | description: 7 | "The free plan is limited to 3 posts. Upgrade to the PRO plan for unlimited posts.", 8 | stripePriceId: "", 9 | } 10 | 11 | export const proPlan: SubscriptionPlan = { 12 | name: "PRO", 13 | description: "The PRO plan has unlimited posts.", 14 | stripePriceId: env.STRIPE_PRO_MONTHLY_PLAN_ID || "", 15 | } 16 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | import "./env.mjs" 2 | 3 | const isDev = process.argv.indexOf('dev') !== -1 4 | const isBuild = process.argv.indexOf('build') !== -1 5 | if (!process.env.VELITE_STARTED && (isDev || isBuild)) { 6 | process.env.VELITE_STARTED = '1' 7 | const { build } = await import('velite') 8 | await build({ watch: isDev, clean: !isDev }) 9 | } 10 | 11 | /** @type {import('next').NextConfig} */ 12 | const nextConfig = { 13 | images: { 14 | domains: ["avatars.githubusercontent.com"], 15 | } 16 | } 17 | 18 | export default nextConfig 19 | -------------------------------------------------------------------------------- /components/card-skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card" 2 | import { Skeleton } from "@/components/ui/skeleton" 3 | 4 | export function CardSkeleton() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /app/(dashboard)/dashboard/billing/loading.tsx: -------------------------------------------------------------------------------- 1 | import { CardSkeleton } from "@/components/card-skeleton" 2 | import { DashboardHeader } from "@/components/header" 3 | import { DashboardShell } from "@/components/shell" 4 | 5 | export default function DashboardBillingLoading() { 6 | return ( 7 | 8 | 12 |
13 | 14 |
15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | import { env } from "@/env.mjs" 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)) 7 | } 8 | 9 | export function formatDate(input: string | number): string { 10 | const date = new Date(input) 11 | return date.toLocaleDateString("en-US", { 12 | month: "long", 13 | day: "numeric", 14 | year: "numeric", 15 | }) 16 | } 17 | 18 | export function absoluteUrl(path: string) { 19 | return `${env.NEXT_PUBLIC_APP_URL}${path}` 20 | } 21 | -------------------------------------------------------------------------------- /components/header.tsx: -------------------------------------------------------------------------------- 1 | interface DashboardHeaderProps { 2 | heading: string 3 | text?: string 4 | children?: React.ReactNode 5 | } 6 | 7 | export function DashboardHeader({ 8 | heading, 9 | text, 10 | children, 11 | }: DashboardHeaderProps) { 12 | return ( 13 |
14 |
15 |

{heading}

16 | {text &&

{text}

} 17 |
18 | {children} 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /app/(dashboard)/dashboard/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "@/components/ui/card" 2 | import { CardSkeleton } from "@/components/card-skeleton" 3 | import { DashboardHeader } from "@/components/header" 4 | import { DashboardShell } from "@/components/shell" 5 | 6 | export default function DashboardSettingsLoading() { 7 | return ( 8 | 9 | 13 |
14 | 15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | .vscode 40 | 41 | # velite generated files 42 | .velite 43 | public/static 44 | -------------------------------------------------------------------------------- /app/(docs)/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { docsConfig } from "@/config/docs" 2 | import { DocsSidebarNav } from "@/components/sidebar-nav" 3 | 4 | interface DocsLayoutProps { 5 | children: React.ReactNode 6 | } 7 | 8 | export default function DocsLayout({ children }: DocsLayoutProps) { 9 | return ( 10 |
11 | 14 | {children} 15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /components/page-header.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | interface DocsPageHeaderProps extends React.HTMLAttributes { 4 | heading: string 5 | text?: string 6 | } 7 | 8 | export function DocsPageHeader({ 9 | heading, 10 | text, 11 | className, 12 | ...props 13 | }: DocsPageHeaderProps) { 14 | return ( 15 | <> 16 |
17 |

18 | {heading} 19 |

20 | {text &&

{text}

} 21 |
22 |
23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /app/(editor)/editor/[postId]/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /config/dashboard.ts: -------------------------------------------------------------------------------- 1 | import { DashboardConfig } from "types" 2 | 3 | export const dashboardConfig: DashboardConfig = { 4 | mainNav: [ 5 | { 6 | title: "Documentation", 7 | href: "/docs", 8 | }, 9 | { 10 | title: "Support", 11 | href: "/support", 12 | disabled: true, 13 | }, 14 | ], 15 | sidebarNav: [ 16 | { 17 | title: "Posts", 18 | href: "/dashboard", 19 | icon: "post", 20 | }, 21 | { 22 | title: "Billing", 23 | href: "/dashboard/billing", 24 | icon: "billing", 25 | }, 26 | { 27 | title: "Settings", 28 | href: "/dashboard/settings", 29 | icon: "settings", 30 | }, 31 | ], 32 | } 33 | -------------------------------------------------------------------------------- /components/callout.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | interface CalloutProps { 4 | icon?: string 5 | children?: React.ReactNode 6 | type?: "default" | "warning" | "danger" 7 | } 8 | 9 | export function Callout({ 10 | children, 11 | icon, 12 | type = "default", 13 | ...props 14 | }: CalloutProps) { 15 | return ( 16 |
23 | {icon && {icon}} 24 |
{children}
25 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /app/(editor)/editor/[postId]/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | 3 | import { buttonVariants } from "@/components/ui/button" 4 | import { EmptyPlaceholder } from "@/components/empty-placeholder" 5 | 6 | export default function NotFound() { 7 | return ( 8 | 9 | 10 | Uh oh! Not Found 11 | 12 | This post cound not be found. Please try again. 13 | 14 | 15 | Go to Dashboard 16 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /components/tailwind-indicator.tsx: -------------------------------------------------------------------------------- 1 | export function TailwindIndicator() { 2 | if (process.env.NODE_ENV === "production") return null 3 | 4 | return ( 5 |
6 |
xs
7 |
8 | sm 9 |
10 |
md
11 |
lg
12 |
xl
13 |
2xl
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /components/user-avatar.tsx: -------------------------------------------------------------------------------- 1 | import { User } from "@prisma/client" 2 | import { AvatarProps } from "@radix-ui/react-avatar" 3 | 4 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" 5 | import { Icons } from "@/components/icons" 6 | 7 | interface UserAvatarProps extends AvatarProps { 8 | user: Pick 9 | } 10 | 11 | export function UserAvatar({ user, ...props }: UserAvatarProps) { 12 | return ( 13 | 14 | {user.image ? ( 15 | 16 | ) : ( 17 | 18 | {user.name} 19 | 20 | 21 | )} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/eslintrc", 3 | "root": true, 4 | "extends": [ 5 | "next/core-web-vitals", 6 | "prettier", 7 | "plugin:tailwindcss/recommended" 8 | ], 9 | "plugins": ["tailwindcss"], 10 | "rules": { 11 | "@next/next/no-html-link-for-pages": "off", 12 | "react/jsx-key": "off", 13 | "tailwindcss/no-custom-classname": "off", 14 | "tailwindcss/classnames-order": "error" 15 | }, 16 | "settings": { 17 | "tailwindcss": { 18 | "callees": ["cn"], 19 | "config": "tailwind.config.js" 20 | }, 21 | "next": { 22 | "rootDir": true 23 | } 24 | }, 25 | "overrides": [ 26 | { 27 | "files": ["*.ts", "*.tsx"], 28 | "parser": "@typescript-eslint/parser" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /app/(dashboard)/dashboard/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/header" 2 | import { PostCreateButton } from "@/components/post-create-button" 3 | import { PostItem } from "@/components/post-item" 4 | import { DashboardShell } from "@/components/shell" 5 | 6 | export default function DashboardLoading() { 7 | return ( 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |