From a9cd027476e1b3d36f805dcf6cc62f67d4059aba Mon Sep 17 00:00:00 2001 From: mickasmt Date: Wed, 29 May 2024 16:28:04 +0200 Subject: [PATCH] refactor: add constructMetadata() and update layouts --- app/(dashboard)/dashboard/billing/page.tsx | 13 ++-- app/(dashboard)/dashboard/layout.tsx | 19 ++--- app/(dashboard)/dashboard/page.tsx | 26 +++---- app/(dashboard)/dashboard/settings/page.tsx | 25 +++---- app/(docs)/layout.tsx | 2 +- app/(marketing)/blog/page.tsx | 20 +++--- app/(marketing)/pricing/page.tsx | 28 ++++---- app/layout.tsx | 50 +------------ components/blog-posts.tsx | 27 ++++--- lib/utils.ts | 78 ++++++++++++++++++--- 10 files changed, 161 insertions(+), 127 deletions(-) diff --git a/app/(dashboard)/dashboard/billing/page.tsx b/app/(dashboard)/dashboard/billing/page.tsx index 196a4679..6ca5e821 100644 --- a/app/(dashboard)/dashboard/billing/page.tsx +++ b/app/(dashboard)/dashboard/billing/page.tsx @@ -1,17 +1,18 @@ import { redirect } from "next/navigation"; -import { getCurrentUser } from "@/lib/session"; -import { getUserSubscriptionPlan } from "@/lib/subscription"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { BillingInfo } from "@/components/billing-info"; import { DashboardHeader } from "@/components/dashboard/header"; import { DashboardShell } from "@/components/dashboard/shell"; import { Icons } from "@/components/shared/icons"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { getCurrentUser } from "@/lib/session"; +import { getUserSubscriptionPlan } from "@/lib/subscription"; +import { constructMetadata } from "@/lib/utils"; -export const metadata = { - title: "Billing", +export const metadata = constructMetadata({ + title: "Billing – SaaS Starter", description: "Manage billing and your subscription plan.", -}; +}); export default async function BillingPage() { const user = await getCurrentUser(); diff --git a/app/(dashboard)/dashboard/layout.tsx b/app/(dashboard)/dashboard/layout.tsx index 17e5dbf1..d93b6521 100644 --- a/app/(dashboard)/dashboard/layout.tsx +++ b/app/(dashboard)/dashboard/layout.tsx @@ -2,6 +2,7 @@ import { dashboardConfig } from "@/config/dashboard"; import { DashboardNav } from "@/components/layout/nav"; import { NavBar } from "@/components/layout/navbar"; import { SiteFooter } from "@/components/layout/site-footer"; +import MaxWidthWrapper from "@/components/shared/max-width-wrapper"; interface DashboardLayoutProps { children?: React.ReactNode; @@ -12,14 +13,16 @@ export default function DashboardLayout({ children }: DashboardLayoutProps) {
-
- -
- {children} -
-
+ +
+ +
+ {children} +
+
+
); diff --git a/app/(dashboard)/dashboard/page.tsx b/app/(dashboard)/dashboard/page.tsx index 38877b08..18b8eb7b 100644 --- a/app/(dashboard)/dashboard/page.tsx +++ b/app/(dashboard)/dashboard/page.tsx @@ -1,20 +1,22 @@ -import { redirect } from "next/navigation" +import { redirect } from "next/navigation"; -import { getCurrentUser } from "@/lib/session" -import { EmptyPlaceholder } from "@/components/shared/empty-placeholder" -import { DashboardHeader } from "@/components/dashboard/header" -import { DashboardShell } from "@/components/dashboard/shell" -import { Button } from "@/components/ui/button" +import { DashboardHeader } from "@/components/dashboard/header"; +import { DashboardShell } from "@/components/dashboard/shell"; +import { EmptyPlaceholder } from "@/components/shared/empty-placeholder"; +import { Button } from "@/components/ui/button"; +import { getCurrentUser } from "@/lib/session"; +import { constructMetadata } from "@/lib/utils"; -export const metadata = { - title: "Dashboard", -} +export const metadata = constructMetadata({ + title: "Settings – SaaS Starter", + description: "Overview of your account and activities.", +}); export default async function DashboardPage() { - const user = await getCurrentUser() + const user = await getCurrentUser(); if (!user) { - redirect("/login") + redirect("/login"); } return ( @@ -33,5 +35,5 @@ export default async function DashboardPage() { - ) + ); } diff --git a/app/(dashboard)/dashboard/settings/page.tsx b/app/(dashboard)/dashboard/settings/page.tsx index b3b7a751..299d565b 100644 --- a/app/(dashboard)/dashboard/settings/page.tsx +++ b/app/(dashboard)/dashboard/settings/page.tsx @@ -1,20 +1,21 @@ -import { redirect } from "next/navigation" +import { redirect } from "next/navigation"; -import { getCurrentUser } from "@/lib/session" -import { DashboardHeader } from "@/components/dashboard/header" -import { DashboardShell } from "@/components/dashboard/shell" -import { UserNameForm } from "@/components/forms/user-name-form" +import { DashboardHeader } from "@/components/dashboard/header"; +import { DashboardShell } from "@/components/dashboard/shell"; +import { UserNameForm } from "@/components/forms/user-name-form"; +import { getCurrentUser } from "@/lib/session"; +import { constructMetadata } from "@/lib/utils"; -export const metadata = { - title: "Settings", - description: "Manage account and website settings.", -} +export const metadata = constructMetadata({ + title: "Settings – SaaS Starter", + description: "Configure your account and website settings.", +}); export default async function SettingsPage() { - const user = await getCurrentUser() + const user = await getCurrentUser(); if (!user) { - redirect("/login") + redirect("/login"); } return ( @@ -27,5 +28,5 @@ export default async function SettingsPage() { - ) + ); } diff --git a/app/(docs)/layout.tsx b/app/(docs)/layout.tsx index 2264bec6..fa6ebe1c 100644 --- a/app/(docs)/layout.tsx +++ b/app/(docs)/layout.tsx @@ -32,7 +32,7 @@ const rightHeader = () => ( export default function DocsLayout({ children }: DocsLayoutProps) { return (
- +
{children}
diff --git a/app/(marketing)/blog/page.tsx b/app/(marketing)/blog/page.tsx index 13fe297f..0d073bcd 100644 --- a/app/(marketing)/blog/page.tsx +++ b/app/(marketing)/blog/page.tsx @@ -1,22 +1,24 @@ -import { allPosts } from "contentlayer/generated" -import { compareDesc } from "date-fns" +import { allPosts } from "contentlayer/generated"; +import { compareDesc } from "date-fns"; -import { BlogPosts } from "@/components/blog-posts" +import { BlogPosts } from "@/components/blog-posts"; +import { constructMetadata } from "@/lib/utils"; -export const metadata = { - title: "Blog", -} +export const metadata = constructMetadata({ + title: "Blog – SaaS Starter", + description: "Manage billing and your subscription plan.", +}); export default async function BlogPage() { const posts = allPosts .filter((post) => post.published) .sort((a, b) => { - return compareDesc(new Date(a.date), new Date(b.date)) - }) + return compareDesc(new Date(a.date), new Date(b.date)); + }); return (
- ) + ); } diff --git a/app/(marketing)/pricing/page.tsx b/app/(marketing)/pricing/page.tsx index 673ce832..e57cf656 100644 --- a/app/(marketing)/pricing/page.tsx +++ b/app/(marketing)/pricing/page.tsx @@ -1,27 +1,27 @@ +import { PricingCards } from "@/components/pricing-cards"; +import { PricingFaq } from "@/components/pricing-faq"; +import { getCurrentUser } from "@/lib/session"; +import { getUserSubscriptionPlan } from "@/lib/subscription"; +import { constructMetadata } from "@/lib/utils"; -import { PricingCards } from '@/components/pricing-cards'; -import { PricingFaq } from '@/components/pricing-faq'; -import { Skeleton } from '@/components/ui/skeleton'; -import { getCurrentUser } from '@/lib/session'; -import { getUserSubscriptionPlan } from '@/lib/subscription'; - -export const metadata = { - title: "Pricing", -} +export const metadata = constructMetadata({ + title: "Pricing – SaaS Starter", + description: "Explore our subscription plans.", +}); export default async function PricingPage() { - const user = await getCurrentUser() + const user = await getCurrentUser(); let subscriptionPlan; if (user) { - subscriptionPlan = await getUserSubscriptionPlan(user.id) + subscriptionPlan = await getUserSubscriptionPlan(user.id); } return (
-
+
- ) -} \ No newline at end of file + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 816db55f..af18c6f9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,8 +4,7 @@ import { fontHeading, fontSans, fontUrban } from "@/assets/fonts"; import { SessionProvider } from "next-auth/react"; import { ThemeProvider } from "next-themes"; -import { siteConfig } from "@/config/site"; -import { cn } from "@/lib/utils"; +import { cn, constructMetadata } from "@/lib/utils"; import { Toaster } from "@/components/ui/toaster"; import { Analytics } from "@/components/analytics"; import { ModalProvider } from "@/components/modal-provider"; @@ -15,52 +14,7 @@ interface RootLayoutProps { children: React.ReactNode; } -export const metadata = { - title: { - default: siteConfig.name, - template: `%s | ${siteConfig.name}`, - }, - description: siteConfig.description, - keywords: [ - "Next.js", - "React", - "Prisma", - "Neon", - "Auth.js", - "shadcn ui", - "Resend", - "React Email", - "Stripe", - ], - authors: [ - { - name: "mickasmt", - }, - ], - creator: "mickasmt", - metadataBase: new URL(siteConfig.url), - openGraph: { - type: "website", - locale: "en_US", - url: siteConfig.url, - title: siteConfig.name, - description: siteConfig.description, - siteName: siteConfig.name, - }, - twitter: { - card: "summary_large_image", - title: siteConfig.name, - description: siteConfig.description, - images: [siteConfig.ogImage], - creator: "@miickasmt", - }, - icons: { - icon: "/favicon.ico", - shortcut: "/favicon-16x16.png", - apple: "/apple-touch-icon.png", - }, - manifest: `${siteConfig.url}/site.webmanifest`, -}; +export const metadata = constructMetadata(); export default function RootLayout({ children }: RootLayoutProps) { return ( diff --git a/components/blog-posts.tsx b/components/blog-posts.tsx index 0f4d21fb..9b025e74 100644 --- a/components/blog-posts.tsx +++ b/components/blog-posts.tsx @@ -1,10 +1,13 @@ -import { formatDate } from '@/lib/utils'; -import Image from 'next/image'; -import Link from 'next/link'; +import Image from "next/image"; +import Link from "next/link"; + +import { formatDate } from "@/lib/utils"; + +import MaxWidthWrapper from "./shared/max-width-wrapper"; export function BlogPosts({ posts }) { return ( -
+

Last Post

@@ -39,7 +42,10 @@ export function BlogPosts({ posts }) {

Blog Posts

{posts.slice(1).map((post) => ( -
+
{post.image && ( {post.title} )} -

{post.title}

+

+ {post.title} +

{post.description && ( -

{post.description}

+

+ {post.description} +

)} {post.date && (

@@ -65,7 +75,6 @@ export function BlogPosts({ posts }) { ))}

-
+ ); } - diff --git a/lib/utils.ts b/lib/utils.ts index ab9c0da2..571a20be 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,24 +1,86 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { Metadata } from "next"; +import { clsx, type ClassValue } from "clsx"; import ms from "ms"; +import { twMerge } from "tailwind-merge"; -import { env } from "@/env.mjs" +import { env } from "@/env.mjs"; +import { siteConfig } from "@/config/site"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); +} + +export function constructMetadata({ + title = siteConfig.name, + description = siteConfig.description, + image = siteConfig.ogImage, + icons = "/favicon.ico", + noIndex = false, +}: { + title?: string; + description?: string; + image?: string; + icons?: string; + noIndex?: boolean; +} = {}): Metadata { + return { + title, + description, + keywords: [ + "Next.js", + "React", + "Prisma", + "Neon", + "Auth.js", + "shadcn ui", + "Resend", + "React Email", + "Stripe", + ], + authors: [ + { + name: "mickasmt", + }, + ], + creator: "mickasmt", + openGraph: { + type: "website", + locale: "en_US", + url: siteConfig.url, + title, + description, + siteName: title, + }, + twitter: { + card: "summary_large_image", + title, + description, + images: [image], + creator: "@miickasmt", + }, + icons, + metadataBase: new URL(siteConfig.url), + manifest: `${siteConfig.url}/site.webmanifest`, + ...(noIndex && { + robots: { + index: false, + follow: false, + }, + }), + }; } export function formatDate(input: string | number): string { - const date = new Date(input) + const date = new Date(input); return date.toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric", - }) + }); } export function absoluteUrl(path: string) { - return `${env.NEXT_PUBLIC_APP_URL}${path}` + return `${env.NEXT_PUBLIC_APP_URL}${path}`; } // Utils from precedent.dev @@ -82,4 +144,4 @@ export function capitalize(str: string) { export const truncate = (str: string, length: number) => { if (!str || str.length <= length) return str; return `${str.slice(0, length)}...`; -}; \ No newline at end of file +};