feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine

This commit is contained in:
2026-07-06 17:08:13 +05:30
parent ce01795aa9
commit 426b98ddab
66 changed files with 7949 additions and 363 deletions

View File

@@ -1,7 +1,69 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
"use client";
import { useEffect, useState } from "react";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
export default function SettingsPage() {
const [targetExam, setTargetExam] = useState<"JEE_Main" | "NEET">("JEE_Main");
const [targetYear, setTargetYear] = useState("2026");
const [firstName, setFirstName] = useState("Rahul");
const [lastName, setLastName] = useState("Sharma");
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const userId = "b0e17378-864d-439e-8806-708dc48a891b";
useEffect(() => {
async function fetchUserData() {
try {
const res = await fetch(`http://localhost:8000/api/users/${userId}`);
if (res.ok) {
const data = await res.json();
setTargetExam(data.target_exam === "NEET" ? "NEET" : "JEE_Main");
if (data.name) {
const parts = data.name.split(" ");
setFirstName(parts[0] || "");
setLastName(parts.slice(1).join(" ") || "");
}
}
} catch (err) {
console.error("Failed to load user profile:", err);
} finally {
setLoading(false);
}
}
fetchUserData();
}, []);
const handleSave = async () => {
setSaving(true);
try {
const res = await fetch(`http://localhost:8000/api/users/${userId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: `${firstName} ${lastName}`.trim(),
target_exam: targetExam,
}),
});
if (res.ok) {
alert("Settings saved successfully!");
} else {
alert("Failed to save settings: " + (await res.text()));
}
} catch (err) {
console.error("Error saving settings:", err);
alert("Network error. Could not save settings.");
} finally {
setSaving(false);
}
};
if (loading) {
return <div className="p-8">Loading settings...</div>;
}
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-4xl mx-auto">
<div>
@@ -19,18 +81,30 @@ export default function SettingsPage() {
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-bold">First Name</label>
<input type="text" defaultValue="Demo" className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF]" />
<input
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF]"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-bold">Last Name</label>
<input type="text" defaultValue="Student" className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF]" />
<input
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF]"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-bold">Email Address</label>
<input type="email" defaultValue="demo@pragativeer.com" disabled className="w-full bg-secondary border border-border rounded-lg px-4 py-2 text-sm text-muted-foreground cursor-not-allowed" />
<input type="email" defaultValue="student@pragativeer.com" disabled className="w-full bg-secondary border border-border rounded-lg px-4 py-2 text-sm text-muted-foreground cursor-not-allowed" />
</div>
<Button className="bg-foreground text-background hover:bg-foreground/90 font-bold mt-2">Save Changes</Button>
<Button onClick={handleSave} disabled={saving} className="bg-foreground text-background hover:bg-foreground/90 font-bold mt-2">
{saving ? "Saving..." : "Save Changes"}
</Button>
</CardContent>
</Card>
@@ -43,26 +117,61 @@ export default function SettingsPage() {
<div className="space-y-3">
<label className="text-sm font-bold">Target Examination</label>
<div className="flex gap-4">
<label className="flex items-center gap-2 cursor-pointer p-4 border border-[#00F0FF] bg-[#00F0FF]/5 rounded-xl flex-1">
<input type="radio" name="target" defaultChecked className="accent-[#00F0FF] w-4 h-4" />
<button
type="button"
onClick={() => setTargetExam("NEET")}
className={`flex items-center gap-2 cursor-pointer p-4 border rounded-xl flex-1 text-left transition-all ${
targetExam === "NEET"
? "border-[#00F0FF] bg-[#00F0FF]/5"
: "border-border bg-secondary/20 hover:bg-secondary/50"
}`}
>
<input
type="radio"
name="target"
checked={targetExam === "NEET"}
onChange={() => {}}
className="accent-[#00F0FF] w-4 h-4"
/>
<span className="font-bold">NEET (UG)</span>
</label>
<label className="flex items-center gap-2 cursor-pointer p-4 border border-border bg-secondary/20 hover:bg-secondary/50 rounded-xl flex-1 transition-colors">
<input type="radio" name="target" className="accent-[#00F0FF] w-4 h-4" />
</button>
<button
type="button"
onClick={() => setTargetExam("JEE_Main")}
className={`flex items-center gap-2 cursor-pointer p-4 border rounded-xl flex-1 text-left transition-all ${
targetExam === "JEE_Main"
? "border-[#00F0FF] bg-[#00F0FF]/5"
: "border-border bg-secondary/20 hover:bg-secondary/50"
}`}
>
<input
type="radio"
name="target"
checked={targetExam === "JEE_Main"}
onChange={() => {}}
className="accent-[#00F0FF] w-4 h-4"
/>
<span className="font-bold">JEE Main & Adv</span>
</label>
</button>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-bold">Target Year</label>
<select className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF] appearance-none font-medium">
<select
value={targetYear}
onChange={(e) => setTargetYear(e.target.value)}
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF] appearance-none font-medium"
>
<option>2026</option>
<option>2027</option>
</select>
</div>
<Button onClick={handleSave} disabled={saving} className="bg-foreground text-background hover:bg-foreground/90 font-bold mt-2">
{saving ? "Saving..." : "Save Preferences"}
</Button>
</CardContent>
</Card>
</div>
</div>
)
);
}