From c3ae379870a328098f1c31a6b34d11d1b0a008b3 Mon Sep 17 00:00:00 2001 From: Yamen Ahmad Date: Sun, 12 Apr 2026 11:25:02 +0400 Subject: [PATCH] feat: connect academic pages to backend APIs - Generation page: exam structure dropdown loads from backend - Generation page: selecting structure auto-selects its modules - Rubrics page: loads rubrics from backend API with fallback to demo data Made-with: Cursor --- src/pages/GenerationPage.tsx | 34 ++++++++++++++++++++++++++++------ src/pages/RubricsPage.tsx | 22 ++++++++++++++++++++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/pages/GenerationPage.tsx b/src/pages/GenerationPage.tsx index af366210f..da11e8221 100644 --- a/src/pages/GenerationPage.tsx +++ b/src/pages/GenerationPage.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -52,6 +52,7 @@ import { import AiTipBanner from "@/components/ai/AiTipBanner"; import { generationService } from "@/services/generation.service"; import { mediaService, type Avatar } from "@/services/media.service"; +import { examsService } from "@/services/exams.service"; import { useToast } from "@/hooks/use-toast"; type ModuleKey = "reading" | "listening" | "writing" | "speaking" | "level" | "industry"; @@ -73,6 +74,7 @@ const MODULES: ModuleInfo[] = [ { key: "industry", label: "Industry", icon: , color: "text-amber-700", bgColor: "bg-amber-50 border-amber-200" }, ]; +const MODULE_KEYS: ModuleKey[] = MODULES.map((m) => m.key); const CEFR_LEVELS = ["A1", "A2", "B1", "B2", "C1", "C2"]; const READING_EXERCISE_TYPES = [ @@ -197,6 +199,12 @@ export default function GenerationPage() { const [moduleStates, setModuleStates] = useState>({}); const [previewOpen, setPreviewOpen] = useState(false); + const structuresQ = useQuery({ + queryKey: ["exam-structures"], + queryFn: () => examsService.listStructures({}), + }); + const structures = structuresQ.data?.items ?? []; + const getModuleState = useCallback((mod: ModuleKey): ModuleState => { return moduleStates[mod] ?? defaultModuleState(mod); }, [moduleStates]); @@ -990,13 +998,27 @@ export default function GenerationPage() {
- { + setExamStructure(val); + const s = structures.find((st) => String(st.id) === val); + if (s) { + const mods = (s as Record).modules as string[] | undefined; + if (Array.isArray(mods) && mods.length) { + const next = new Set(); + mods.forEach((m) => { if (MODULE_KEYS.includes(m as ModuleKey)) next.add(m as ModuleKey); }); + setSelectedModules(next); + if (next.size > 0) setActiveModule([...next][0]); + } + } + }}> - Standard IELTS Academic - Corporate English Assessment - Hospitality English Test - Medical English Proficiency + {structures.map((s) => ( + {s.name} + ))} + {structures.length === 0 && ( + No structures available + )}
diff --git a/src/pages/RubricsPage.tsx b/src/pages/RubricsPage.tsx index f6f86d303..23c2e7942 100644 --- a/src/pages/RubricsPage.tsx +++ b/src/pages/RubricsPage.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -8,11 +9,21 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Search, Plus } from "lucide-react"; +import { Search, Plus, Loader2 } from "lucide-react"; import AiTipBanner from "@/components/ai/AiTipBanner"; import AiCreationAssistant from "@/components/ai/AiCreationAssistant"; +import { examsService } from "@/services/exams.service"; -const rubrics = [ +interface RubricItem { + id: number; + name: string; + levels: string[]; + criteria: number; + created: string; + skill?: string; +} + +const FALLBACK_RUBRICS: RubricItem[] = [ { id: 1, name: "IELTS Writing Task 2", levels: ["A1","A2","B1","B2","C1","C2"], criteria: 4, created: "2025-01-05" }, { id: 2, name: "Speaking Fluency", levels: ["A1","A2","B1","B2","C1","C2"], criteria: 3, created: "2025-01-10" }, { id: 3, name: "Reading Comprehension", levels: ["A1","A2","B1","B2","C1"], criteria: 5, created: "2025-02-01" }, @@ -26,6 +37,13 @@ const rubricGroups = [ export default function RubricsPage() { const [search, setSearch] = useState(""); + const rubricsQ = useQuery({ + queryKey: ["rubrics"], + queryFn: () => examsService.listRubrics({}), + }); + const backendRubrics = (rubricsQ.data?.items ?? []) as RubricItem[]; + const rubrics = backendRubrics.length > 0 ? backendRubrics : FALLBACK_RUBRICS; + return (