Skip to content

Commit 8b110a9

Browse files
authored
chore: adding jsonld to questions page (#399)
2 parents 58e04e2 + 4602ca5 commit 8b110a9

File tree

34 files changed

+734
-266
lines changed

34 files changed

+734
-266
lines changed

next.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ const nextConfig = {
2929
destination: '/features/roadmap',
3030
permanent: true,
3131
},
32+
{
33+
source: '/features/daily-challenges',
34+
destination: '/features/daily-coding-challenges',
35+
permanent: true,
36+
},
3237
];
3338
},
3439
async headers() {

src/app/(app)/(default_layout)/(questions)/questions/explore/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import QuestionPageSidebar from '@/components/app/questions/layout/question-page-sidebar';
22
import QuestionsCarouselList from '@/components/app/questions/layout/carousel/question-carousel-list';
33
import Hero from '@/components/global/hero';
4-
import { createMetadata } from '@/utils';
4+
import { createMetadata } from '@/utils/seo';
55
import { Button } from '@/components/ui/button';
66
import { useUserServer } from '@/hooks/use-user-server';
77

src/app/(app)/(default_layout)/(questions)/questions/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useUserServer } from '@/hooks/use-user-server';
1111
import { validateSearchParams } from '@/utils/search-params';
1212
import { parseSearchParams } from '@/utils/search-params';
1313
import { getTags } from '@/utils/data/questions/tags/get-tags';
14-
import { createMetadata } from '@/utils';
14+
import { createMetadata } from '@/utils/seo';
1515
import { Button } from '@/components/ui/button';
1616
import LoadingQuestions from '@/components/app/questions/loading/loading-questions';
1717
import { Suspense } from 'react';

src/app/(app)/(default_layout)/(questions)/questions/previous/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useUserServer } from '@/hooks/use-user-server';
1111
import { validateSearchParams } from '@/utils/search-params';
1212
import { parseSearchParams } from '@/utils/search-params';
1313
import { getTags } from '@/utils/data/questions/tags/get-tags';
14-
import { createMetadata } from '@/utils';
14+
import { createMetadata } from '@/utils/seo';
1515
import { Button } from '@/components/ui/button';
1616

1717
export async function generateMetadata() {

src/app/(app)/(default_layout)/leaderboard/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import LeaderboardLongestStreaks from '@/components/app/leaderboard/leaderboard-
44
import LeaderboardMostQuestionsAnswered from '@/components/app/leaderboard/leaderboard-most-questions-answered';
55
import LeaderboardTodayBoard from '@/components/app/leaderboard/leaderboard-today-board';
66
import { useUserServer } from '@/hooks/use-user-server';
7-
import { createMetadata } from '@/utils';
7+
import { createMetadata } from '@/utils/seo';
88

99
export async function generateMetadata() {
1010
return createMetadata({

src/app/(app)/(questions)/question/[slug]/layout.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,33 @@ import { Separator } from '@/components/ui/separator';
55
import FeedbackButton from '@/components/ui/feedback-button';
66
import SidebarLayoutTrigger from '@/components/global/navigation/sidebar-layout-trigger';
77
import RandomQuestion from '@/components/global/random-question';
8+
import { createMetadata, getQuestionEducationLevel } from '@/utils/seo';
9+
import { capitalise, getBaseUrl } from '@/utils';
810

911
// Actions
1012
import { getQuestion } from '@/utils/data/questions/get';
13+
import { QuizJsonLd } from '@/types/Seo';
14+
15+
export async function generateMetadata({
16+
params,
17+
}: {
18+
params: { slug: string };
19+
}) {
20+
const question = await getQuestion('slug', params.slug);
21+
// get the title via slug and removing the - from the slug
22+
const title = question?.slug?.replace(/-/g, ' ') || 'Coding Question';
23+
24+
return createMetadata({
25+
title: `${capitalise(title)} | TechBlitz`,
26+
description: 'Boost your coding skills for free with TechBlitz',
27+
image: {
28+
text: `${title} | TechBlitz`,
29+
bgColor: '#000000',
30+
textColor: '#ffffff',
31+
},
32+
canonicalUrl: `/question/${params.slug}`,
33+
});
34+
}
1135

1236
export default async function QuestionUidLayout({
1337
children,
@@ -17,8 +41,43 @@ export default async function QuestionUidLayout({
1741

1842
const question = await getQuestion('slug', slug);
1943

44+
// determine the education level based on the question
45+
46+
// create json ld
47+
const jsonLd: QuizJsonLd = {
48+
'@context': 'https://schema.org',
49+
'@type': 'Quiz',
50+
// replace the - with a space and
51+
name: capitalise(question?.slug?.replace(/-/g, ' ') || ''),
52+
description: question?.question || '',
53+
url: `${getBaseUrl()}/question/${slug}`,
54+
educationLevel: getQuestionEducationLevel(question?.difficulty || 'EASY'),
55+
educationalUse: 'practice',
56+
learningResourceType: ['quiz', 'learning activity'],
57+
creator: {
58+
'@type': 'Organization',
59+
name: 'TechBlitz',
60+
url: getBaseUrl(),
61+
},
62+
assesses: ['coding'],
63+
dateCreated: new Date(question?.createdAt || '').toISOString(),
64+
dateModified: new Date(question?.updatedAt || '').toISOString(),
65+
datePublished: new Date(
66+
question?.questionDate || question?.createdAt || ''
67+
).toISOString(),
68+
headline: question?.question || '',
69+
interactivityType: 'mixed',
70+
isAccessibleForFree: true,
71+
isFamilyFriendly: true,
72+
teaches: 'coding',
73+
};
74+
2075
return (
2176
<>
77+
<script
78+
type="application/ld+json"
79+
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
80+
/>
2281
<div className="flex items-center justify-between py-2 px-6">
2382
<div className="flex items-center gap-x-5 py-2">
2483
<SidebarLayoutTrigger />

src/app/(app)/(questions)/question/[slug]/page.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,6 @@ import { getRandomQuestion } from '@/utils/data/questions/get-random';
1010
import ExpandedCodeModal from '@/components/app/questions/expanded-code-modal';
1111
import RelatedQuestions from '@/components/app/questions/single/related-question-card';
1212
import ResizableLayout from '@/components/ui/resizable-layout';
13-
import { capitalise, createMetadata } from '@/utils';
14-
15-
export async function generateMetadata({
16-
params,
17-
}: {
18-
params: { slug: string };
19-
}) {
20-
const question = await getQuestion('slug', params.slug);
21-
// get the title via slug and removing the - from the slug
22-
const title = question?.slug?.replace(/-/g, ' ') || 'Coding Question';
23-
24-
return createMetadata({
25-
title: `${capitalise(title)} | TechBlitz`,
26-
description: 'Boost your coding skills for free with TechBlitz',
27-
image: {
28-
text: `${title} | TechBlitz`,
29-
bgColor: '#000000',
30-
textColor: '#ffffff',
31-
},
32-
canonicalUrl: `/question/${params.slug}`,
33-
});
34-
}
3513

3614
export default async function TodaysQuestionPage({
3715
params,

src/app/(app)/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import '@mantine/core/styles.css';
1010
import '@mantine/dates/styles.css';
1111

1212
import NextTopLoader from 'nextjs-toploader';
13-
import { createMetadata } from '@/utils';
13+
import { createMetadata } from '@/utils/seo';
1414
import { useUserServer } from '@/hooks/use-user-server';
1515
import { getTodaysQuestion } from '@/utils/data/questions/get-today';
1616
import { userAnsweredDailyQuestion } from '@/utils/data/questions/user-answered-daily-question';

src/app/(marketing)/blog/[slug]/page.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Link from 'next/link';
55
import { ChevronLeft } from 'lucide-react';
66

77
import { getBlogPost, getBlogPosts } from '@/lib/blog';
8-
import { createMetadata } from '@/utils';
8+
import { createMetadata } from '@/utils/seo';
99
import { Button } from '@/components/ui/button';
1010
import {
1111
Card,
@@ -32,6 +32,10 @@ interface BlogFrontmatter {
3232
author: string;
3333
readingTime: number;
3434
authorImage: string;
35+
headings: {
36+
title: string;
37+
level: number;
38+
}[];
3539
}
3640

3741
// generate metadata for the blog post
@@ -77,7 +81,7 @@ export default async function BlogPost({ params }: BlogPostParams) {
7781
const typedFrontmatter = frontmatter as unknown as BlogFrontmatter;
7882

7983
return (
80-
<div className="container px-4 lg:px-0 flex flex-col md:flex-row gap-10 max-w-7xl mx-auto pt-32 pb-20">
84+
<div className="container flex flex-col md:flex-row gap-10 max-w-7xl mx-auto pt-32 pb-20">
8185
<article className="w-full md:w-3/5">
8286
{/** global hero that displays on all blog posts */}
8387
<div className="mb-8">
@@ -152,7 +156,15 @@ export default async function BlogPost({ params }: BlogPostParams) {
152156
</div>
153157
</article>
154158
<aside className="w-full md:w-2/5 order-first md:order-last">
155-
<div className="sticky top-32 md:max-w-[320px] mx-auto space-y-5">
159+
<h3 className="text-2xl font-medium">Table of contents</h3>
160+
<div className="mt-8">
161+
{typedFrontmatter.headings.map((heading) => (
162+
<div key={heading.title}>
163+
<h4 className="text-lg font-medium">{heading.title}</h4>
164+
</div>
165+
))}
166+
</div>
167+
<div className="sticky top-32 md:max-w-[320px] ml-auto space-y-5">
156168
<Card className="w-full border border-black-50 text-white shadow-lg">
157169
<CardHeader className="pb-2">
158170
<CardTitle className="font-onest text-2xl flex items-center justify-center">

src/app/(marketing)/blog/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getBlogPosts } from '@/lib/blog';
44
import GridPattern from '@/components/ui/grid-pattern';
55
import { Button } from '@/components/ui/button';
66
import BlogCard from '@/components/marketing/resources/blog/blog-card';
7-
import { createMetadata } from '@/utils';
7+
import { createMetadata } from '@/utils/seo';
88

99
export async function generateMetadata() {
1010
return createMetadata({

src/app/(marketing)/blog/posts/introducing-techblitz.mdx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ date: '2025-01-07'
66
author: 'Logan Ford'
77
authorImage: 'https://lbycuccwrcmdaxjqyxut.supabase.co/storage/v1/object/public/user-profile-pictures/3a57d7e8-8b80-483b-93d0-70fe1f06b0c0/logo.png?u=1l5im5h5n6e5'
88
readingTime: 5
9+
headings:
10+
- title: 'Introducing TechBlitz: The Future of Software Engineering Education'
11+
level: 2
12+
- title: 'Why TechBlitz Stands Out'
13+
level: 2
14+
- title: 'Level Up Daily with Practical Challenges'
15+
level: 2
16+
- title: 'Learn on the Go'
17+
level: 2
18+
- title: 'Comprehensive Question Library for Real-World Excellence'
19+
level: 2
20+
- title: 'Personalized Learning Paths'
21+
level: 2
22+
- title: 'Dynamic Question Generation'
23+
level: 2
24+
- title: 'Advanced Analytics and Progress Tracking'
25+
level: 2
26+
- title: 'Detailed Performance Reports'
27+
level: 2
28+
- title: 'Open Source and Community-Driven'
29+
level: 2
30+
- title: 'Your career in tech is just a click away'
31+
level: 2
32+
- title: 'Conclusion'
33+
level: 2
934
status: published
1035
---
1136

@@ -54,7 +79,7 @@ For those with a competitive spirit, our global leaderboard system lets you meas
5479
</div>
5580

5681
<div className="mt-4">
57-
Explore our daily challenge system in detail <a href="/features/daily-challenges" aria-label="Navigate to Daily Challenges" className="text-accent">here</a>, or <a href="/signup" aria-label="Navigate to Signup" className="text-accent">begin your journey today</a>.
82+
Explore our daily challenge system in detail <a href="/features/daily-coding-challenges" aria-label="Navigate to Daily Challenges" className="text-accent">here</a>, or <a href="/signup" aria-label="Navigate to Signup" className="text-accent">begin your journey today</a>.
5883
</div>
5984

6085
<br />

src/app/(marketing)/daily-challenge/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Separator } from '@/components/ui/separator';
66
import { ChartColumn, User } from 'lucide-react';
77
import QuestionDisplay from '@/components/app/questions/single/code-snippet';
88
import FeedbackButton from '@/components/ui/feedback-button';
9-
import { createMetadata } from '@/utils';
9+
import { createMetadata } from '@/utils/seo';
1010

1111
import ResizableLayout from '@/components/ui/resizable-layout';
1212

0 commit comments

Comments
 (0)