import { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { BookOpen, Plus, Sparkles, Trash2, Calendar, Layers, } from "lucide-react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; import { coursePlanService } from "@/services/coursePlan.service"; import { describeApiError } from "@/lib/api-client"; /** * Admin list of AI-generated course plans. * * Each plan row shows name + CEFR + weeks + status, plus an "Open" * button that deep-links to the detail page. A prominent "Generate new" * CTA routes to the Smart Wizard's CoursePlanWizard. */ export default function AdminCoursePlans() { const { t } = useTranslation(); const navigate = useNavigate(); const qc = useQueryClient(); const [search, setSearch] = useState(""); const { data, isLoading, isError, error } = useQuery({ queryKey: ["course-plans", { search }], queryFn: () => coursePlanService.list({ page: 0, size: 50, search: search || undefined, }), }); const removeMut = useMutation({ mutationFn: (id: number) => coursePlanService.remove(id), onSuccess: () => { qc.invalidateQueries({ queryKey: ["course-plans"] }); toast.success(t("coursePlan.deleted")); }, onError: (err) => toast.error(describeApiError(err, t("coursePlan.deleteFailed"))), }); const items = data?.items ?? []; return (

{t("coursePlan.listTitle")}

{t("coursePlan.listSubtitle")}

setSearch(e.target.value)} />
{isLoading && (
{Array.from({ length: 3 }).map((_, i) => ( ))}
)} {isError && (
{describeApiError(error, t("coursePlan.loadFailed"))}
)} {!isLoading && !isError && items.length === 0 && (
{t("coursePlan.emptyTitle")}
{t("coursePlan.emptySubtitle")}
)} {!isLoading && items.length > 0 && (
{items.map((plan) => (
{plan.name} {t(`coursePlan.status.${plan.status}`, plan.status)}
{plan.description || t("coursePlan.noDescription")}
{plan.cefr_level || "—"} {t("coursePlan.weeksCount", { count: plan.total_weeks })} {t("coursePlan.materialsCount", { count: plan.material_count })}
))}
)}
); }