feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine
This commit is contained in:
@@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user