feat: drop reflection_prompt type and flag cached micro-learnings

Remove the reflection_prompt micro-learning format end-to-end: type
config, tool definition, container case, selector tile, and the
ReflectionPrompt component file. The format wasn't pulling its weight as
a learning surface.

Add a Beschikbaar badge to selector tiles whose topic already has a
published micro-learning of that type, so users know which formats open
instantly instead of triggering a fresh generation. Cached records are
fetched once per topic via the new getExistingTypesForTopic helper, and
re-fetched after a generation returns so newly-created formats light up
without a manual refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
RaymondVerhoef
2026-05-26 13:41:20 +02:00
parent 9ea5d5444d
commit 84e7468841
5 changed files with 58 additions and 102 deletions

View File

@@ -1,9 +1,8 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import ConceptExplainer from './ConceptExplainer';
import ScenarioQuiz from './ScenarioQuiz';
import FlashcardSet from './FlashcardSet';
import ReflectionPrompt from './ReflectionPrompt';
import { useMicroLearningCompletions } from '../../hooks/useMicroLearningCompletions';
export default function MicroLearningContainer({ microLearning, sessionWeek, onCompletedSuccessfully }) {
@@ -42,8 +41,6 @@ export default function MicroLearningContainer({ microLearning, sessionWeek, onC
return <ScenarioQuiz {...props} />;
case 'flashcard_set':
return <FlashcardSet {...props} />;
case 'reflection_prompt':
return <ReflectionPrompt {...props} />;
default:
return <div>Unknown micro learning type.</div>;
}

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Loader, BookOpen, Target, Layers, MessageCircle } from 'lucide-react';
import { useEffect, useState } from 'react';
import { Loader, BookOpen, Target, Layers, CheckCircle2 } from 'lucide-react';
import { useMicroLearnings } from '../../hooks/useMicroLearnings';
import { getExistingTypesForTopic } from '../../lib/microLearningService';
import MicroLearningContainer from './MicroLearningContainer';
import Card from '../ui/Card';
@@ -23,12 +24,6 @@ const TYPES = [
description: 'Test your recall with a set of quick flashcards.',
icon: Layers,
},
{
key: 'reflection_prompt',
label: 'Reflection Prompt',
description: 'Connect the topic to your own professional experience.',
icon: MessageCircle,
},
];
export default function MicroLearningSelector({ topicId, sessionWeek, onTopicCompleted }) {
@@ -36,6 +31,15 @@ export default function MicroLearningSelector({ topicId, sessionWeek, onTopicCom
const [selectedML, setSelectedML] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [availableTypes, setAvailableTypes] = useState(() => new Set());
useEffect(() => {
let cancelled = false;
getExistingTypesForTopic(topicId).then((set) => {
if (!cancelled) setAvailableTypes(set);
});
return () => { cancelled = true; };
}, [topicId, selectedML]);
const handleSelection = async (type) => {
setLoading(true);
@@ -105,21 +109,36 @@ export default function MicroLearningSelector({ topicId, sessionWeek, onTopicCom
<p className="text-sm text-fg-muted mt-1">Select how you want to engage with this topic.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{TYPES.map(({ key, label, description, icon: Icon }) => (
<div
key={key}
className="cursor-pointer border-2 border-bg-warm rounded-[var(--r-md)] p-6 hover:border-teal hover:bg-teal/5 transition-all group"
onClick={() => handleSelection(key)}
>
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 rounded-full bg-teal/10 flex items-center justify-center flex-shrink-0 group-hover:bg-teal/20 transition-colors">
<Icon size={20} className="text-teal" />
{TYPES.map(({ key, label, description, icon: Icon }) => {
const isAvailable = availableTypes.has(key);
return (
<div
key={key}
className={`relative cursor-pointer border-2 rounded-[var(--r-md)] p-6 hover:bg-teal/5 transition-all group ${
isAvailable
? 'border-teal/40 bg-teal/[0.03] hover:border-teal'
: 'border-bg-warm hover:border-teal'
}`}
onClick={() => handleSelection(key)}
>
{isAvailable && (
<span
className="absolute top-3 right-3 flex items-center gap-1 text-[10px] font-semibold uppercase tracking-wide text-teal bg-teal/10 px-2 py-0.5 rounded-full"
title="Eerder gegenereerde content beschikbaar — opent direct."
>
<CheckCircle2 size={12} /> Beschikbaar
</span>
)}
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 rounded-full bg-teal/10 flex items-center justify-center flex-shrink-0 group-hover:bg-teal/20 transition-colors">
<Icon size={20} className="text-teal" />
</div>
<h3 className="font-bold text-lg text-teal">{label}</h3>
</div>
<h3 className="font-bold text-lg text-teal">{label}</h3>
<p className="text-sm text-fg-muted">{description}</p>
</div>
<p className="text-sm text-fg-muted">{description}</p>
</div>
))}
);
})}
</div>
</Card>
);

View File

@@ -1,55 +0,0 @@
import React, { useState } from 'react';
import Card from '../ui/Card';
import Button from '../ui/Button';
export default function ReflectionPrompt({ content, onComplete }) {
const [response, setResponse] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
if (!response.trim() || submitted) return;
setSubmitted(true);
onComplete();
};
return (
<Card className="w-full">
<div className="mb-6 pb-4 border-b border-bg-warm">
<h2 className="text-xl font-bold">Reflection Prompt</h2>
</div>
<div className="space-y-6">
<div className="prose dark:prose-invert">
<p className="text-lg font-medium">{content?.prompt}</p>
</div>
{!submitted ? (
<form onSubmit={handleSubmit} className="space-y-4">
<textarea
className="w-full min-h-[150px] p-4 border rounded-[var(--r-sm)] resize-y focus:ring-2 focus:ring-teal focus:outline-none bg-bg"
placeholder="Write your reflection here..."
value={response}
onChange={(e) => setResponse(e.target.value)}
/>
<Button type="submit" disabled={!response.trim()}>Submit Reflection</Button>
</form>
) : (
<div className="space-y-6">
<div className="p-4 bg-bg-warm rounded-[var(--r-sm)]">
<h4 className="text-sm font-semibold text-fg-muted uppercase tracking-wider mb-2">Your Reflection</h4>
<p className="whitespace-pre-wrap">{response}</p>
</div>
<div className="p-4 bg-teal/5 border border-teal/20 rounded-[var(--r-sm)]">
<h4 className="text-sm font-semibold text-teal uppercase tracking-wider mb-2">Model Answer</h4>
<div className="prose dark:prose-invert">
<p>{content?.model_answer}</p>
</div>
</div>
</div>
)}
</div>
</Card>
);
}