250 lines
11 KiB
TypeScript
250 lines
11 KiB
TypeScript
![]() |
"use client"
|
||
|
|
||
|
import { useState } from "react"
|
||
|
import Link from "next/link"
|
||
|
import { Search, Filter, ArrowUpDown, Plus } from "lucide-react"
|
||
|
import { Button } from "@/components/ui/button"
|
||
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
||
|
import { Input } from "@/components/ui/input"
|
||
|
import { Badge } from "@/components/ui/badge"
|
||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||
|
|
||
|
// Mock data for issues
|
||
|
const mockIssues = [
|
||
|
{
|
||
|
id: "climate-action",
|
||
|
title: "Climate Action Policies",
|
||
|
description: "Discussion on policies to address climate change and reduce carbon emissions.",
|
||
|
category: "Environmental Policy",
|
||
|
status: "active",
|
||
|
metrics: {
|
||
|
perspectives: 324,
|
||
|
insights: 18,
|
||
|
contributions: 5
|
||
|
},
|
||
|
dateCreated: "2024-01-10"
|
||
|
},
|
||
|
{
|
||
|
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
|
||
|
},
|
||
|
dateCreated: "2024-01-22"
|
||
|
},
|
||
|
{
|
||
|
id: "healthcare-access",
|
||
|
title: "Healthcare Accessibility",
|
||
|
description: "Solutions to make healthcare more affordable and accessible for everyone.",
|
||
|
category: "Healthcare",
|
||
|
status: "active",
|
||
|
metrics: {
|
||
|
perspectives: 256,
|
||
|
insights: 15,
|
||
|
contributions: 4
|
||
|
},
|
||
|
dateCreated: "2024-02-05"
|
||
|
},
|
||
|
{
|
||
|
id: "housing-affordability",
|
||
|
title: "Housing Affordability Crisis",
|
||
|
description: "Addressing the growing housing affordability crisis in urban areas.",
|
||
|
category: "Infrastructure",
|
||
|
status: "active",
|
||
|
metrics: {
|
||
|
perspectives: 210,
|
||
|
insights: 8,
|
||
|
contributions: 2
|
||
|
},
|
||
|
dateCreated: "2024-02-15"
|
||
|
},
|
||
|
{
|
||
|
id: "digital-privacy",
|
||
|
title: "Digital Privacy Regulations",
|
||
|
description: "Balancing innovation with personal privacy in the digital age.",
|
||
|
category: "Technology",
|
||
|
status: "active",
|
||
|
metrics: {
|
||
|
perspectives: 156,
|
||
|
insights: 7,
|
||
|
contributions: 1
|
||
|
},
|
||
|
dateCreated: "2024-02-28"
|
||
|
}
|
||
|
]
|
||
|
|
||
|
// Categories for filtering
|
||
|
const categories = [
|
||
|
"All Categories",
|
||
|
"Environmental Policy",
|
||
|
"Education",
|
||
|
"Healthcare",
|
||
|
"Infrastructure",
|
||
|
"Technology",
|
||
|
"Economy",
|
||
|
"Social Services"
|
||
|
]
|
||
|
|
||
|
export default function IssuesPage() {
|
||
|
const [selectedTab, setSelectedTab] = useState("all")
|
||
|
const [searchQuery, setSearchQuery] = useState("")
|
||
|
const [selectedCategory, setSelectedCategory] = useState("All Categories")
|
||
|
const [sortBy, setSortBy] = useState("latest")
|
||
|
|
||
|
// Filter issues based on search, category, and tab
|
||
|
const filteredIssues = mockIssues.filter(issue => {
|
||
|
// Search filter
|
||
|
const matchesSearch = searchQuery
|
||
|
? issue.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
|
issue.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||
|
: true
|
||
|
|
||
|
// Category filter
|
||
|
const matchesCategory = selectedCategory === "All Categories" || issue.category === selectedCategory
|
||
|
|
||
|
// Tab filter (all, trending, etc.) - simplified for demo
|
||
|
const matchesTab = selectedTab === "all" ||
|
||
|
(selectedTab === "trending" && issue.metrics.perspectives > 200)
|
||
|
|
||
|
return matchesSearch && matchesCategory && matchesTab
|
||
|
})
|
||
|
|
||
|
// Sort issues
|
||
|
const sortedIssues = [...filteredIssues].sort((a, b) => {
|
||
|
if (sortBy === "latest") {
|
||
|
return new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime()
|
||
|
} else if (sortBy === "popular") {
|
||
|
return b.metrics.perspectives - a.metrics.perspectives
|
||
|
} else if (sortBy === "insights") {
|
||
|
return b.metrics.insights - a.metrics.insights
|
||
|
}
|
||
|
return 0
|
||
|
})
|
||
|
|
||
|
return (
|
||
|
<div className="container mx-auto px-4 py-8">
|
||
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-8">
|
||
|
<div>
|
||
|
<h1 className="text-3xl font-bold tracking-tight">Issues</h1>
|
||
|
<p className="mt-2 text-muted-foreground">
|
||
|
Browse and contribute to active policy discussions
|
||
|
</p>
|
||
|
</div>
|
||
|
<Button className="mt-4 sm:mt-0" asChild>
|
||
|
<Link href="/issues/propose">
|
||
|
<Plus className="mr-2 h-4 w-4" />
|
||
|
Propose New Issue
|
||
|
</Link>
|
||
|
</Button>
|
||
|
</div>
|
||
|
|
||
|
<div className="flex flex-col gap-6">
|
||
|
{/* Filters and Search */}
|
||
|
<div className="flex flex-col sm:flex-row gap-4">
|
||
|
<div className="relative flex-1">
|
||
|
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||
|
<Input
|
||
|
placeholder="Search issues..."
|
||
|
className="pl-8"
|
||
|
value={searchQuery}
|
||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||
|
/>
|
||
|
</div>
|
||
|
<div className="flex flex-col sm:flex-row gap-2">
|
||
|
<Select
|
||
|
value={selectedCategory}
|
||
|
onValueChange={setSelectedCategory}
|
||
|
>
|
||
|
<SelectTrigger className="w-full sm:w-[180px]">
|
||
|
<SelectValue placeholder="Category" />
|
||
|
</SelectTrigger>
|
||
|
<SelectContent>
|
||
|
{categories.map((category) => (
|
||
|
<SelectItem key={category} value={category}>
|
||
|
{category}
|
||
|
</SelectItem>
|
||
|
))}
|
||
|
</SelectContent>
|
||
|
</Select>
|
||
|
<Select
|
||
|
value={sortBy}
|
||
|
onValueChange={setSortBy}
|
||
|
>
|
||
|
<SelectTrigger className="w-full sm:w-[180px]">
|
||
|
<SelectValue placeholder="Sort by" />
|
||
|
</SelectTrigger>
|
||
|
<SelectContent>
|
||
|
<SelectItem value="latest">Latest</SelectItem>
|
||
|
<SelectItem value="popular">Most Perspectives</SelectItem>
|
||
|
<SelectItem value="insights">Most Insights</SelectItem>
|
||
|
</SelectContent>
|
||
|
</Select>
|
||
|
<Button variant="outline" size="icon">
|
||
|
<Filter className="h-4 w-4" />
|
||
|
</Button>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
{/* Tabs */}
|
||
|
<Tabs value={selectedTab} onValueChange={setSelectedTab} className="w-full">
|
||
|
<TabsList className="grid w-full sm:w-auto sm:inline-grid grid-cols-3 sm:grid-cols-3">
|
||
|
<TabsTrigger value="all">All Issues</TabsTrigger>
|
||
|
<TabsTrigger value="trending">Trending</TabsTrigger>
|
||
|
<TabsTrigger value="following">Following</TabsTrigger>
|
||
|
</TabsList>
|
||
|
</Tabs>
|
||
|
|
||
|
{/* Results */}
|
||
|
{sortedIssues.length === 0 ? (
|
||
|
<div className="text-center py-12">
|
||
|
<h3 className="text-lg font-medium">No issues found</h3>
|
||
|
<p className="text-muted-foreground mt-2">Try adjusting your filters or search terms</p>
|
||
|
</div>
|
||
|
) : (
|
||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
|
{sortedIssues.map((issue) => (
|
||
|
<Link key={issue.id} href={`/issues/${issue.id}`} className="transition-transform hover:scale-[1.01]">
|
||
|
<Card className="h-full flex flex-col">
|
||
|
<CardHeader>
|
||
|
<div className="flex justify-between items-start">
|
||
|
<div>
|
||
|
<CardTitle className="line-clamp-2">{issue.title}</CardTitle>
|
||
|
<CardDescription className="mt-1">
|
||
|
<Badge variant="outline">{issue.category}</Badge>
|
||
|
</CardDescription>
|
||
|
</div>
|
||
|
</div>
|
||
|
</CardHeader>
|
||
|
<CardContent className="flex-grow">
|
||
|
<p className="text-muted-foreground line-clamp-3">{issue.description}</p>
|
||
|
</CardContent>
|
||
|
<CardFooter className="border-t pt-4">
|
||
|
<div className="w-full flex justify-between text-sm text-muted-foreground">
|
||
|
<div className="flex gap-4">
|
||
|
<div>{issue.metrics.perspectives} Perspectives</div>
|
||
|
<div>{issue.metrics.insights} Insights</div>
|
||
|
</div>
|
||
|
<div>
|
||
|
{new Date(issue.dateCreated).toLocaleDateString('en-US', {
|
||
|
year: 'numeric',
|
||
|
month: 'short',
|
||
|
day: 'numeric'
|
||
|
})}
|
||
|
</div>
|
||
|
</div>
|
||
|
</CardFooter>
|
||
|
</Card>
|
||
|
</Link>
|
||
|
))}
|
||
|
</div>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
}
|