Files
encoach_backend_new_v2/frontend/src/pages/GrammarPage.tsx
Yamen Ahmad b02ee8b6b7 feat: Generation Page AI workflows + AI/Vector modules + exam session fixes
Generation Page (complete rebuild):
- Full production-parity exam generation wizard with 4 IELTS modules
- Reading: AI passage gen, 5 exercise types (MCQ, Fill, Write, T/F, Match)
- Listening: 4 section types, AI context gen, TTS audio gen (ElevenLabs)
- Writing: Task 1/2, AI instruction gen, word limits, marks
- Speaking: 3 parts, AI script gen, avatar video gen (7 avatars)
- Per-module config: timer, CEFR difficulty, access, approval, rubrics
- Exam submission workflow (draft/published)

Exam Structures:
- New encoach.exam.structure model + CRUD controller
- ExamStructuresPage wired to real API

AI Module (encoach_ai):
- OpenAI service, ElevenLabs TTS, AWS Polly, ELAI avatars
- AI settings model with Odoo config parameters
- 7 generation endpoints (passage, exercises, instructions, scripts, context)

Vector Module (encoach_vector):
- pgvector integration for RAG-based content search
- Embedding service with sentence-transformers

Exam Session Fixes:
- Fixed ExamSession.tsx field mapping (question_type→type, exam_title→title)
- Fixed submit payload to include attempt_id and answers
- Fixed normalizeType to handle null/undefined

Tested: 12/12 API tests passed, browser-verified with real OpenAI calls
Made-with: Cursor
2026-04-11 14:27:03 +04:00

95 lines
5.2 KiB
TypeScript

import { useState } from "react";
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 { CheckCircle, PenTool, Sparkles } from "lucide-react";
import AiTipBanner from "@/components/ai/AiTipBanner";
const grammarItems = [
{ rule: "Conditional Sentences (Type 2 & 3)", description: "If + past simple / past perfect for hypothetical situations", level: "B2", completed: true },
{ rule: "Passive Voice in Academic Writing", description: "Using passive constructions in formal reports", level: "B2", completed: false },
{ rule: "Relative Clauses", description: "Defining vs non-defining relative clauses", level: "B1", completed: true },
{ rule: "Subject-Verb Agreement", description: "Complex subjects with prepositional phrases", level: "B1", completed: false },
{ rule: "Modal Verbs for Speculation", description: "Must have, could have, might have + past participle", level: "C1", completed: false },
{ rule: "Reported Speech", description: "Tense changes and time references in indirect speech", level: "B2", completed: true },
];
export default function GrammarPage() {
const [showCompleted, setShowCompleted] = useState(false);
const completed = grammarItems.filter(g => g.completed).length;
const filtered = showCompleted ? grammarItems : grammarItems.filter(g => !g.completed);
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold tracking-tight">Grammar Training</h1>
<p className="text-muted-foreground">Master grammar rules essential for IELTS.</p>
</div>
<AiTipBanner context="grammar" variant="recommendation" />
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 space-y-4">
<Card className="border-0 shadow-sm">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-base">Progress</CardTitle>
<span className="text-sm text-muted-foreground">{completed}/{grammarItems.length} completed</span>
</div>
</CardHeader>
<CardContent>
<Progress value={(completed / grammarItems.length) * 100} className="h-3" />
</CardContent>
</Card>
<div className="flex items-center gap-2">
<Switch id="show" checked={showCompleted} onCheckedChange={setShowCompleted} />
<Label htmlFor="show" className="text-sm">Show completed</Label>
</div>
<div className="space-y-2">
{filtered.map((g) => (
<Card key={g.rule} className="border-0 shadow-sm">
<CardContent className="p-4 flex items-center justify-between">
<div className="flex items-center gap-3">
{g.completed && <CheckCircle className="h-4 w-4 text-success shrink-0" />}
<div>
<p className="font-semibold text-sm">{g.rule}</p>
<p className="text-xs text-muted-foreground">{g.description}</p>
</div>
</div>
<Badge variant="outline">{g.level}</Badge>
</CardContent>
</Card>
))}
</div>
</div>
<div className="space-y-4">
<Card className="border-0 shadow-sm">
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2"><Sparkles className="h-4 w-4 text-primary" /> AI Recommendations</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2">
<li className="text-sm text-muted-foreground flex items-start gap-2"><span className="text-primary font-bold"></span>Practice passive voice transformations high exam frequency</li>
<li className="text-sm text-muted-foreground flex items-start gap-2"><span className="text-primary font-bold"></span>Review modal verbs for IELTS Writing Task 1</li>
<li className="text-sm text-muted-foreground flex items-start gap-2"><span className="text-primary font-bold"></span>Focus on complex sentence structures for C1 readiness</li>
<li className="text-sm text-muted-foreground flex items-start gap-2"><span className="text-primary font-bold"></span>Your conditional sentences are strong try advanced mixed conditionals</li>
</ul>
</CardContent>
</Card>
<Card className="border-0 shadow-sm border-primary/20 bg-primary/5">
<CardContent className="p-4">
<p className="text-xs font-semibold text-primary flex items-center gap-1 mb-2"><Sparkles className="h-3 w-3" /> AI Study Plan</p>
<p className="text-sm text-muted-foreground">Based on your progress, complete Passive Voice and Subject-Verb Agreement this week. At your current pace, you'll finish all B2 grammar by April 15.</p>
</CardContent>
</Card>
</div>
</div>
</div>
);
}