Skip to content

Commit 0c39ff5

Browse files
committed
feat: Add error and loading pages, refactor font loading to next/font/local, enhance SEO metadata, and improve admin loading and access denied UI.
1 parent 7189639 commit 0c39ff5

5 files changed

Lines changed: 147 additions & 30 deletions

File tree

app/admin/layout.tsx

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import { useRouter } from "next/navigation"
66
import AdminSidebar from "@/components/admin-sidebar"
77
import { useAuth } from "@/lib/auth-context"
88

9+
import { Skeleton } from "@/components/ui/skeleton"
10+
import { Button } from "@/components/ui/button"
11+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
12+
import { Lock } from "lucide-react"
13+
914
export default function AdminLayout({
1015
children,
1116
}: {
@@ -24,16 +29,52 @@ export default function AdminLayout({
2429

2530
if (isLoading) {
2631
return (
27-
<div className="flex items-center justify-center h-screen text-white">
28-
<p>Loading...</p>
32+
<div className="flex h-screen w-full bg-background">
33+
{/* Sidebar Skeleton */}
34+
<div className="hidden w-60 border-r md:block p-4 space-y-4">
35+
<Skeleton className="h-8 w-32" />
36+
<div className="space-y-2 mt-8">
37+
<Skeleton className="h-10 w-full" />
38+
<Skeleton className="h-10 w-full" />
39+
<Skeleton className="h-10 w-full" />
40+
<Skeleton className="h-10 w-full" />
41+
</div>
42+
</div>
43+
{/* Content Skeleton */}
44+
<div className="flex-1 p-8 space-y-4">
45+
<Skeleton className="h-8 w-48 mb-8" />
46+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
47+
<Skeleton className="h-32 rounded-xl" />
48+
<Skeleton className="h-32 rounded-xl" />
49+
<Skeleton className="h-32 rounded-xl" />
50+
</div>
51+
<Skeleton className="h-96 w-full rounded-xl mt-8" />
52+
</div>
2953
</div>
3054
)
3155
}
3256

3357
if (!user || !["super", "admin", "manager"].includes(userRole || "")) {
3458
return (
35-
<div className="flex items-center justify-center h-screen text-white">
36-
<p>Access Denied</p>
59+
<div className="flex h-screen w-full items-center justify-center bg-background p-4">
60+
<Card className="w-full max-w-md">
61+
<CardHeader className="text-center">
62+
<div className="flex justify-center mb-4">
63+
<div className="p-3 rounded-full bg-destructive/10">
64+
<Lock className="h-6 w-6 text-destructive" />
65+
</div>
66+
</div>
67+
<CardTitle>Access Denied</CardTitle>
68+
<CardDescription>
69+
You do not have permission to access the admin area.
70+
</CardDescription>
71+
</CardHeader>
72+
<CardContent className="flex justify-center">
73+
<Button variant="outline" onClick={() => router.replace("/")}>
74+
Return to Home
75+
</Button>
76+
</CardContent>
77+
</Card>
3778
</div>
3879
)
3980
}

app/error.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client"
2+
3+
import { useEffect } from "react"
4+
import { Button } from "@/components/ui/button"
5+
import { AlertCircle } from "lucide-react"
6+
7+
export default function Error({
8+
error,
9+
reset,
10+
}: {
11+
error: Error & { digest?: string }
12+
reset: () => void
13+
}) {
14+
useEffect(() => {
15+
// Log the error to an error reporting service
16+
console.error(error)
17+
}, [error])
18+
19+
return (
20+
<div className="flex h-screen w-full flex-col items-center justify-center gap-4 bg-background text-foreground">
21+
<div className="flex flex-col items-center gap-2 text-center">
22+
<AlertCircle className="h-10 w-10 text-destructive" />
23+
<h2 className="text-2xl font-bold tracking-tight">Something went wrong!</h2>
24+
<p className="text-muted-foreground">{error.message || "An unexpected error occurred."}</p>
25+
</div>
26+
<Button onClick={() => reset()}>Try again</Button>
27+
</div>
28+
)
29+
}

app/globals.css

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
@import url("https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-dark.min.css");
22
@import "tailwindcss";
33

4+
@theme {
5+
--font-sans: var(--font-general-sans), ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6+
}
7+
48
iframe[aria-hidden="true"] {
59
display: none !important;
610
}
@@ -75,36 +79,44 @@ iframe[aria-hidden="true"] {
7579
font-weight: 600;
7680
line-height: 2.4rem;
7781
}
82+
7883
.podcast-title {
7984
font-weight: 600;
8085
font-size: clamp(1rem, 0.8997rem + 3.3439vw, 6.25rem);
8186
text-transform: uppercase;
8287
}
88+
8389
.text-blog-subheading {
8490
font-weight: 600;
8591
font-size: clamp(3rem, 2.7857rem + 1.0714vw, 4.5rem);
8692
}
93+
8794
.text-blog-quote {
8895
font-weight: 600;
8996
font-size: clamp(2rem, 1.8571rem + 0.7143vw, 3rem);
9097
}
98+
9199
.text-blog-summary {
92100
font-weight: 500;
93101
font-size: 1.375rem;
94102
}
103+
95104
.text-subheading {
96105
font-weight: 600;
97106
/* font-size: clamp(3.25rem, 2.4853rem + 3.8235vw, 6.5rem); */
98107
font-size: clamp(2.25rem, 1.6429rem + 3.0357vw, 6.5rem);
99108
text-transform: uppercase;
100109
line-height: 1;
101110
}
111+
102112
.text-subtitle {
103113
line-height: 1;
104114
}
115+
105116
.text-footer-title {
106117
color: #ffffff;
107118
}
119+
108120
.text-subtitle,
109121
.text-footer-title {
110122
font-size: clamp(2.625rem, 1.9917rem + 3.1667vw, 5rem);
@@ -113,29 +125,7 @@ iframe[aria-hidden="true"] {
113125
}
114126
}
115127

116-
@font-face {
117-
font-family: "General Sans";
118-
font-weight: 400;
119-
font-style: normal;
120-
src: url("/fonts/general-sans/GeneralSans-Regular.woff2"), format("woff2");
121-
font-display: swap;
122-
}
123128

124-
@font-face {
125-
font-family: "General Sans";
126-
font-weight: 500;
127-
font-style: normal;
128-
src: url("/fonts/general-sans/GeneralSans-Medium.woff2"), format("woff2");
129-
font-display: swap;
130-
}
131-
132-
@font-face {
133-
font-family: "General Sans";
134-
font-weight: 600;
135-
font-style: normal;
136-
src: url("/fonts/general-sans/GeneralSans-Semibold.woff2"), format("woff2");
137-
font-display: swap;
138-
}
139129

140130
/* Text selection */
141131
::selection {
@@ -425,6 +415,7 @@ iframe {
425415
margin-bottom: 4rem;
426416
margin-top: 4rem;
427417
}
418+
428419
@media (max-width: 768px) {
429420
iframe {
430421
height: 14rem !important;
@@ -453,5 +444,4 @@ iframe {
453444
.overflow-x-auto {
454445
-webkit-overflow-scrolling: touch;
455446
}
456-
}
457-
447+
}

app/layout.tsx

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,61 @@
11
import type React from "react"
22
import "./globals.css"
33
import type { Metadata } from "next"
4+
import localFont from "next/font/local"
45
import { Toaster } from "@/components/ui/toaster"
56
import { Providers } from "./providers"
67

8+
const generalSans = localFont({
9+
src: [
10+
{
11+
path: "../public/fonts/general-sans/GeneralSans-Regular.woff2",
12+
weight: "400",
13+
style: "normal",
14+
},
15+
{
16+
path: "../public/fonts/general-sans/GeneralSans-Medium.woff2",
17+
weight: "500",
18+
style: "normal",
19+
},
20+
{
21+
path: "../public/fonts/general-sans/GeneralSans-Semibold.woff2",
22+
weight: "600",
23+
style: "normal",
24+
},
25+
],
26+
variable: "--font-general-sans",
27+
display: "swap",
28+
})
29+
730
export const metadata: Metadata = {
8-
title: "L.A.P CMS",
31+
metadataBase: new URL("https://lap-cms.vercel.app"),
32+
title: {
33+
default: "L.A.P CMS",
34+
template: "%s | L.A.P CMS",
35+
},
936
description: "Getting things done on L.A.P Docs",
1037
generator: "v0.dev",
38+
openGraph: {
39+
title: "L.A.P CMS",
40+
description: "Getting things done on L.A.P Docs",
41+
url: "https://lap-cms.vercel.app", // Assuming a URL or leaving generic
42+
siteName: "L.A.P CMS",
43+
images: [
44+
{
45+
url: "/logos/LAP-Logo-Color.png",
46+
width: 800,
47+
height: 600,
48+
},
49+
],
50+
locale: "en_US",
51+
type: "website",
52+
},
53+
twitter: {
54+
card: "summary_large_image",
55+
title: "L.A.P CMS",
56+
description: "Getting things done on L.A.P Docs",
57+
images: ["/logos/LAP-Logo-Color.png"],
58+
},
1159
}
1260

1361
export const viewport = {
@@ -29,7 +77,7 @@ export default function RootLayout({
2977
<link rel="icon" href="/logos/LAP-Logo-Color.png" type="image/x-icon" />
3078
<meta name="robots" content="noindex, nofollow" />
3179
</head>
32-
<body>
80+
<body className={`${generalSans.variable} font-sans`}>
3381
<Providers>
3482
{children}
3583
<Toaster />

app/loading.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Loader2 } from "lucide-react"
2+
3+
export default function Loading() {
4+
return (
5+
<div className="flex h-screen w-full items-center justify-center bg-background">
6+
<Loader2 className="h-10 w-10 animate-spin text-primary" />
7+
</div>
8+
)
9+
}

0 commit comments

Comments
 (0)