feat: implement AI-driven learning content generation service and interactive student dashboard
All checks were successful
On Push to Main / test (push) Successful in 30s
On Push to Main / publish (push) Successful in 57s
On Push to Main / deploy-dev (push) Successful in 1m32s

This commit is contained in:
RaymondVerhoef
2026-05-17 17:10:40 +02:00
parent 98e32d8ac0
commit 5b37c04588
5 changed files with 109 additions and 30 deletions

View File

@@ -20,8 +20,17 @@ const MODES = [
{ key: 'infographic', icon: BarChart2, label: 'Infographic' },
];
const LearningContentViewer = ({ content, topic, initialMode = 'article' }) => {
const [activeMode, setActiveMode] = useState(initialMode);
const LearningContentViewer = ({ content, topic, initialMode = 'article', onGenerate, isLoading }) => {
const populatedModes = MODES.filter(m => m.key === 'podcast' ? !!content?.podcastScript : !!content?.[m.key]);
const defaultMode = populatedModes.length > 0 ? populatedModes[0].key : initialMode;
const [activeMode, setActiveMode] = useState(defaultMode);
// If content populates while we are on an empty tab, keep the tab active
useEffect(() => {
if (content && populatedModes.length === 1 && !populatedModes.find(m => m.key === activeMode)) {
setActiveMode(populatedModes[0].key);
}
}, [content]);
if (!content || !topic) return null;
@@ -54,10 +63,26 @@ const LearningContentViewer = ({ content, topic, initialMode = 'article' }) => {
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
>
{activeMode === 'article' && <ArticleView content={content.article} />}
{activeMode === 'slides' && <SlidesView slides={content.slides} />}
{activeMode === 'podcast' && <PodcastView script={content.podcastScript} topicLabel={topic.label} />}
{activeMode === 'infographic' && <InfographicView data={content.infographic} topicLabel={topic.label} />}
{(() => {
const isPopulated = activeMode === 'podcast' ? !!content?.podcastScript : !!content?.[activeMode];
if (!isPopulated) {
return (
<Card className="border border-bg-warm text-center py-16">
<p className="text-fg-muted mb-6">This content has not been generated yet.</p>
{onGenerate && (
<Button onClick={() => onGenerate(activeMode)} disabled={isLoading}>
Generate {MODES.find(m => m.key === activeMode)?.label}
</Button>
)}
</Card>
);
}
if (activeMode === 'article') return <ArticleView content={content.article} />;
if (activeMode === 'slides') return <SlidesView slides={content.slides} />;
if (activeMode === 'podcast') return <PodcastView script={content.podcastScript} topicLabel={topic.label} />;
if (activeMode === 'infographic') return <InfographicView data={content.infographic} topicLabel={topic.label} />;
return null;
})()}
</motion.div>
</AnimatePresence>
</div>