Skip to content

Commit 2a1fcc5

Browse files
author
Rajat Saxena
committed
Blog post is fetched on server side; Breadcrumb UI fix; featured image added back
1 parent 555be36 commit 2a1fcc5

3 files changed

Lines changed: 158 additions & 15 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use client";
2+
3+
import { TextRenderer } from "@courselit/components-library";
4+
5+
export default function ClientSideTextRenderer({ json }: { json: any }) {
6+
return <TextRenderer json={json} showTableOfContent={true} />;
7+
}
Lines changed: 134 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,149 @@
1-
"use client";
2-
3-
import { useContext } from "react";
41
import { Course } from "@courselit/common-models";
5-
import { Section } from "@courselit/page-primitives";
6-
import Post from "./post";
7-
import { ThemeContext } from "@components/contexts";
2+
import { Caption, Header1, Section, Text1 } from "@courselit/page-primitives";
3+
import { formattedLocaleDate, getFullSiteSetup } from "@ui-lib/utils";
4+
import { getAddressFromHeaders } from "@ui-lib/utils";
5+
import { headers } from "next/headers";
6+
import { ProductWithAdminProps } from "@/hooks/use-product";
7+
import { FetchBuilder } from "@courselit/utils";
8+
import { Text2 } from "@courselit/page-primitives";
9+
import Link from "next/link";
10+
import { truncate } from "@ui-lib/utils";
11+
import Image from "next/image";
12+
import ClientSideTextRenderer from "./client-side-text-renderer";
13+
import {
14+
Breadcrumb,
15+
BreadcrumbItem,
16+
BreadcrumbLink,
17+
BreadcrumbList,
18+
BreadcrumbPage,
19+
BreadcrumbSeparator,
20+
} from "@components/ui/breadcrumb";
821

9-
export default function BlogPost({
22+
export default async function ProductPage({
1023
params,
1124
}: {
1225
params: { slug: string; id: string };
1326
course: Course;
1427
}) {
15-
const { theme } = useContext(ThemeContext);
28+
const address = getAddressFromHeaders(headers);
29+
const siteInfo = await getFullSiteSetup(address);
30+
if (!siteInfo) {
31+
return null;
32+
}
33+
34+
const { theme } = siteInfo;
35+
const product = await getProduct(address, params.id);
36+
if (!product) {
37+
return null;
38+
}
1639

1740
return (
1841
<Section theme={theme.theme}>
1942
<div className="flex flex-col gap-4 max-w-[640px] mx-auto">
20-
<Post courseId={params.id} />
43+
<Breadcrumb className="mb-4">
44+
<BreadcrumbList>
45+
<BreadcrumbItem>
46+
<BreadcrumbLink asChild>
47+
<Text2
48+
className="cursor-pointer"
49+
theme={theme.theme}
50+
>
51+
<Link href="/blog">Blog</Link>
52+
</Text2>
53+
</BreadcrumbLink>
54+
</BreadcrumbItem>
55+
<BreadcrumbSeparator />
56+
<BreadcrumbItem>
57+
<BreadcrumbPage>
58+
<Text2 theme={theme.theme}>
59+
{truncate(product?.title, 20)}
60+
</Text2>
61+
</BreadcrumbPage>
62+
</BreadcrumbItem>
63+
</BreadcrumbList>
64+
</Breadcrumb>
65+
<Header1 theme={theme.theme}>{product?.title}</Header1>
66+
<div className="flex items-center gap-4 mb-6">
67+
<Image
68+
src={
69+
product?.user?.avatar?.file ||
70+
"/courselit_backdrop_square.webp"
71+
}
72+
alt={product?.user?.name || ""}
73+
width={32}
74+
height={32}
75+
className="rounded-full aspect-square"
76+
/>
77+
<div className="flex items-center gap-2">
78+
<Text2 theme={theme.theme}>
79+
{truncate(product?.user?.name, 50)}
80+
</Text2>
81+
<Caption theme={theme.theme}>
82+
· {formattedLocaleDate(product?.updatedAt, "long")}
83+
</Caption>
84+
</div>
85+
</div>
86+
{product?.featuredImage && (
87+
<div className="mb-4 border rounded overflow-hidden">
88+
<Image
89+
alt={product.featuredImage.caption || ""}
90+
src={product.featuredImage.file!}
91+
width={640}
92+
height={360}
93+
/>
94+
</div>
95+
)}
96+
{product?.description && (
97+
<Text1 theme={theme.theme} component="span">
98+
<ClientSideTextRenderer
99+
json={JSON.parse(product.description)}
100+
/>
101+
</Text1>
102+
)}
21103
</div>
22104
</Section>
23105
);
24106
}
107+
108+
export const getProduct = async (
109+
backend: string,
110+
id: string,
111+
): Promise<ProductWithAdminProps | undefined> => {
112+
const query = `
113+
query ($id: String!) {
114+
course: getCourse(id: $id) {
115+
title
116+
description
117+
type
118+
slug
119+
courseId
120+
featuredImage {
121+
file
122+
thumbnail
123+
caption
124+
},
125+
updatedAt
126+
user {
127+
name
128+
avatar {
129+
file
130+
thumbnail
131+
caption
132+
}
133+
}
134+
}
135+
}
136+
`;
137+
const fetch = new FetchBuilder()
138+
.setUrl(`${backend}/api/graph`)
139+
.setPayload({ query, variables: { id } })
140+
.setIsGraphQLEndpoint(true)
141+
.build();
142+
143+
try {
144+
const response = await fetch.exec();
145+
return response.course;
146+
} catch (e: any) {
147+
console.log("Error fetching product", e.message); // eslint-disable-line no-console
148+
}
149+
};

apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/post.tsx renamed to apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/post.tsx.notused

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useProduct from "@/hooks/use-product";
1313
import { AddressContext, ThemeContext } from "@components/contexts";
1414
import { useContext } from "react";
1515
import Link from "next/link";
16+
import { Image as CourselitImage } from "@courselit/components-library";
1617

1718
export default function Post({ courseId }: { courseId: string }) {
1819
const address = useContext(AddressContext);
@@ -39,22 +40,32 @@ export default function Post({ courseId }: { courseId: string }) {
3940
<div className="flex items-center gap-4 mb-6">
4041
<Image
4142
src={
42-
post.featuredImage?.file ||
43+
post?.user?.avatar?.file ||
4344
"/courselit_backdrop_square.webp"
4445
}
45-
alt={post.featuredImage?.caption || ""}
46+
alt={post?.user?.name || ""}
4647
width={32}
4748
height={32}
48-
className="rounded-full"
49+
className="rounded-full aspect-square"
4950
/>
5051
<div className="flex items-center gap-2">
51-
<Text2 theme={theme.theme}>{post.creatorName}</Text2>
52+
<Text2 theme={theme.theme}>{post?.creatorName}</Text2>
5253
<Caption theme={theme.theme}>
53-
· {formattedLocaleDate(post.updatedAt, "long")}
54+
· {formattedLocaleDate(post?.updatedAt, "long")}
5455
</Caption>
5556
</div>
5657
</div>
57-
{post.description && (
58+
{post?.featuredImage && (
59+
<div className="mb-4 border rounded overflow-hidden">
60+
<CourselitImage
61+
alt={post.featuredImage.caption || ""}
62+
src={post.featuredImage.file!}
63+
loading="eager"
64+
sizes="50vw"
65+
/>
66+
</div>
67+
)}
68+
{post?.description && (
5869
<Text1 theme={theme.theme}>
5970
<TextRenderer
6071
json={JSON.parse(post.description)}

0 commit comments

Comments
 (0)