- Restructure: move backend from new_project/ to backend/ - Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks) - Add docs/ with SRS specs, user stories, and workflow documentation - Update .gitignore for new directory layout Workflows implemented: WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration, WF4 General English Exam, WF5 Course Generation, WF6 Entity Student Onboarding, AI Course Generation, Adaptive Learning Engine UI, White-Label Branding, Score Release Made-with: Cursor
86 lines
5.0 KiB
TypeScript
86 lines
5.0 KiB
TypeScript
import { useState } from "react";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Search, Plus, Layers, Trash2 } from "lucide-react";
|
|
import AiTipBanner from "@/components/ai/AiTipBanner";
|
|
import AiCreationAssistant from "@/components/ai/AiCreationAssistant";
|
|
|
|
const structures = [
|
|
{ id: 1, name: "Standard IELTS Academic", entity: "Global", industry: "General", modules: ["Reading", "Listening", "Writing", "Speaking"] },
|
|
{ id: 2, name: "Corporate English Assessment", entity: "Acme Corp", industry: "Technology", modules: ["Reading", "Writing", "Speaking"] },
|
|
{ id: 3, name: "Hospitality English Test", entity: "EduGroup", industry: "Hospitality", modules: ["Listening", "Speaking"] },
|
|
{ id: 4, name: "Medical English Proficiency", entity: "Global", industry: "Healthcare", modules: ["Reading", "Listening", "Writing", "Speaking"] },
|
|
];
|
|
|
|
export default function ExamStructuresPage() {
|
|
const [search, setSearch] = useState("");
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold tracking-tight">Exam Structures</h1>
|
|
<p className="text-muted-foreground">Define exam structure templates by entity and industry.</p>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<AiCreationAssistant type="exam" />
|
|
<Dialog>
|
|
<DialogTrigger asChild>
|
|
<Button size="sm"><Plus className="h-4 w-4 mr-1" /> Create Structure</Button>
|
|
</DialogTrigger>
|
|
<DialogContent>
|
|
<DialogHeader><DialogTitle>Create Exam Structure</DialogTitle></DialogHeader>
|
|
<div className="space-y-4">
|
|
<div className="space-y-2"><Label>Structure Name</Label><Input placeholder="e.g. Corporate Writing Test" /></div>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="space-y-2"><Label>Entity</Label><Select><SelectTrigger><SelectValue placeholder="Entity" /></SelectTrigger><SelectContent><SelectItem value="global">Global</SelectItem><SelectItem value="acme">Acme Corp</SelectItem></SelectContent></Select></div>
|
|
<div className="space-y-2"><Label>Industry</Label><Select><SelectTrigger><SelectValue placeholder="Industry" /></SelectTrigger><SelectContent><SelectItem value="general">General</SelectItem><SelectItem value="tech">Technology</SelectItem><SelectItem value="health">Healthcare</SelectItem></SelectContent></Select></div>
|
|
</div>
|
|
<Button className="w-full">Create</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
</div>
|
|
|
|
<AiTipBanner context="exam-structures" variant="insight" />
|
|
|
|
<div className="flex gap-3 items-center">
|
|
<div className="relative flex-1 max-w-sm">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
<Input placeholder="Search structures..." className="pl-9" value={search} onChange={(e) => setSearch(e.target.value)} />
|
|
</div>
|
|
<Select><SelectTrigger className="w-[140px]"><SelectValue placeholder="Entity" /></SelectTrigger><SelectContent><SelectItem value="all">All</SelectItem></SelectContent></Select>
|
|
<Select><SelectTrigger className="w-[140px]"><SelectValue placeholder="Industry" /></SelectTrigger><SelectContent><SelectItem value="all">All</SelectItem></SelectContent></Select>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{structures.map((s) => (
|
|
<Card key={s.id} className="border-0 shadow-sm">
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center justify-between">
|
|
<CardTitle className="text-base font-semibold flex items-center gap-2">
|
|
<Layers className="h-4 w-4 text-primary" />{s.name}
|
|
</CardTitle>
|
|
<Button variant="ghost" size="icon" className="h-8 w-8 text-destructive"><Trash2 className="h-4 w-4" /></Button>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-3">
|
|
<span>Entity: <span className="text-foreground font-medium">{s.entity}</span></span>
|
|
<span>Industry: <span className="text-foreground font-medium">{s.industry}</span></span>
|
|
</div>
|
|
<div className="flex gap-1.5 flex-wrap">{s.modules.map(m => <Badge key={m} variant="outline">{m}</Badge>)}</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|