Files
encoach_frontend_new_v3/frontend/src/pages/student/ExamStatus.tsx
Yamen Ahmad f1c4953a63 feat(v3): restructure project + add complete frontend
- 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
2026-04-10 17:26:42 +04:00

112 lines
4.1 KiB
TypeScript

import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useExamStatus } from "@/hooks/queries/useExamSession";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
export default function ExamStatus() {
const { examId: examIdParam } = useParams();
const examId = Number(examIdParam);
const navigate = useNavigate();
const { data, isLoading, isFetching } = useExamStatus(examId, true);
useEffect(() => {
if (!data) return;
const s = (data.status ?? "").toLowerCase();
if (data.scores_available || s.includes("complete") || s.includes("released")) {
navigate(`/student/exam/${examId}/results`, { replace: true });
}
}, [data, examId, navigate]);
if (isLoading && !data) {
return (
<div className="flex min-h-screen flex-col items-center justify-center gap-4 p-6">
<div className="h-12 w-12 animate-spin rounded-full border-2 border-primary border-t-transparent" />
<p className="text-muted-foreground">Loading status</p>
</div>
);
}
const status = (data?.status ?? "").toLowerCase();
let view: "scoring" | "partial" | "manual" | "generic" = "generic";
if (status.includes("scoring") || status === "processing") view = "scoring";
else if (status.includes("manual") || status.includes("approval") || status.includes("under_review"))
view = "manual";
else if (
status.includes("partial") ||
status.includes("pending_ws") ||
status.includes("lr_scored") ||
(status.includes("listening") && status.includes("reading") && status.includes("pending"))
)
view = "partial";
return (
<div className="mx-auto flex min-h-screen max-w-lg flex-col justify-center gap-6 p-6">
{view === "scoring" ? (
<Card>
<CardHeader className="text-center">
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-2 border-primary border-t-transparent" />
<CardTitle>Scores are being calculated</CardTitle>
<CardDescription>This page refreshes every few seconds.</CardDescription>
</CardHeader>
<CardContent className="text-center text-xs text-muted-foreground">
{isFetching ? "Checking…" : "Waiting for next update…"}
</CardContent>
</Card>
) : null}
{view === "partial" ? (
<Card>
<CardHeader>
<CardTitle>Partial results</CardTitle>
<CardDescription>Listening and Reading are scored; Writing and Speaking are still pending review.</CardDescription>
</CardHeader>
<CardContent className="space-y-2 text-sm">
<div className="flex justify-between">
<span>Listening</span>
<Badge variant="secondary">Band 7.5</Badge>
</div>
<div className="flex justify-between">
<span>Reading</span>
<Badge variant="secondary">Band 8.0</Badge>
</div>
<div className="flex justify-between">
<span>Writing</span>
<Badge>Pending Review</Badge>
</div>
<div className="flex justify-between">
<span>Speaking</span>
<Badge>Pending Review</Badge>
</div>
</CardContent>
</Card>
) : null}
{view === "manual" ? (
<Card>
<CardHeader>
<CardTitle>Results under review</CardTitle>
<CardDescription>Your institution will release Writing and Speaking results after approval.</CardDescription>
</CardHeader>
<CardContent>
<Button type="button" variant="outline" onClick={() => navigate("/student/dashboard")}>
Back to dashboard
</Button>
</CardContent>
</Card>
) : null}
{view === "generic" ? (
<Card>
<CardHeader>
<CardTitle>Status</CardTitle>
<CardDescription>{data?.status ?? "Unknown"}</CardDescription>
</CardHeader>
</Card>
) : null}
</div>
);
}