Skip to content

Commit 66317cb

Browse files
committed
content(app): js masterclass
1 parent e764c14 commit 66317cb

File tree

13 files changed

+1941
-2
lines changed

13 files changed

+1941
-2
lines changed

public/apps/apps.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,5 +656,22 @@
656656
"pinned_order": 22
657657
}
658658
]
659+
},
660+
"Education": {
661+
"name": "Education",
662+
"description": "Learn new skills and expand your knowledge.",
663+
"icon": "GraduationCapIcon",
664+
"order": 6,
665+
"apps": [
666+
{
667+
"slug": "js-masterclass",
668+
"to": "/apps/js-masterclass",
669+
"title": "JS Masterclass",
670+
"description": "The ultimate Zero to Hero JavaScript and Node.js course.",
671+
"icon": "CodeIcon",
672+
"created_at": "2025-12-25T12:00:00+03:00",
673+
"pinned_order": 1
674+
}
675+
]
659676
}
660677
}

public/apps/js-masterclass/course.json

Lines changed: 593 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Firefly (TV Series)
2+
3+
- **Category:** Series
4+
- **Creator:** Joss Whedon
5+
- **Date:** 2025-12-25
6+
- **Rating:** 5/5
7+
- **Link:** [IMDb - Firefly](https://www.imdb.com/title/tt0303461)
8+
9+
## About
10+
11+
**Firefly (2002)** is an American space western drama television series created by writer and director Joss Whedon. Set in the year 2517, the show follows the adventures of the renegade crew of *Serenity*, a "Firefly-class" spaceship. The ensemble cast portrays the nine characters who live on *Serenity*. Whedon pitched the show as "nine people looking into the blackness of space and seeing nine different things." The series explores the lives of people who fought on the losing side of a civil war and now make a living on the fringes of society, as part of the pioneer culture that exists on the edges of their star system.
12+
13+
## My Thoughts
14+
15+
*Firefly* is a masterpiece of character-driven storytelling that was tragically cut short. Its unique blend of space opera and Western aesthetics creates a world that feels lived-in, dusty, and remarkably human. The chemistry between the crew members is unparalleled, making *Serenity* feel less like a ship and more like a home.
16+
17+
The dialogue is sharp, witty, and uniquely "Whedonesque," full of memorable quips and profound moments of vulnerability. Despite its brief run, the show managed to build a deep mythology and tackle complex themes of freedom, loyalty, and the price of resisting authority. Nathan Fillion's portrayal of Mal Reynolds as a morally gray but ultimately principled leader is iconic. It's a show that leaves you wanting so much more, and its enduring cult status is a testament to its exceptional quality. If you haven't seen it, you're missing out on one of the greatest sci-fi experiences ever made.

public/logs/series/series.piml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
(logs)
2+
> (item)
3+
(category) Series
4+
(date) 2025-12-25
5+
(link) https://www.imdb.com/title/tt0303461
6+
(platform) Hulu, Disney+
7+
(rating) 5
8+
(slug) firefly-tv-series
9+
(title) Firefly (TV Series)
10+
(description) Firefly (2002) is a space western drama created by Joss Whedon. It follows the renegade crew of the spaceship Serenity as they navigate life on the fringes of space. Known for its witty dialogue, deep character development, and unique blend of sci-fi and western elements.
11+
212
> (item)
313
(category) Series
414
(date) 2025-12-12

scripts/generate_course.py

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, { useState, useEffect } from 'react';
2+
import Sidebar from './Sidebar';
3+
import LessonView from './LessonView';
4+
import usePersistentState from '../../../hooks/usePersistentState';
5+
import { useToast } from '../../../hooks/useToast';
6+
7+
const JsMasterclassApp = () => {
8+
const [course, setCourse] = useState(null);
9+
const [currentLessonId, setCurrentLessonId] = usePersistentState('js-masterclass-last-lesson', null);
10+
const [completedLessons, setCompletedLessons] = usePersistentState('js-masterclass-completed', []);
11+
const { addToast } = useToast();
12+
13+
useEffect(() => {
14+
console.log('Fetching course data...');
15+
fetch('/apps/js-masterclass/course.json')
16+
.then((res) => {
17+
if (!res.ok) {
18+
throw new Error(`HTTP error! status: ${res.status}`);
19+
}
20+
return res.json();
21+
})
22+
.then((data) => {
23+
console.log('Course data loaded:', data);
24+
setCourse(data);
25+
26+
// Validation Logic
27+
let isValidLesson = false;
28+
if (currentLessonId) {
29+
// Check if the saved ID actually exists in the new data
30+
for (const module of data.modules) {
31+
if (module.lessons.find(l => l.id === currentLessonId)) {
32+
isValidLesson = true;
33+
break;
34+
}
35+
}
36+
}
37+
38+
if (!isValidLesson && data.modules.length > 0 && data.modules[0].lessons.length > 0) {
39+
console.log('Invalid or missing lesson ID, resetting to start.');
40+
setCurrentLessonId(data.modules[0].lessons[0].id);
41+
}
42+
})
43+
.catch((err) => {
44+
console.error('Failed to load course:', err);
45+
addToast({ title: 'SYSTEM_ERROR', message: 'FAILED_TO_LOAD_DATA_STREAM', type: 'error' });
46+
});
47+
// eslint-disable-next-line react-hooks/exhaustive-deps
48+
}, []); // Empty dependency array to run only once on mount
49+
50+
const handleLessonSelect = (lessonId) => {
51+
setCurrentLessonId(lessonId);
52+
window.scrollTo({ top: 0, behavior: 'smooth' });
53+
};
54+
55+
const handleLessonComplete = (lessonId) => {
56+
if (!completedLessons.includes(lessonId)) {
57+
setCompletedLessons((prev) => [...prev, lessonId]);
58+
addToast({
59+
title: 'PROTOCOL_COMPLETE',
60+
message: 'DATA_SAVED_TO_LOCAL_MEMORY',
61+
type: 'success', // Assuming the toast component handles variants or defaults
62+
});
63+
64+
// Auto-advance logic could go here
65+
}
66+
};
67+
68+
const handleResetCourse = () => {
69+
setCompletedLessons([]);
70+
if (course && course.modules.length > 0 && course.modules[0].lessons.length > 0) {
71+
setCurrentLessonId(course.modules[0].lessons[0].id);
72+
}
73+
addToast({
74+
title: 'SYSTEM_RESET',
75+
message: 'PROTOCOL_PROGRESS_WIPED',
76+
type: 'techno',
77+
});
78+
};
79+
80+
// Helper to find lesson object by ID
81+
const getCurrentLesson = () => {
82+
if (!course || !currentLessonId) return null;
83+
for (const module of course.modules) {
84+
const lesson = module.lessons.find(l => l.id === currentLessonId);
85+
if (lesson) return lesson;
86+
}
87+
return null;
88+
};
89+
90+
return (
91+
<div className="flex h-[calc(100vh-64px)] overflow-hidden bg-[#050505]">
92+
<Sidebar
93+
course={course}
94+
currentLessonId={currentLessonId}
95+
onSelectLesson={handleLessonSelect}
96+
completedLessons={completedLessons}
97+
onResetCourse={handleResetCourse}
98+
/>
99+
<main className="flex-grow overflow-y-auto bg-[#050505] p-8 md:p-16 scrollbar-thin scrollbar-thumb-white/10 scrollbar-track-transparent">
100+
<LessonView
101+
lesson={getCurrentLesson()}
102+
onComplete={handleLessonComplete}
103+
/>
104+
</main>
105+
</div>
106+
);
107+
};
108+
109+
export default JsMasterclassApp;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import React, { useState } from 'react';
2+
import MarkdownContent from '../../../components/MarkdownContent';
3+
import Quiz from './Quiz';
4+
import { CaretRight, Flask, ArrowsOutSimple, ClipboardText } from '@phosphor-icons/react';
5+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
6+
import { customTheme } from '../../../utils/customTheme';
7+
import CodeModal from '../../../components/CodeModal';
8+
import { useToast } from '../../../hooks/useToast';
9+
10+
const LessonView = ({ lesson, onComplete }) => {
11+
const [isModalOpen, setIsModalOpen] = useState(false);
12+
const [modalContent, setModalContent] = useState('');
13+
const [modalLanguage, setModalLanguage] = useState('javascript');
14+
const { addToast } = useToast();
15+
16+
const openModal = (content, language) => {
17+
setModalContent(content);
18+
setModalLanguage(language);
19+
setIsModalOpen(true);
20+
};
21+
22+
const CodeBlock = ({ inline, className, children, ...props }) => {
23+
const match = /language-(\w+)/.exec(className || '');
24+
25+
const handleCopy = () => {
26+
const textToCopy = String(children);
27+
navigator.clipboard.writeText(textToCopy).then(
28+
() =>
29+
addToast({
30+
title: 'INTEL COPIED',
31+
message: 'Code block synchronized to clipboard.',
32+
duration: 3000,
33+
type: 'success',
34+
}),
35+
() =>
36+
addToast({
37+
title: 'ERROR',
38+
message: 'Failed to access clipboard.',
39+
duration: 3000,
40+
type: 'error',
41+
}),
42+
);
43+
};
44+
45+
if (!inline && match) {
46+
return (
47+
<div className="relative group my-8">
48+
<div className="absolute -top-3 right-4 flex gap-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
49+
<button
50+
onClick={() =>
51+
openModal(String(children).replace(/\n$/, ''), match[1])
52+
}
53+
className="bg-zinc-900 border border-white/10 px-2 py-1 text-[10px] uppercase font-mono font-bold tracking-widest text-gray-400 hover:text-white hover:bg-emerald-500 hover:border-emerald-500 transition-all rounded-sm"
54+
title="Enlarge Intel"
55+
>
56+
<ArrowsOutSimple size={12} weight="bold" /> EXPAND
57+
</button>
58+
<button
59+
onClick={handleCopy}
60+
className="bg-zinc-900 border border-white/10 px-2 py-1 text-[10px] uppercase font-mono font-bold tracking-widest text-gray-400 hover:text-white hover:bg-emerald-500 hover:border-emerald-500 transition-all rounded-sm"
61+
title="Copy to Clipboard"
62+
>
63+
<ClipboardText size={12} weight="bold" /> COPY
64+
</button>
65+
</div>
66+
<div className="border border-white/10 rounded-sm overflow-hidden shadow-2xl bg-zinc-950">
67+
<div className="bg-white/5 px-4 py-2 border-b border-white/10 flex justify-between items-center">
68+
<span className="font-mono text-[9px] uppercase tracking-[0.2em] text-gray-500">
69+
DATA_NODE: {match[1]}
70+
</span>
71+
</div>
72+
<SyntaxHighlighter
73+
style={customTheme}
74+
language={match[1]}
75+
PreTag="div"
76+
CodeTag="code"
77+
customStyle={{
78+
margin: 0,
79+
padding: '1.5rem',
80+
fontSize: '0.9rem',
81+
lineHeight: '1.6',
82+
background: 'transparent',
83+
}}
84+
{...props}
85+
codeTagProps={{
86+
style: { fontFamily: "'JetBrains Mono', monospace" },
87+
}}
88+
>
89+
{String(children).replace(/\n$/, '')}
90+
</SyntaxHighlighter>
91+
</div>
92+
</div>
93+
);
94+
}
95+
96+
return (
97+
<code
98+
className={`${className} font-mono bg-white/5 text-emerald-400 px-1.5 py-0.5 rounded-sm text-sm`}
99+
{...props}
100+
>
101+
{children}
102+
</code>
103+
);
104+
};
105+
106+
if (!lesson) return (
107+
<div className="flex flex-col items-center justify-center h-full text-gray-500 font-mono text-[10px] uppercase tracking-widest space-y-4">
108+
<div className="w-8 h-8 border border-t-emerald-500 border-r-emerald-500/50 border-b-emerald-500/20 border-l-emerald-500/10 animate-spin rounded-full"></div>
109+
<p>Loading_Matrix_Data...</p>
110+
</div>
111+
);
112+
113+
return (
114+
<div className="max-w-5xl mx-auto pb-32">
115+
{/* Lesson Header */}
116+
<div className="mb-16 border-b border-white/10 pb-12">
117+
<div className="flex items-center gap-2 text-[10px] font-mono text-emerald-500 uppercase tracking-[0.2em] mb-6">
118+
<span>Module_0{lesson.id.split('-')[0] || '1'}</span>
119+
<CaretRight size={10} />
120+
<span>Unit_Protocol</span>
121+
</div>
122+
<h1 className="text-4xl md:text-6xl font-playfairDisplay font-light text-white mb-8 leading-none tracking-tight uppercase">
123+
{lesson.title}
124+
</h1>
125+
<div className="flex gap-4">
126+
<div className="h-1 w-24 bg-emerald-500"></div>
127+
<div className="h-1 w-12 bg-white/20"></div>
128+
<div className="h-1 w-4 bg-white/10"></div>
129+
</div>
130+
</div>
131+
132+
{/* Content */}
133+
<div className="prose prose-invert prose-lg max-w-none
134+
prose-headings:font-playfairDisplay prose-headings:font-light prose-headings:uppercase prose-headings:tracking-wide prose-headings:text-white
135+
prose-p:text-gray-400 prose-p:font-light prose-p:leading-relaxed prose-p:font-mono
136+
prose-strong:text-white prose-strong:font-bold
137+
prose-pre:bg-transparent prose-pre:border-none prose-pre:p-0
138+
prose-blockquote:border-l-2 prose-blockquote:border-emerald-500 prose-blockquote:bg-white/[0.02] prose-blockquote:py-2 prose-blockquote:px-6 prose-blockquote:not-italic prose-blockquote:text-gray-500
139+
prose-li:text-gray-400 prose-li:font-mono prose-li:marker:text-emerald-500
140+
prose-img:rounded-none prose-img:border prose-img:border-white/10 prose-img:grayscale hover:prose-img:grayscale-0 prose-img:transition-all
141+
">
142+
<MarkdownContent
143+
content={lesson.content}
144+
components={{ code: CodeBlock }}
145+
/>
146+
</div>
147+
148+
{/* Footer / Quiz Section */}
149+
<div className="mt-24 pt-12 border-t border-white/10">
150+
{lesson.quiz && lesson.quiz.length > 0 ? (
151+
<Quiz questions={lesson.quiz} onComplete={() => onComplete(lesson.id)} />
152+
) : (
153+
<div className="flex flex-col items-center justify-center bg-white/[0.02] border border-white/5 p-16 text-center group hover:border-emerald-500/30 transition-all">
154+
<div className="mb-6 p-4 bg-white/5 rounded-full text-gray-500 group-hover:text-emerald-500 transition-colors">
155+
<Flask size={32} weight="duotone" />
156+
</div>
157+
<h3 className="text-xl font-bold text-white uppercase tracking-widest mb-2 font-mono">Protocol_End</h3>
158+
<p className="text-gray-500 font-mono text-xs uppercase tracking-widest mb-8">No assessment required for this unit.</p>
159+
<button
160+
onClick={() => onComplete(lesson.id)}
161+
className="group relative inline-flex items-center justify-center px-8 py-4 font-black text-[10px] uppercase tracking-[0.2em] text-black bg-white hover:bg-emerald-500 transition-all"
162+
>
163+
<span>MARK_COMPLETE</span>
164+
<CaretRight className="ml-3 transition-transform duration-200 group-hover:translate-x-1" weight="bold" />
165+
</button>
166+
</div>
167+
)}
168+
</div>
169+
170+
<CodeModal
171+
isOpen={isModalOpen}
172+
onClose={() => setIsModalOpen(false)}
173+
language={modalLanguage}
174+
>
175+
{modalContent}
176+
</CodeModal>
177+
</div>
178+
);
179+
};
180+
181+
export default LessonView;

0 commit comments

Comments
 (0)