From 5659ff6726e75c230ec82c143f4a8fbad4e8b3cc Mon Sep 17 00:00:00 2001 From: RaymondVerhoef Date: Sun, 17 May 2026 17:53:09 +0200 Subject: [PATCH] feat: implement learning module page with AI-generated content and feedback workflow --- src/components/admin/UploadZone.jsx | 2 +- src/components/ui/LearningContentViewer.jsx | 68 +-------------------- src/lib/learningService.js | 13 +--- src/pages/Leren.jsx | 1 - 4 files changed, 6 insertions(+), 78 deletions(-) diff --git a/src/components/admin/UploadZone.jsx b/src/components/admin/UploadZone.jsx index 5c6ed02..c372fb3 100644 --- a/src/components/admin/UploadZone.jsx +++ b/src/components/admin/UploadZone.jsx @@ -26,7 +26,7 @@ const UploadZone = ({ onUploadComplete }) => { const [status, setStatus] = useState(null); // GitHub sync state - const [githubUrl, setGithubUrl] = useState('https://github.com/respellion/employee-handbook/tree/main/docs'); + const [githubUrl, setGithubUrl] = useState('https://github.com/respellion/employee-handbook/tree/main/docs/knowledge-base'); const [isFetching, setIsFetching] = useState(false); const [fileList, setFileList] = useState(null); const [syncProgress, setSyncProgress] = useState(null); diff --git a/src/components/ui/LearningContentViewer.jsx b/src/components/ui/LearningContentViewer.jsx index 5a50585..c6e1366 100644 --- a/src/components/ui/LearningContentViewer.jsx +++ b/src/components/ui/LearningContentViewer.jsx @@ -6,7 +6,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { ChevronLeft, ChevronRight, BookOpen, Presentation, - Mic, CheckCircle, Volume2, VolumeX, BarChart2 + CheckCircle, BarChart2 } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import Card from './Card'; @@ -16,12 +16,11 @@ import Button from './Button'; const MODES = [ { key: 'article', icon: BookOpen, label: 'Article' }, { key: 'slides', icon: Presentation, label: 'Slides' }, - { key: 'podcast', icon: Mic, label: 'Podcast' }, { key: 'infographic', icon: BarChart2, label: 'Infographic' }, ]; const LearningContentViewer = ({ content, topic, initialMode = 'article', onGenerate, isLoading }) => { - const populatedModes = MODES.filter(m => m.key === 'podcast' ? !!content?.podcastScript : !!content?.[m.key]); + const populatedModes = MODES.filter(m => !!content?.[m.key]); const defaultMode = populatedModes.length > 0 ? populatedModes[0].key : initialMode; const [activeMode, setActiveMode] = useState(defaultMode); @@ -64,7 +63,7 @@ const LearningContentViewer = ({ content, topic, initialMode = 'article', onGene transition={{ duration: 0.2 }} > {(() => { - const isPopulated = activeMode === 'podcast' ? !!content?.podcastScript : !!content?.[activeMode]; + const isPopulated = !!content?.[activeMode]; if (!isPopulated) { return ( @@ -79,7 +78,6 @@ const LearningContentViewer = ({ content, topic, initialMode = 'article', onGene } if (activeMode === 'article') return ; if (activeMode === 'slides') return ; - if (activeMode === 'podcast') return ; if (activeMode === 'infographic') return ; return null; })()} @@ -180,66 +178,6 @@ const SlidesView = ({ slides }) => { ); }; -/* ── Podcast Renderer ─────────────────────────────────────── */ -const PodcastView = ({ script, topicLabel }) => { - const [isPlaying, setIsPlaying] = useState(false); - const utteranceRef = useRef(null); - - const togglePlayback = () => { - if (!('speechSynthesis' in window)) { - alert('Text-to-speech is not supported by your browser.'); - return; - } - if (isPlaying) { - window.speechSynthesis.cancel(); - setIsPlaying(false); - } else { - const utterance = new SpeechSynthesisUtterance(script); - utterance.lang = 'en-US'; - utterance.rate = 0.95; - utterance.onend = () => setIsPlaying(false); - utteranceRef.current = utterance; - window.speechSynthesis.speak(utterance); - setIsPlaying(true); - } - }; - - useEffect(() => () => window.speechSynthesis?.cancel(), []); - - return ( - -
-
- -
-
-

Podcast Episode

-

{topicLabel}

-
- -
- {isPlaying && ( -
-
- {[0.3, 0.7, 1, 0.6, 0.4, 0.8, 0.5].map((h, i) => ( -
- ))} -
- Now playing... -
- )} -

Script

-

{script}

- - ); -}; /* ── Infographic Renderer ─────────────────────────────────── */ const InfographicView = ({ data, topicLabel }) => { diff --git a/src/lib/learningService.js b/src/lib/learningService.js index 0cc6725..1159596 100644 --- a/src/lib/learningService.js +++ b/src/lib/learningService.js @@ -23,9 +23,7 @@ const CONTENT_SCHEMA_SLIDES = `{ ] }`; -const CONTENT_SCHEMA_PODCAST = `{ - "podcastScript": "A natural spoken script of approx. 300 words summarizing the topic as a podcast episode." -}`; + const CONTENT_SCHEMA_INFOGRAPHIC = `{ "infographic": { @@ -45,7 +43,6 @@ const CONTENT_SCHEMA_INFOGRAPHIC = `{ const CONTENT_SCHEMA_ALL = `{ "article": ${CONTENT_SCHEMA_ARTICLE.replace(/^\{|\}$/g, '').trim()}, "slides": ${CONTENT_SCHEMA_SLIDES.replace(/^\{|\}$/g, '').trim()}, - "podcastScript": "A natural spoken script of approx. 300 words summarizing the topic as a podcast episode.", "infographic": ${CONTENT_SCHEMA_INFOGRAPHIC.replace(/^\{|\}$/g, '').trim()} }`; @@ -83,10 +80,7 @@ export async function generateLearningContent(topic, force = false, selectedType if (!force) { cached = await db.getContent(topic.id); if (cached) { - if (selectedType === 'podcast' && cached.podcastScript) { - console.log(`[Learn] Cache hit for topic: ${topic.id} (podcast)`); - return cached; - } else if (selectedType !== 'podcast' && cached[selectedType]) { + if (cached[selectedType]) { console.log(`[Learn] Cache hit for topic: ${topic.id} (${selectedType})`); return cached; } @@ -104,9 +98,6 @@ export async function generateLearningContent(topic, force = false, selectedType } else if (selectedType === 'slides') { schema = CONTENT_SCHEMA_SLIDES; instructions = 'Provide at least 4 slides.'; - } else if (selectedType === 'podcast') { - schema = CONTENT_SCHEMA_PODCAST; - instructions = 'Provide a natural spoken script.'; } else if (selectedType === 'infographic') { schema = CONTENT_SCHEMA_INFOGRAPHIC; instructions = 'Provide at least 3 stats, and 3-5 steps in the infographic.'; diff --git a/src/pages/Leren.jsx b/src/pages/Leren.jsx index 3fe3ad0..8305e6d 100644 --- a/src/pages/Leren.jsx +++ b/src/pages/Leren.jsx @@ -224,7 +224,6 @@ const Leren = () => {
-