import { useState } from 'react'; import { Plus, Trash2, GripVertical, Link, Upload, BookOpen, Layers, FileText } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; // ─── Requirement type config ────────────────────────────────────────────────── const REQUIREMENT_TYPES = [ { value: 'visit_link', label: 'Visit a Link', icon: Link, category: 'Action' }, { value: 'upload_file', label: 'Upload a File', icon: Upload, category: 'Action' }, { value: 'read_course', label: 'Read a Course', icon: BookOpen, category: 'Content' }, { value: 'read_unit', label: 'Read a Unit', icon: Layers, category: 'Content' }, { value: 'read_lesson', label: 'Read a Lesson', icon: FileText, category: 'Content' }, ]; const TYPE_MAP = Object.fromEntries(REQUIREMENT_TYPES.map((t) => [t.value, t])); const FILE_TYPE_OPTIONS = [ { value: 'pdf', label: 'PDF' }, { value: 'docx', label: 'DOCX' }, { value: 'xlsx', label: 'XLSX' }, { value: 'png', label: 'PNG' }, { value: 'jpg', label: 'JPG' }, { value: 'mp4', label: 'MP4' }, { value: 'zip', label: 'ZIP' }, ]; // ─── Empty requirement factory ──────────────────────────────────────────────── function createRequirement(type = 'visit_link') { return { _key: crypto.randomUUID(), type, // visit_link link_url: '', link_label: '', // upload_file allowed_file_types: [], max_file_count: 1, // read_* reference_id: '', reference_label: '', }; } // ─── RequirementBuilder ─────────────────────────────────────────────────────── export default function RequirementBuilder({ value = [], onChange, courses = [], units = [], lessons = [] }) { const [items, setItems] = useState( value.length > 0 ? value.map((r) => ({ _key: crypto.randomUUID(), ...r })) : [] ); const emit = (next) => { setItems(next); // strip _key before calling onChange onChange?.(next.map(({ _key, ...r }) => r)); }; const addItem = () => emit([...items, createRequirement('visit_link')]); const removeItem = (key) => emit(items.filter((i) => i._key !== key)); const updateItem = (key, patch) => emit(items.map((i) => (i._key === key ? { ...i, ...patch } : i))); const toggleFileType = (key, ft) => { const item = items.find((i) => i._key === key); if (!item) return; const current = item.allowed_file_types ?? []; const next = current.includes(ft) ? current.filter((t) => t !== ft) : [...current, ft]; updateItem(key, { allowed_file_types: next }); }; return (
{items.length === 0 && (

No requirements added. Click "Add Requirement" to start.

)} {items.map((item, idx) => { const typeDef = TYPE_MAP[item.type]; const Icon = typeDef?.icon ?? Link; return ( {/* Header row */}
{idx + 1} {/* Type selector */}
{/* ── visit_link fields ── */} {item.type === 'visit_link' && (
updateItem(item._key, { link_url: e.target.value })} className="h-8 text-sm" />
updateItem(item._key, { link_label: e.target.value })} className="h-8 text-sm" />
)} {/* ── upload_file fields ── */} {item.type === 'upload_file' && (
{FILE_TYPE_OPTIONS.map((ft) => ( toggleFileType(item._key, ft.value)} > {ft.label} ))}
updateItem(item._key, { max_file_count: parseInt(e.target.value) || 1 })} className="h-8 text-sm" />
)} {/* ── read_course / read_unit / read_lesson fields ── */} {['read_course', 'read_unit', 'read_lesson'].includes(item.type) && (
{/* Reference selector */} {item.type === 'read_course' && ( )} {item.type === 'read_unit' && ( )} {item.type === 'read_lesson' && ( )}
)}
); })}
); }