fix: smart CBT result/back routing for DPPs and add Test Type selector in Admin panel
This commit is contained in:
@@ -646,6 +646,7 @@ class ManualQuestionInput(BaseModel):
|
|||||||
|
|
||||||
class ManualTestCreate(BaseModel):
|
class ManualTestCreate(BaseModel):
|
||||||
title: str
|
title: str
|
||||||
|
type: str = "CBT"
|
||||||
exam_pattern: str
|
exam_pattern: str
|
||||||
time_limit: int
|
time_limit: int
|
||||||
scheduled_date: Optional[datetime] = None
|
scheduled_date: Optional[datetime] = None
|
||||||
@@ -707,7 +708,7 @@ def create_manual_test(
|
|||||||
# 1. Create the test object
|
# 1. Create the test object
|
||||||
test_obj = models.Test(
|
test_obj = models.Test(
|
||||||
title=data.title,
|
title=data.title,
|
||||||
type="Mock",
|
type=data.type,
|
||||||
exam_pattern=models.ExamEnum(data.exam_pattern),
|
exam_pattern=models.ExamEnum(data.exam_pattern),
|
||||||
sections={},
|
sections={},
|
||||||
total_time=data.time_limit,
|
total_time=data.time_limit,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default function AdminTestsPage() {
|
|||||||
|
|
||||||
// Upload Form State
|
// Upload Form State
|
||||||
const [title, setTitle] = useState("")
|
const [title, setTitle] = useState("")
|
||||||
|
const [testType, setTestType] = useState("CBT")
|
||||||
const [examPattern, setExamPattern] = useState("JEE_Main")
|
const [examPattern, setExamPattern] = useState("JEE_Main")
|
||||||
const [timeLimit, setTimeLimit] = useState("180")
|
const [timeLimit, setTimeLimit] = useState("180")
|
||||||
const [scheduledDate, setScheduledDate] = useState<Date | null>(null)
|
const [scheduledDate, setScheduledDate] = useState<Date | null>(null)
|
||||||
@@ -180,6 +181,7 @@ export default function AdminTestsPage() {
|
|||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
title,
|
title,
|
||||||
|
type: testType,
|
||||||
exam_pattern: examPattern,
|
exam_pattern: examPattern,
|
||||||
time_limit: parseInt(timeLimit),
|
time_limit: parseInt(timeLimit),
|
||||||
admin_id: userId,
|
admin_id: userId,
|
||||||
@@ -237,6 +239,27 @@ export default function AdminTestsPage() {
|
|||||||
<Input required value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. JEE Main Mock Test 1" />
|
<Input required value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. JEE Main Mock Test 1" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* TEST TYPE — KEY DIFFERENTIATOR */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium flex items-center gap-2">
|
||||||
|
Test Type
|
||||||
|
<span className="text-[10px] text-muted-foreground font-normal">(determines which section it appears in)</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
||||||
|
value={testType}
|
||||||
|
onChange={(e) => setTestType(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="CBT">🎯 CBT Mock Test — appears in Mock Tests section</option>
|
||||||
|
<option value="DPS">📋 DPP (Daily Practice) — appears in PrepPilot AI section</option>
|
||||||
|
</select>
|
||||||
|
<p className="text-[11px] text-muted-foreground">
|
||||||
|
{testType === 'CBT'
|
||||||
|
? '✅ This will appear under Dashboard → Mock Tests (CBT) for students to attempt.'
|
||||||
|
: '✅ This will appear under AI Coach → PrepPilot AI as a DPP with full CBT interface.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Exam Pattern</label>
|
<label className="text-sm font-medium">Exam Pattern</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ export default function CBTExamPage({ params }: { params: Promise<{ examId: stri
|
|||||||
<ExamResult
|
<ExamResult
|
||||||
payload={payload}
|
payload={payload}
|
||||||
attemptId={attemptId}
|
attemptId={attemptId}
|
||||||
|
examType={examType}
|
||||||
onRetake={examType === 'DPS' ? () => {
|
onRetake={examType === 'DPS' ? () => {
|
||||||
resetExamState();
|
resetExamState();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ export default function AttemptHistoryPage({ params }: { params: Promise<{ attem
|
|||||||
fetchHistoryData();
|
fetchHistoryData();
|
||||||
}, [attemptId, userId]);
|
}, [attemptId, userId]);
|
||||||
|
|
||||||
|
// Smart back route based on test type
|
||||||
|
const backRoute = test?.type === 'DPS' ? '/dashboard/ai-coach/cbt-dpp' : '/dashboard/cbt';
|
||||||
|
const backLabel = test?.type === 'DPS' ? '← Back to PrepPilot AI' : '← Back to Tests';
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen items-center justify-center">
|
<div className="flex h-screen items-center justify-center">
|
||||||
@@ -83,8 +87,8 @@ export default function AttemptHistoryPage({ params }: { params: Promise<{ attem
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-6xl mx-auto p-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<div className="max-w-6xl mx-auto p-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||||
<Link href="/dashboard/cbt" className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground mb-6 transition-colors">
|
<Link href={backRoute} className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground mb-6 transition-colors">
|
||||||
<ArrowLeft size={16} className="mr-2" /> Back to Tests
|
<ArrowLeft size={16} className="mr-2" /> {test?.type === 'DPS' ? 'Back to PrepPilot AI' : 'Back to Tests'}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row gap-8 mb-10">
|
<div className="flex flex-col md:flex-row gap-8 mb-10">
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ interface ExamAttemptPayload {
|
|||||||
interface ExamResultProps {
|
interface ExamResultProps {
|
||||||
payload: ExamAttemptPayload;
|
payload: ExamAttemptPayload;
|
||||||
attemptId: string | null;
|
attemptId: string | null;
|
||||||
|
examType?: string; // 'CBT' or 'DPS'
|
||||||
onRetake: () => void;
|
onRetake: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExamResult({ payload, attemptId, onRetake }: ExamResultProps) {
|
export function ExamResult({ payload, attemptId, examType, onRetake }: ExamResultProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const isDPP = examType === 'DPS';
|
||||||
|
const backRoute = isDPP ? '/dashboard/ai-coach/cbt-dpp' : '/dashboard/cbt';
|
||||||
|
const backLabel = isDPP ? 'Back to PrepPilot AI' : 'Back to Mock Tests';
|
||||||
const answered = payload.answers.filter(a => a.selected_answer !== null).length;
|
const answered = payload.answers.filter(a => a.selected_answer !== null).length;
|
||||||
const unanswered = payload.total_questions - answered;
|
const unanswered = payload.total_questions - answered;
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ export function ExamResult({ payload, attemptId, onRetake }: ExamResultProps) {
|
|||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-8">
|
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-8">
|
||||||
<Button variant="outline" size="lg" className="rounded-xl h-14 px-8 font-bold text-lg gap-2" onClick={onRetake}>
|
<Button variant="outline" size="lg" className="rounded-xl h-14 px-8 font-bold text-lg gap-2" onClick={onRetake}>
|
||||||
<RotateCcw className="size-5" /> Retake Mock Test
|
<RotateCcw className="size-5" /> {isDPP ? 'Retake DPP' : 'Retake Mock Test'}
|
||||||
</Button>
|
</Button>
|
||||||
{attemptId && (
|
{attemptId && (
|
||||||
<Button
|
<Button
|
||||||
@@ -146,10 +150,10 @@ export function ExamResult({ payload, attemptId, onRetake }: ExamResultProps) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error exiting fullscreen", err);
|
console.error("Error exiting fullscreen", err);
|
||||||
}
|
}
|
||||||
router.push("/dashboard/cbt");
|
router.push(backRoute);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Dashboard <ArrowRight className="size-5" />
|
{backLabel} <ArrowRight className="size-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user