460 lines
23 KiB
TypeScript
460 lines
23 KiB
TypeScript
"use client"
|
|
|
|
import { useState, use } from "react"
|
|
import { ArrowLeft, MessageSquare, ThumbsUp, ThumbsDown, Share2, FileText, Filter } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { Separator } from "@/components/ui/separator"
|
|
import { Progress } from "@/components/ui/progress"
|
|
|
|
// Mock data for the issue
|
|
const mockIssues = {
|
|
"climate-action": {
|
|
id: "climate-action",
|
|
title: "Climate Action Policies",
|
|
description: "Discussion on policies to address climate change and reduce carbon emissions.",
|
|
category: "Environmental Policy",
|
|
dateCreated: "2024-01-10",
|
|
status: "active",
|
|
metrics: {
|
|
perspectives: 324,
|
|
insights: 18,
|
|
contributions: 5,
|
|
participants: 156
|
|
}
|
|
},
|
|
"education-reform": {
|
|
id: "education-reform",
|
|
title: "Education System Reform",
|
|
description: "Ideas for improving educational outcomes and accessibility for all students.",
|
|
category: "Education",
|
|
status: "active",
|
|
metrics: {
|
|
perspectives: 187,
|
|
insights: 12,
|
|
contributions: 3,
|
|
participants: 89
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mock perspectives for issues
|
|
const mockPerspectivesByIssue = {
|
|
"climate-action": [
|
|
{
|
|
id: 1,
|
|
userId: "user1",
|
|
userName: "Alex Johnson",
|
|
userAvatar: "",
|
|
content: "We should focus on renewable energy investments. Solar and wind power have become more cost-effective and could replace fossil fuels in many regions.",
|
|
dateSubmitted: "2024-03-01",
|
|
likes: 45,
|
|
dislikes: 3
|
|
},
|
|
{
|
|
id: 2,
|
|
userId: "user2",
|
|
userName: "Jamie Smith",
|
|
userAvatar: "",
|
|
content: "Carbon pricing is the most efficient way to reduce emissions. It creates market incentives for businesses to innovate and cut their carbon footprint.",
|
|
dateSubmitted: "2024-02-28",
|
|
likes: 38,
|
|
dislikes: 7
|
|
},
|
|
{
|
|
id: 3,
|
|
userId: "user3",
|
|
userName: "Taylor Reed",
|
|
userAvatar: "",
|
|
content: "We need to address transportation emissions through better public transit and EV infrastructure. This sector is a major contributor to greenhouse gases.",
|
|
dateSubmitted: "2024-02-25",
|
|
likes: 52,
|
|
dislikes: 4
|
|
}
|
|
],
|
|
"education-reform": [
|
|
{
|
|
id: 1,
|
|
userId: "user4",
|
|
userName: "Morgan Lee",
|
|
userAvatar: "",
|
|
content: "Teachers need better resources and smaller class sizes to effectively improve student outcomes.",
|
|
dateSubmitted: "2024-03-02",
|
|
likes: 62,
|
|
dislikes: 5
|
|
},
|
|
{
|
|
id: 2,
|
|
userId: "user5",
|
|
userName: "Casey Wilson",
|
|
userAvatar: "",
|
|
content: "Access to early childhood education should be a priority as it sets the foundation for all future learning.",
|
|
dateSubmitted: "2024-02-27",
|
|
likes: 41,
|
|
dislikes: 2
|
|
}
|
|
]
|
|
}
|
|
|
|
// Mock insights for issues
|
|
const mockInsightsByIssue = {
|
|
"climate-action": [
|
|
{
|
|
id: 1,
|
|
title: "Renewable Energy Investment",
|
|
description: "67% of perspectives support increased government funding for renewable energy projects, with solar and wind being the most frequently mentioned technologies.",
|
|
votes: { yes: 124, no: 18 },
|
|
status: "consensus", // voting, consensus, rejected
|
|
perspectives: [1, 5, 8, 12] // Reference to perspective IDs
|
|
},
|
|
{
|
|
id: 2,
|
|
title: "Carbon Pricing Mechanism",
|
|
description: "A majority of users advocate for carbon pricing policies, with 58% specifically mentioning tax incentives for low-emission businesses.",
|
|
votes: { yes: 98, no: 32 },
|
|
status: "voting", // voting, consensus, rejected
|
|
perspectives: [2, 7, 15] // Reference to perspective IDs
|
|
}
|
|
],
|
|
"education-reform": [
|
|
{
|
|
id: 1,
|
|
title: "Teacher Support Systems",
|
|
description: "78% of perspectives emphasize the need for better resources and support for teachers, including smaller class sizes and professional development.",
|
|
votes: { yes: 112, no: 15 },
|
|
status: "consensus",
|
|
perspectives: [1, 3, 7]
|
|
},
|
|
{
|
|
id: 2,
|
|
title: "Early Childhood Education Access",
|
|
description: "64% of perspectives advocate for universal access to early childhood education to establish a strong foundation for learning.",
|
|
votes: { yes: 87, no: 34 },
|
|
status: "voting",
|
|
perspectives: [2, 8, 10]
|
|
}
|
|
]
|
|
}
|
|
|
|
export default function IssueDetails({ params }: { params: Promise<{ id: string }> }) {
|
|
// Properly unwrap params using React.use()
|
|
const { id: issueId } = use(params)
|
|
const [activeTab, setActiveTab] = useState("overview")
|
|
const [commentText, setCommentText] = useState("")
|
|
|
|
// Get the issue data from our mock data
|
|
const issue = mockIssues[issueId as keyof typeof mockIssues] || mockIssues["climate-action"]
|
|
const perspectives = mockPerspectivesByIssue[issueId as keyof typeof mockPerspectivesByIssue] || []
|
|
const insights = mockInsightsByIssue[issueId as keyof typeof mockInsightsByIssue] || []
|
|
|
|
const handleSubmitComment = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (commentText.trim()) {
|
|
// In a real app, submit the comment to the backend
|
|
console.log("Submitting comment:", commentText)
|
|
setCommentText("")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="container mx-auto px-4 py-8">
|
|
<div className="mb-6">
|
|
<Button variant="ghost" size="sm" asChild className="mb-4">
|
|
<Link href="/issues">
|
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
Back to Issues
|
|
</Link>
|
|
</Button>
|
|
|
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between mb-6">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">{issue.title}</h1>
|
|
<div className="flex items-center gap-2 mt-2">
|
|
<Badge variant="outline">{issue.category}</Badge>
|
|
<Badge variant="secondary" className="capitalize">{issue.status}</Badge>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2 mt-4 md:mt-0">
|
|
<Button variant="outline" size="sm" className="gap-1">
|
|
<Share2 className="h-4 w-4" />
|
|
Share
|
|
</Button>
|
|
<Button variant="default" size="sm" asChild>
|
|
<Link href={`/submit?issue=${issueId}`}>
|
|
<MessageSquare className="mr-2 h-4 w-4" />
|
|
Add Perspective
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-muted-foreground mb-6">{issue.description}</p>
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-2xl font-bold">{issue.metrics.perspectives}</div>
|
|
<p className="text-sm text-muted-foreground">Perspectives</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-2xl font-bold">{issue.metrics.insights}</div>
|
|
<p className="text-sm text-muted-foreground">Insights</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-2xl font-bold">{issue.metrics.contributions}</div>
|
|
<p className="text-sm text-muted-foreground">Contributions</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-2xl font-bold">{issue.metrics.participants}</div>
|
|
<p className="text-sm text-muted-foreground">Participants</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
|
<TabsList className="grid w-full grid-cols-3">
|
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
<TabsTrigger value="perspectives">Perspectives</TabsTrigger>
|
|
<TabsTrigger value="insights">Insights</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="overview" className="pt-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Issue Summary</CardTitle>
|
|
<CardDescription>Key information and progress on this issue</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h3 className="font-medium mb-2">Progress</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>Perspectives Gathering</span>
|
|
<span className="text-muted-foreground">75%</span>
|
|
</div>
|
|
<Progress value={75} />
|
|
</div>
|
|
<div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>Insight Formation</span>
|
|
<span className="text-muted-foreground">45%</span>
|
|
</div>
|
|
<Progress value={45} />
|
|
</div>
|
|
<div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>Contribution Validation</span>
|
|
<span className="text-muted-foreground">20%</span>
|
|
</div>
|
|
<Progress value={20} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Separator />
|
|
<div>
|
|
<h3 className="font-medium mb-2">Timeline</h3>
|
|
<div className="space-y-4">
|
|
<div className="flex">
|
|
<div className="mr-4 mt-0.5">
|
|
<Badge variant="secondary" className="h-8 w-8 rounded-full flex items-center justify-center p-0">1</Badge>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium">Perspectives Collection</h4>
|
|
<p className="text-sm text-muted-foreground">In progress - Ends Apr 15, 2024</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex">
|
|
<div className="mr-4 mt-0.5">
|
|
<Badge variant="outline" className="h-8 w-8 rounded-full flex items-center justify-center p-0">2</Badge>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium">Insight Voting</h4>
|
|
<p className="text-sm text-muted-foreground">Apr 16 - May 1, 2024</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex">
|
|
<div className="mr-4 mt-0.5">
|
|
<Badge variant="outline" className="h-8 w-8 rounded-full flex items-center justify-center p-0">3</Badge>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium">Resolution Formation</h4>
|
|
<p className="text-sm text-muted-foreground">May 2 - May 15, 2024</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="perspectives" className="pt-6">
|
|
<div className="flex justify-between mb-4">
|
|
<h2 className="text-xl font-bold">Recent Perspectives</h2>
|
|
<div className="flex gap-2">
|
|
<Select defaultValue="recent">
|
|
<SelectTrigger className="w-[150px]">
|
|
<SelectValue placeholder="Sort by" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="recent">Most Recent</SelectItem>
|
|
<SelectItem value="popular">Most Popular</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<Button variant="outline" size="icon">
|
|
<Filter className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{perspectives.map((perspective) => (
|
|
<Card key={perspective.id}>
|
|
<CardHeader className="pb-2">
|
|
<div className="flex justify-between">
|
|
<div className="flex items-center">
|
|
<Avatar className="h-8 w-8 mr-2">
|
|
<AvatarImage src={perspective.userAvatar} />
|
|
<AvatarFallback>{perspective.userName.charAt(0)}</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<p className="font-medium">{perspective.userName}</p>
|
|
<p className="text-xs text-muted-foreground">{perspective.dateSubmitted}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p>{perspective.content}</p>
|
|
</CardContent>
|
|
<CardFooter className="border-t pt-4 flex justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<Button variant="ghost" size="sm" className="gap-1">
|
|
<ThumbsUp className="h-4 w-4" />
|
|
{perspective.likes}
|
|
</Button>
|
|
<Button variant="ghost" size="sm" className="gap-1">
|
|
<ThumbsDown className="h-4 w-4" />
|
|
{perspective.dislikes}
|
|
</Button>
|
|
</div>
|
|
<Button variant="ghost" size="sm">Reply</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<Card className="mt-6">
|
|
<CardHeader>
|
|
<CardTitle>Add Your Perspective</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form onSubmit={handleSubmitComment}>
|
|
<Textarea
|
|
placeholder="Share your thoughts on this issue..."
|
|
value={commentText}
|
|
onChange={(e) => setCommentText(e.target.value)}
|
|
className="min-h-[100px]"
|
|
/>
|
|
<div className="flex justify-end mt-4">
|
|
<Button type="submit" disabled={!commentText.trim()}>
|
|
<MessageSquare className="mr-2 h-4 w-4" />
|
|
Submit Perspective
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="insights" className="pt-6">
|
|
<div className="flex justify-between mb-4">
|
|
<h2 className="text-xl font-bold">Emerging Insights</h2>
|
|
<Button variant="outline" size="sm">
|
|
<Filter className="mr-2 h-4 w-4" />
|
|
Filter
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{insights.map((insight) => (
|
|
<Card key={insight.id}>
|
|
<CardHeader>
|
|
<div className="flex justify-between">
|
|
<CardTitle>{insight.title}</CardTitle>
|
|
<Badge
|
|
variant={insight.status === "consensus" ? "default" : "outline"}
|
|
className={insight.status === "consensus" ? "bg-green-500" : ""}
|
|
>
|
|
{insight.status === "consensus" ? "Consensus Reached" : "Voting In Progress"}
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="mb-4">{insight.description}</p>
|
|
<div className="rounded-md bg-muted p-4">
|
|
<div className="text-sm text-muted-foreground mb-2">Voting Progress</div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>
|
|
<ThumbsUp className="h-3 w-3 inline mr-1" />
|
|
{insight.votes.yes} Yes
|
|
</span>
|
|
<span>
|
|
<ThumbsDown className="h-3 w-3 inline mr-1" />
|
|
{insight.votes.no} No
|
|
</span>
|
|
</div>
|
|
<Progress
|
|
value={(insight.votes.yes / (insight.votes.yes + insight.votes.no)) * 100}
|
|
className="h-2"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter className="border-t pt-4 flex justify-between">
|
|
<div>
|
|
<Badge variant="outline" className="mr-2">
|
|
Based on {insight.perspectives.length} perspectives
|
|
</Badge>
|
|
</div>
|
|
{insight.status === "voting" && (
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="gap-1">
|
|
<ThumbsDown className="h-4 w-4" />
|
|
Vote No
|
|
</Button>
|
|
<Button variant="default" size="sm" className="gap-1">
|
|
<ThumbsUp className="h-4 w-4" />
|
|
Vote Yes
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
}
|