feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine
This commit is contained in:
195
src/app/dashboard/community/page.tsx
Normal file
195
src/app/dashboard/community/page.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
|
||||
import { MessageSquare, ThumbsUp, ChevronUp, Search, Image as ImageIcon, CheckCircle } from "lucide-react"
|
||||
|
||||
interface Doubt {
|
||||
id: string
|
||||
author: string
|
||||
avatar: string
|
||||
timeAgo: string
|
||||
subject: string
|
||||
topic: string
|
||||
title: string
|
||||
content: string
|
||||
upvotes: number
|
||||
replies: number
|
||||
isSolved: boolean
|
||||
hasModeratorReply: boolean
|
||||
}
|
||||
|
||||
export default function CommunityPage() {
|
||||
const [activeFilter, setActiveFilter] = useState<'all' | 'my-doubts' | 'solved' | 'unsolved'>('all')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
|
||||
const mockDoubts: Doubt[] = [
|
||||
{
|
||||
id: "1",
|
||||
author: "Rahul Verma",
|
||||
avatar: "RV",
|
||||
timeAgo: "2 hours ago",
|
||||
subject: "Physics",
|
||||
topic: "Rotational Mechanics",
|
||||
title: "Why does friction act forwards in a rolling wheel?",
|
||||
content: "I'm confused about the direction of friction when a car accelerates forward. My book says friction pushes the wheel forward, but shouldn't it oppose motion?",
|
||||
upvotes: 42,
|
||||
replies: 5,
|
||||
isSolved: true,
|
||||
hasModeratorReply: true
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
author: "Priya Sharma",
|
||||
avatar: "PS",
|
||||
timeAgo: "5 hours ago",
|
||||
subject: "Chemistry",
|
||||
topic: "Chemical Kinetics",
|
||||
title: "How to quickly identify a pseudo first-order reaction?",
|
||||
content: "Are there any tricks to spot these in MCQs? I always end up wasting 2 minutes trying to derive the rate law.",
|
||||
upvotes: 18,
|
||||
replies: 2,
|
||||
isSolved: false,
|
||||
hasModeratorReply: false
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
author: "Demo Student",
|
||||
avatar: "DS",
|
||||
timeAgo: "1 day ago",
|
||||
subject: "Mathematics",
|
||||
topic: "Definite Integration",
|
||||
title: "Applying King's Rule when limits are -a to a",
|
||||
content: "I keep messing up the signs when applying the property f(x) + f(-x). Can someone show a step-by-step for the modulus function?",
|
||||
upvotes: 12,
|
||||
replies: 8,
|
||||
isSolved: true,
|
||||
hasModeratorReply: true
|
||||
}
|
||||
]
|
||||
|
||||
const filteredDoubts = mockDoubts.filter(d => {
|
||||
if (activeFilter === 'my-doubts' && d.author !== "Demo Student") return false
|
||||
if (activeFilter === 'solved' && !d.isSolved) return false
|
||||
if (activeFilter === 'unsolved' && d.isSolved) return false
|
||||
if (searchQuery && !d.title.toLowerCase().includes(searchQuery.toLowerCase())) return false
|
||||
return true
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-5xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground flex items-center gap-3">
|
||||
<MessageSquare className="text-[#00F0FF]" size={32} />
|
||||
Doubt Forum
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-2 font-medium">Ask questions, share knowledge, and learn together with the community.</p>
|
||||
</div>
|
||||
|
||||
<button className="px-5 py-2.5 bg-[#00F0FF] text-black font-bold rounded-xl shadow-[0_0_15px_-3px_rgba(0,240,255,0.4)] hover:bg-[#00d0dd] transition-colors">
|
||||
Ask a Doubt
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="relative flex-1">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<Search size={18} className="text-muted-foreground" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search doubts by keyword..."
|
||||
className="w-full pl-10 pr-4 py-2.5 bg-card border border-border rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-[#00F0FF]/50"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 bg-secondary/50 p-1 rounded-xl border border-border shrink-0 overflow-x-auto">
|
||||
<FilterButton active={activeFilter === 'all'} onClick={() => setActiveFilter('all')}>All Doubts</FilterButton>
|
||||
<FilterButton active={activeFilter === 'my-doubts'} onClick={() => setActiveFilter('my-doubts')}>My Doubts</FilterButton>
|
||||
<FilterButton active={activeFilter === 'solved'} onClick={() => setActiveFilter('solved')}>Solved</FilterButton>
|
||||
<FilterButton active={activeFilter === 'unsolved'} onClick={() => setActiveFilter('unsolved')}>Unsolved</FilterButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{filteredDoubts.map((doubt) => (
|
||||
<Card key={doubt.id} className="border-border shadow-sm hover:border-[#00F0FF]/50 transition-colors cursor-pointer">
|
||||
<CardContent className="p-0 flex">
|
||||
{/* Upvote Sidebar */}
|
||||
<div className="w-16 bg-secondary/20 border-r border-border flex flex-col items-center py-4 gap-1">
|
||||
<button className="p-1.5 text-muted-foreground hover:text-[#00F0FF] hover:bg-[#00F0FF]/10 rounded-lg transition-colors">
|
||||
<ChevronUp size={24} />
|
||||
</button>
|
||||
<span className="font-bold text-foreground">{doubt.upvotes}</span>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 p-5">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs font-bold px-2.5 py-1 bg-secondary text-foreground rounded-md">{doubt.subject}</span>
|
||||
<span className="text-xs font-medium text-muted-foreground">• {doubt.topic}</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">{doubt.timeAgo}</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-bold text-foreground mb-1">{doubt.title}</h3>
|
||||
<p className="text-sm text-muted-foreground line-clamp-2 mb-4">{doubt.content}</p>
|
||||
|
||||
<div className="flex items-center justify-between mt-auto">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-6 w-6 rounded-full bg-secondary flex items-center justify-center text-[10px] font-bold">
|
||||
{doubt.avatar}
|
||||
</div>
|
||||
<span className="text-xs font-medium text-foreground">{doubt.author}</span>
|
||||
{doubt.hasModeratorReply && (
|
||||
<span className="ml-2 flex items-center gap-1 text-[10px] font-bold text-emerald-500 bg-emerald-500/10 px-2 py-0.5 rounded-sm border border-emerald-500/20">
|
||||
<CheckCircle size={10} /> Expert Replied
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{doubt.isSolved && (
|
||||
<span className="text-xs font-bold text-emerald-500 flex items-center gap-1">
|
||||
<CheckCircle size={14} /> Solved
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-center gap-1.5 text-muted-foreground hover:text-foreground transition-colors text-sm font-medium">
|
||||
<MessageSquare size={16} />
|
||||
{doubt.replies} Replies
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
{filteredDoubts.length === 0 && (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
<MessageSquare size={48} className="mx-auto mb-4 opacity-20" />
|
||||
<p className="font-medium">No doubts found matching your criteria.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FilterButton({ children, active, onClick }: { children: React.ReactNode, active: boolean, onClick: () => void }) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`px-4 py-1.5 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
|
||||
active
|
||||
? 'bg-background shadow-sm border border-border text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user