Skip to content

Commit eef2a26

Browse files
committed
fix: goofy dominik broke blogs
1 parent aa398b7 commit eef2a26

File tree

2 files changed

+128
-45
lines changed

2 files changed

+128
-45
lines changed

apps/docs/app/(home)/blog/[slug]/page.tsx

Lines changed: 105 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import {
44
CalendarIcon,
55
ClockIcon,
66
UserIcon,
7+
WarningCircleIcon,
78
} from '@phosphor-icons/react/ssr';
89
import type { Metadata } from 'next';
910
import Image from 'next/image';
1011
import Link from 'next/link';
11-
import { notFound } from 'next/navigation';
1212
import { SITE_URL } from '@/app/util/constants';
1313
import { Footer } from '@/components/footer';
14+
import { SciFiButton } from '@/components/landing/scifi-btn';
1415
import { Prose } from '@/components/prose';
1516
import { getPosts, getSinglePost } from '@/lib/blog-query';
1617

@@ -36,61 +37,122 @@ export async function generateMetadata({
3637
}: PageProps): Promise<Metadata> {
3738
const slug = (await params).slug;
3839

39-
const data = await getSinglePost(slug);
40+
try {
41+
const data = await getSinglePost(slug);
42+
if (!data?.post) {
43+
return { title: 'Not Found | Databuddy' };
44+
}
4045

41-
if (!data?.post) {
42-
return notFound();
43-
}
44-
45-
return {
46-
title: `${data.post.title} | Databuddy`,
47-
description: data.post.description,
48-
twitter: {
49-
title: `${data.post.title} | Databuddy`,
50-
description: data.post.description,
51-
card: 'summary_large_image',
52-
images: [
53-
{
54-
url: data.post.coverImage ?? `${SITE_URL}/og.webp`,
55-
width: '1200',
56-
height: '630',
57-
alt: data.post.title,
58-
},
59-
],
60-
},
61-
openGraph: {
46+
return {
6247
title: `${data.post.title} | Databuddy`,
6348
description: data.post.description,
64-
type: 'article',
65-
images: [
66-
{
67-
url: data.post.coverImage ?? `${SITE_URL}/og.webp`,
68-
width: '1200',
69-
height: '630',
70-
alt: data.post.title,
71-
},
72-
],
73-
publishedTime: new Date(data.post.publishedAt).toISOString(),
74-
authors: [
75-
...data.post.authors.map((author: { name: string }) => author.name),
76-
],
77-
},
78-
};
49+
twitter: {
50+
title: `${data.post.title} | Databuddy`,
51+
description: data.post.description,
52+
card: 'summary_large_image',
53+
images: [
54+
{
55+
url: data.post.coverImage ?? `${SITE_URL}/og.webp`,
56+
width: '1200',
57+
height: '630',
58+
alt: data.post.title,
59+
},
60+
],
61+
},
62+
openGraph: {
63+
title: `${data.post.title} | Databuddy`,
64+
description: data.post.description,
65+
type: 'article',
66+
images: [
67+
{
68+
url: data.post.coverImage ?? `${SITE_URL}/og.webp`,
69+
width: '1200',
70+
height: '630',
71+
alt: data.post.title,
72+
},
73+
],
74+
publishedTime: new Date(data.post.publishedAt).toISOString(),
75+
authors: [
76+
...data.post.authors.map((author: { name: string }) => author.name),
77+
],
78+
},
79+
};
80+
} catch {
81+
return { title: 'Not Found | Databuddy' };
82+
}
7983
}
8084

8185
export default async function PostPage({
8286
params,
8387
}: {
8488
params: Promise<{ slug: string }>;
8589
}) {
86-
const slug = (await params).slug;
87-
88-
const { post } = await getSinglePost(slug);
90+
const { slug } = await params;
91+
const result = (await getSinglePost(slug)) as {
92+
post?: import('@/types/post').Post;
93+
error?: boolean;
94+
status?: number;
95+
statusText?: string;
96+
};
8997

90-
if (!post) {
91-
return notFound();
98+
if (!result?.post) {
99+
return (
100+
<>
101+
<div className="relative flex min-h-[60vh] w-full items-center justify-center overflow-hidden px-4 pt-10 sm:px-6 sm:pt-12 lg:px-8">
102+
{/* Main Content */}
103+
<div className="relative z-10 mx-auto w-full max-w-lg text-center">
104+
<div className="group relative">
105+
<div className="relative rounded border border-border bg-card/50 p-8 backdrop-blur-sm transition-all duration-300 hover:border-border/80 hover:bg-card/70 sm:p-12">
106+
<WarningCircleIcon
107+
className="mx-auto mb-4 h-12 w-12 text-muted-foreground transition-colors duration-300 group-hover:text-foreground sm:h-16 sm:w-16"
108+
weight="duotone"
109+
/>
110+
<h1 className="mb-3 text-balance font-semibold text-2xl leading-tight tracking-tight sm:text-3xl md:text-4xl">
111+
Post Not Found
112+
</h1>
113+
<p className="mb-6 font-medium text-muted-foreground text-sm leading-relaxed tracking-tight sm:text-base">
114+
The article you're looking for seems to have been moved or no
115+
longer exists.
116+
</p>
117+
<div className="flex flex-col gap-3 sm:flex-row sm:justify-center">
118+
<SciFiButton asChild className="flex-1 sm:flex-initial">
119+
<Link aria-label="Back to blog" href="/blog">
120+
<ArrowLeftIcon className="h-4 w-4" weight="fill" />
121+
Back to Blog
122+
</Link>
123+
</SciFiButton>
124+
</div>
125+
</div>
126+
127+
{/* Sci-fi corners */}
128+
<div className="pointer-events-none absolute inset-0">
129+
<div className="absolute top-0 left-0 h-2 w-2 group-hover:animate-[cornerGlitch_0.6s_ease-in-out]">
130+
<div className="absolute top-0 left-0.5 h-0.5 w-1.5 origin-left bg-foreground" />
131+
<div className="absolute top-0 left-0 h-2 w-0.5 origin-top bg-foreground" />
132+
</div>
133+
<div className="-scale-x-[1] absolute top-0 right-0 h-2 w-2 group-hover:animate-[cornerGlitch_0.6s_ease-in-out]">
134+
<div className="absolute top-0 left-0.5 h-0.5 w-1.5 origin-left bg-foreground" />
135+
<div className="absolute top-0 left-0 h-2 w-0.5 origin-top bg-foreground" />
136+
</div>
137+
<div className="-scale-y-[1] absolute bottom-0 left-0 h-2 w-2 group-hover:animate-[cornerGlitch_0.6s_ease-in-out]">
138+
<div className="absolute top-0 left-0.5 h-0.5 w-1.5 origin-left bg-foreground" />
139+
<div className="absolute top-0 left-0 h-2 w-0.5 origin-top bg-foreground" />
140+
</div>
141+
<div className="-scale-[1] absolute right-0 bottom-0 h-2 w-2 group-hover:animate-[cornerGlitch_0.6s_ease-in-out]">
142+
<div className="absolute top-0 left-0.5 h-0.5 w-1.5 origin-left bg-foreground" />
143+
<div className="absolute top-0 left-0 h-2 w-0.5 origin-top bg-foreground" />
144+
</div>
145+
</div>
146+
</div>
147+
</div>
148+
</div>
149+
<Footer />
150+
</>
151+
);
92152
}
93153

154+
const post = result.post;
155+
94156
const estimateReadingTime = (htmlContent: string): string => {
95157
const text = htmlContent.replace(STRIP_HTML_REGEX, ' ');
96158
const words = text.trim().split(WORD_SPLIT_REGEX).filter(Boolean).length;

apps/docs/lib/blog-query.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,38 @@ import type {
88
MarbleTagList,
99
} from '@/types/post';
1010

11-
async function fetchFromMarble<T>(endpoint: string): Promise<T> {
11+
type FetchError = { error: true; status: number; statusText: string };
12+
13+
async function fetchFromMarble<T>(
14+
endpoint: string,
15+
options?: { returnStatusOnError?: boolean }
16+
): Promise<T | FetchError> {
1217
try {
1318
const response = await fetch(
1419
`${process.env.MARBLE_API_URL}/${process.env.MARBLE_WORKSPACE_KEY}/${endpoint}`
1520
);
1621
if (!response.ok) {
22+
if (options?.returnStatusOnError) {
23+
return {
24+
error: true,
25+
status: response.status,
26+
statusText: response.statusText,
27+
};
28+
}
1729
throw new Error(
1830
`Failed to fetch ${endpoint}: ${response.status} ${response.statusText}`
1931
);
2032
}
2133
return (await response.json()) as T;
2234
} catch (error) {
2335
console.error(`Error fetching ${endpoint}:`, error);
36+
if (options?.returnStatusOnError) {
37+
return {
38+
error: true,
39+
status: 500,
40+
statusText: 'Internal Error',
41+
};
42+
}
2443
throw error;
2544
}
2645
}
@@ -34,7 +53,9 @@ export const getTags = cache(() => {
3453
});
3554

3655
export const getSinglePost = cache((slug: string) => {
37-
return fetchFromMarble<MarblePost>(`posts/${slug}`);
56+
return fetchFromMarble<MarblePost>(`posts/${slug}`, {
57+
returnStatusOnError: true,
58+
});
3859
});
3960

4061
export const getCategories = cache(() => {

0 commit comments

Comments
 (0)