feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine
This commit is contained in:
103
src/components/AI/AdaptiveStudyPlan.tsx
Normal file
103
src/components/AI/AdaptiveStudyPlan.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { CheckCircle2, Clock, SkipForward, BrainCircuit } from "lucide-react"
|
||||
|
||||
type PlanState = 'PENDING' | 'DONE' | 'SKIPPED';
|
||||
|
||||
interface PlanItem {
|
||||
id: string;
|
||||
title: string;
|
||||
duration: string;
|
||||
type: string;
|
||||
status: PlanState;
|
||||
}
|
||||
|
||||
const INITIAL_PLAN: PlanItem[] = [
|
||||
{ id: '1', title: 'Revise Thermodynamics Concepts', duration: '45 min', type: 'Theory', status: 'PENDING' },
|
||||
{ id: '2', title: 'Solve 15 Rotational Dynamics DPPs', duration: '30 min', type: 'Practice', status: 'PENDING' },
|
||||
{ id: '3', title: 'Review Mistakes in Optics (T-3)', duration: '20 min', type: 'Review', status: 'PENDING' },
|
||||
];
|
||||
|
||||
export function AdaptiveStudyPlan() {
|
||||
const [items, setItems] = useState<PlanItem[]>(INITIAL_PLAN);
|
||||
|
||||
const updateStatus = (id: string, newStatus: PlanState) => {
|
||||
setItems(items.map(item => item.id === id ? { ...item, status: newStatus } : item));
|
||||
};
|
||||
|
||||
const totalTime = items.reduce((acc, curr) => {
|
||||
if (curr.status === 'DONE' || curr.status === 'PENDING') {
|
||||
return acc + parseInt(curr.duration);
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<Card className="border-border bg-background shadow-sm h-full flex flex-col">
|
||||
<CardHeader className="pb-4 border-b border-border">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-xl flex items-center gap-2">
|
||||
<BrainCircuit className="text-primary" /> Adaptive Study Plan
|
||||
</CardTitle>
|
||||
<div className="flex items-center gap-2 bg-secondary/50 px-3 py-1 rounded-full text-xs font-bold text-foreground">
|
||||
<Clock size={14} className="text-primary" /> Today: {totalTime} min
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">Dynamically generated based on your weak areas.</p>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 p-6">
|
||||
<div className="relative space-y-6 before:absolute before:inset-0 before:ml-5 before:-translate-x-px md:before:mx-auto md:before:translate-x-0 before:h-full before:w-0.5 before:bg-gradient-to-b before:from-transparent before:via-border before:to-transparent">
|
||||
{items.map((item, index) => {
|
||||
const isDone = item.status === 'DONE';
|
||||
const isSkipped = item.status === 'SKIPPED';
|
||||
|
||||
return (
|
||||
<div key={item.id} className="relative flex items-center justify-between md:justify-normal md:odd:flex-row-reverse group is-active">
|
||||
{/* Timeline Icon */}
|
||||
<div className={`flex items-center justify-center w-10 h-10 rounded-full border-4 border-background shrink-0 md:order-1 md:group-odd:-translate-x-1/2 md:group-even:translate-x-1/2 shadow-sm z-10 transition-colors
|
||||
${isDone ? 'bg-green-500 text-white' : isSkipped ? 'bg-muted border-muted text-muted-foreground' : 'bg-primary/20 text-primary border-primary/30'}
|
||||
`}>
|
||||
{isDone ? <CheckCircle2 size={18} /> : isSkipped ? <SkipForward size={18} /> : <div className="size-2.5 rounded-full bg-primary animate-pulse" />}
|
||||
</div>
|
||||
|
||||
{/* Card */}
|
||||
<div className="w-[calc(100%-4rem)] md:w-[calc(50%-2.5rem)] p-4 rounded-xl border border-border bg-background shadow-sm transition-all hover:shadow-md">
|
||||
<div className={`flex items-center justify-between mb-1 ${isSkipped ? 'opacity-50' : ''}`}>
|
||||
<span className="text-[10px] uppercase font-bold tracking-wider text-muted-foreground bg-secondary/50 px-2 py-0.5 rounded-md">
|
||||
{item.type}
|
||||
</span>
|
||||
<span className="text-xs font-semibold text-primary">{item.duration}</span>
|
||||
</div>
|
||||
<h4 className={`font-bold text-sm md:text-base leading-tight mb-3 transition-colors ${isDone ? 'line-through text-muted-foreground' : isSkipped ? 'text-muted-foreground' : 'text-foreground'}`}>
|
||||
{item.title}
|
||||
</h4>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2">
|
||||
{item.status === 'PENDING' ? (
|
||||
<>
|
||||
<Button size="sm" onClick={() => updateStatus(item.id, 'DONE')} className="h-8 text-xs font-bold flex-1 bg-green-500/10 text-green-500 hover:bg-green-500 hover:text-white border border-green-500/20">
|
||||
Mark Done
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => updateStatus(item.id, 'SKIPPED')} className="h-8 text-xs font-bold px-3 hover:bg-destructive/10 hover:text-destructive">
|
||||
Skip
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full flex items-center justify-center py-1 bg-secondary/50 rounded-lg text-xs font-bold text-muted-foreground">
|
||||
{isDone ? 'Completed' : 'Rescheduled'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user