import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { CheckCircle, Plus, Search, Trash2, Loader2, Sparkles, Circle, } from "lucide-react"; import AiTipBanner from "@/components/ai/AiTipBanner"; import { useToast } from "@/hooks/use-toast"; import { trainingService, type VocabItem, type CefrLevel, } from "@/services/training.service"; const LEVELS: (CefrLevel | "all")[] = ["all", "A1", "A2", "B1", "B2", "C1", "C2"]; export default function VocabularyPage() { const { toast } = useToast(); const qc = useQueryClient(); const [showCompleted, setShowCompleted] = useState(true); const [levelFilter, setLevelFilter] = useState("all"); const [search, setSearch] = useState(""); const [createOpen, setCreateOpen] = useState(false); const [form, setForm] = useState({ word: "", meaning: "", example_sentence: "", level: "B1" as CefrLevel, part_of_speech: "noun" as VocabItem["part_of_speech"], category: "general", }); const listQ = useQuery({ queryKey: ["training-vocab", levelFilter, search], queryFn: () => trainingService.listVocab({ ...(levelFilter !== "all" ? { level: levelFilter } : {}), ...(search ? { search } : {}), }), }); const items = listQ.data?.items ?? []; const summary = listQ.data?.summary; const filtered = showCompleted ? items : items.filter((v) => !v.completed); const createMut = useMutation({ mutationFn: trainingService.createVocab, onSuccess: () => { qc.invalidateQueries({ queryKey: ["training-vocab"] }); setCreateOpen(false); setForm({ word: "", meaning: "", example_sentence: "", level: "B1", part_of_speech: "noun", category: "general", }); toast({ title: "Word added" }); }, onError: (err: unknown) => { const msg = err && typeof err === "object" && "message" in err ? String((err as { message: unknown }).message) : "Failed to add word"; toast({ title: msg, variant: "destructive" }); }, }); const delMut = useMutation({ mutationFn: trainingService.deleteVocab, onSuccess: () => { qc.invalidateQueries({ queryKey: ["training-vocab"] }); toast({ title: "Word deleted" }); }, }); const progressMut = useMutation({ mutationFn: ({ id, completed }: { id: number; completed: boolean }) => trainingService.setVocabProgress(id, { completed }), onSuccess: () => qc.invalidateQueries({ queryKey: ["training-vocab"] }), }); return (

Vocabulary Training

Build and manage the vocabulary library — track completion by CEFR level.

{/* Summary */}
Progress {summary ? `${summary.completed}/${summary.total} completed (${summary.completion_rate}%)` : "—"}
{/* Filters */}
setSearch(e.target.value)} />
{/* List */}
{listQ.isLoading && (
)} {!listQ.isLoading && filtered.length === 0 && ( No vocabulary items match. )} {filtered.map((v) => (

{v.word}

{v.meaning}

{v.example_sentence && (

“{v.example_sentence}”

)}
{v.level} {v.part_of_speech}
))}
{/* AI panel */}
AI Recommendations
  • Pair coherent + concise — they share academic-writing contexts.
  • Review C1 words for IELTS Writing Task 2.
  • Try using meticulous in your next essay.
{summary && (

AI Vocabulary Goal

{summary.remaining > 0 ? `At 2 words/day, you'll complete the remaining ${summary.remaining} items in about ${Math.ceil(summary.remaining / 2)} days.` : "You've completed every active word — try adding more to keep challenging yourself."}

)}
{/* Create dialog */} Add Vocabulary Word
setForm((f) => ({ ...f, word: e.target.value }))} placeholder="e.g. Ubiquitous" />