Skip to content

Commit 15943b2

Browse files
✨ feat(classes): add verified classes listing and navigation integration (#269)
1 parent 8f3d07a commit 15943b2

8 files changed

Lines changed: 274 additions & 19 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: Myanmar Python Fundamentals (Sample)
3+
description: >-
4+
An introductory class covering Python syntax, problem-solving basics,
5+
data structures, and scripting practice for students transitioning into
6+
software development.
7+
instructorName: Community Python Mentor Group
8+
classLink: https://example.com/classes/python-fundamentals
9+
tags:
10+
- Python
11+
- Programming Basics
12+
- Backend
13+
classType: In-Person
14+
status: incoming
15+
proofOfAssociation: >-
16+
Submitter is a currently enrolled student and shared enrollment confirmation
17+
plus class orientation details in the pull request for manual verification.
18+
---
19+
20+
### Trust Context
21+
22+
- Submitter role: Attending student (learning side).
23+
- Class legitimacy: This entry demonstrates the required credibility narrative for a moderated listing.
24+
25+
### Notes
26+
27+
- Schedule and venue details are managed through the official class link.
28+
- Listing appears only after maintainer approval.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: MMSWE React Bootcamp (Sample)
3+
description: >-
4+
A structured React bootcamp focused on modern frontend fundamentals:
5+
component architecture, state management, routing, API integration, and
6+
deployment workflow for beginner-to-intermediate learners.
7+
instructorName: MMSWE Education Team
8+
classLink: https://example.com/classes/react-bootcamp
9+
tags:
10+
- React
11+
- TypeScript
12+
- Frontend
13+
classType: online
14+
status: active
15+
proofOfAssociation: >-
16+
Submitter is a teaching assistant in the class and provided internal schedule
17+
screenshots plus mentor confirmation to maintainers during PR review.
18+
---
19+
20+
### Trust Context
21+
22+
- Submitter role: Teaching assistant (teaching side).
23+
- Class legitimacy: This entry represents a sample verified listing format for the curated Classes registry.
24+
25+
### What learners can expect
26+
27+
- Weekly live sessions with guided coding exercises.
28+
- Practical mini-projects with mentor feedback.
29+
- Community discussion and Q&A support.

contentlayer.config.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,30 @@ const bookFields: FieldDefs = {
6262
authorLink: { type: "string", required: false },
6363
};
6464

65+
const classFields: FieldDefs = {
66+
title: { type: "string", required: true },
67+
description: { type: "string", required: true },
68+
instructorName: { type: "string", required: true },
69+
classLink: { type: "string", required: true },
70+
tags: {
71+
type: "list",
72+
of: { type: "string" },
73+
required: true,
74+
},
75+
classType: {
76+
type: "enum",
77+
options: ["online", "In-Person"],
78+
required: true,
79+
},
80+
status: {
81+
type: "enum",
82+
options: ["active", "completed", "incoming"],
83+
required: true,
84+
},
85+
proofOfAssociation: { type: "string", required: true },
86+
image: { type: "string", required: false },
87+
};
88+
6589
export const Book = defineDocumentType(() => ({
6690
name: "Book",
6791
filePathPattern: `./books/**/*.mdx`,
@@ -70,9 +94,17 @@ export const Book = defineDocumentType(() => ({
7094
computedFields: computedFields,
7195
}));
7296

97+
export const Class = defineDocumentType(() => ({
98+
name: "Class",
99+
filePathPattern: `./classes/**/*.mdx`,
100+
fields: classFields,
101+
contentType: "mdx",
102+
computedFields: computedFields,
103+
}));
104+
73105
export default makeSource({
74106
contentDirPath: "./content",
75-
documentTypes: [Blog, Profile, Book],
107+
documentTypes: [Blog, Profile, Book, Class],
76108
mdx: {
77109
remarkPlugins: [remarkGfm],
78110
rehypePlugins: [

messages/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"nav": {
6060
"home": "Home",
6161
"profiles": "Profiles",
62+
"classes": "Classes",
6263
"books": "Books",
6364
"editor": "Editor",
6465
"howTo": "How to",
@@ -369,6 +370,7 @@
369370
"navigate": "Navigate",
370371
"home": "Home",
371372
"profiles": "Profiles",
373+
"classes": "Classes",
372374
"books": "Books",
373375
"profileEditor": "Profile Editor",
374376
"blog": "Blog",

messages/mm.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"nav": {
6060
"home": "ပင်မစာမျက်နှာ",
6161
"profiles": "ပရိုဖိုင်များ",
62+
"classes": "သင်တန်းများ",
6263
"books": "စာအုပ်များ",
6364
"editor": "အယ်ဒီတာ",
6465
"howTo": "လမ်းညွှန်",
@@ -369,6 +370,7 @@
369370
"navigate": "လမ်းညွှန်",
370371
"home": "ပင်မစာမျက်နှာ",
371372
"profiles": "ပရိုဖိုင်များ",
373+
"classes": "သင်တန်းများ",
372374
"books": "စာအုပ်များ",
373375
"profileEditor": "ပရိုဖိုင် အယ်ဒီတာ",
374376
"blog": "ဘလော့ဂ်",

src/app/classes/page.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import PageTransitionWrapper from "@/components/Animate/PageTransitionWrapper/PageTransitionWrapper";
2+
import Container from "@/components/Common/Container/Container";
3+
import SpacingDivider from "@/components/Common/SpacingDivider/SpacingDivider";
4+
import APP_CONFIG from "@/config/config";
5+
import { allClasses } from "contentlayer/generated";
6+
import { Metadata } from "next";
7+
8+
export const metadata: Metadata = {
9+
title: `Classes | ${APP_CONFIG.title}`,
10+
description:
11+
"Explore verified tech classes in Myanmar curated by the MMSWE community.",
12+
openGraph: {
13+
title: `Classes | ${APP_CONFIG.title}`,
14+
description:
15+
"Explore verified tech classes in Myanmar curated by the MMSWE community.",
16+
images: "https://mmswe.com/images/mmswe-seo.png",
17+
},
18+
};
19+
20+
const ClassesPage = async () => {
21+
const classes = [...allClasses].sort((a, b) => a.title.localeCompare(b.title));
22+
23+
return (
24+
<PageTransitionWrapper>
25+
<Container>
26+
<section className="py-16">
27+
<div className="max-w-3xl">
28+
<p className="font-mono text-xs uppercase tracking-[0.25em] text-zinc-500">
29+
Verified Registry
30+
</p>
31+
<h1 className="mt-4 font-display text-4xl font-bold text-zinc-100 sm:text-5xl">
32+
Classes
33+
</h1>
34+
<p className="mt-4 max-w-2xl text-base leading-7 text-zinc-400">
35+
A trust-first, community-curated directory of tech classes in
36+
Myanmar. Listings are reviewed before publication.
37+
</p>
38+
</div>
39+
40+
{classes.length === 0 ? (
41+
<div className="mt-12 rounded-3xl border border-white/10 bg-surface/50 p-8 text-zinc-300">
42+
<h2 className="font-display text-2xl font-semibold text-zinc-100">
43+
No classes yet
44+
</h2>
45+
<p className="mt-3 max-w-2xl text-sm leading-6 text-zinc-400">
46+
Verified class listings will appear here after maintainers review
47+
and approve class submissions.
48+
</p>
49+
</div>
50+
) : (
51+
<div className="mt-12 grid gap-4">
52+
{classes.map((classItem) => (
53+
<article
54+
key={classItem._id}
55+
className="rounded-3xl border border-white/10 bg-surface/50 p-6 transition-colors hover:border-white/20"
56+
>
57+
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
58+
<div className="flex-1">
59+
<div className="flex flex-wrap items-center gap-2">
60+
<h2 className="font-display text-2xl font-semibold text-zinc-100">
61+
{classItem.title}
62+
</h2>
63+
<span className="rounded-full border border-prism-cyan/30 bg-prism-cyan/10 px-3 py-1 text-xs font-medium text-prism-cyan">
64+
{classItem.status}
65+
</span>
66+
<span className="rounded-full border border-white/15 px-3 py-1 text-xs font-medium text-zinc-300">
67+
{classItem.classType}
68+
</span>
69+
</div>
70+
71+
<p className="mt-3 text-sm leading-6 text-zinc-400">
72+
{classItem.description}
73+
</p>
74+
75+
<p className="mt-3 text-sm text-zinc-300">
76+
Instructor: {classItem.instructorName}
77+
</p>
78+
79+
<div className="mt-4 flex flex-wrap gap-2">
80+
{classItem.tags.map((tag) => (
81+
<span
82+
key={`${classItem._id}-${tag}`}
83+
className="rounded-full border border-white/15 px-3 py-1 text-xs text-zinc-300"
84+
>
85+
{tag}
86+
</span>
87+
))}
88+
</div>
89+
</div>
90+
91+
<div className="flex flex-col gap-2 sm:items-end">
92+
<a
93+
href={classItem.classLink}
94+
target="_blank"
95+
rel="noopener noreferrer"
96+
className="inline-flex items-center justify-center rounded-full border border-prism-cyan/40 px-4 py-2 text-sm font-medium text-prism-cyan transition-colors hover:border-prism-cyan hover:bg-prism-cyan/10"
97+
>
98+
Official Link
99+
</a>
100+
</div>
101+
</div>
102+
</article>
103+
))}
104+
</div>
105+
)}
106+
</section>
107+
108+
<SpacingDivider size="lg" />
109+
</Container>
110+
</PageTransitionWrapper>
111+
);
112+
};
113+
114+
export default ClassesPage;

0 commit comments

Comments
 (0)