"use client"; import { motion, AnimatePresence } from "framer-motion"; import { AlertCircle, CheckCircle2, MessageSquare, Search, Tag, Calendar, User, ArrowUpDown, Plus, Sparkles, Filter, Layers, CheckSquare, FileText, ChevronDown, ChevronRight, LayoutList } from "lucide-react"; import { useState } from "react"; interface Issue { id: string; number: number; title: string; type: string; state: "open" | "closed"; createdAt: string; commentCount: number; author: { username: string; avatarUrl?: string; }; labels?: Array<{ name: string; color: string; }>; parent?: { number: number; title: string; state: "open" | "closed"; }; children?: Array<{ number: number; title: string; state: "open" | "closed"; }>; } interface Props { issues: Issue[]; openCount: number; closedCount: number; repoOwner: string; repoName: string; } function timeAgo(date: string): string { const now = new Date(); const past = new Date(date); const diffMs = now.getTime() - past.getTime(); const diffMins = Math.floor(diffMs / 60000); if (diffMins < 1) return 'just now'; if (diffMins < 60) return `${diffMins} minutes ago`; const diffHours = Math.floor(diffMins / 60); if (diffHours < 24) return `${diffHours} hours ago`; const diffDays = Math.floor(diffHours / 24); if (diffDays < 30) return `${diffDays} days ago`; const diffMonths = Math.floor(diffDays / 30); return `${diffMonths} months ago`; } export default function IssuesList({ issues, openCount, closedCount, repoOwner, repoName }: Props) { const [filter, setFilter] = useState<"open" | "closed" | "all">("open"); const [searchQuery, setSearchQuery] = useState(""); const [groupByEpic, setGroupByEpic] = useState(false); const [expandedEpics, setExpandedEpics] = useState>({}); const toggleEpic = (id: string, e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); setExpandedEpics(prev => ({ ...prev, [id]: !prev[id] })); }; const filteredIssues = issues.filter(issue => { const matchesFilter = filter === "all" || issue.state === filter; const matchesSearch = issue.title.toLowerCase().includes(searchQuery.toLowerCase()); // If grouping by epic, hide sub-tasks from top level (they appear inside epics) // Unless they are orphans (parent not found in current list? actually logic is simpler: hide if has parent) // But wait, if I hide if has parent, and the parent is NOT in the list (e.g. parent is closed but filter is open), then the sub-task disappears? // Correct. Standard Epic view usually implies viewing the Epic to see children. // But if filtering by "Open", we maybe want to see open tasks of a closed Epic? // GitHub doesn't really have "Group by Epic" in issue list. Jira does. // Let's stick to: If groupByEpic is on, show top-level items only (no parent). // If an issue has a parent, it is hidden from top level. const isTopLevel = !groupByEpic || !issue.parent; return matchesFilter && matchesSearch && isTopLevel; }); return (
{/* Enhanced Header with Glow */} {/* Search Input with Glass Effect */}
setSearchQuery(e.target.value)} className="w-full rounded-xl border border-border bg-secondary backdrop-blur-sm pl-10 pr-4 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-cyan-500/50 focus:ring-2 focus:ring-cyan-500/20 transition-all" />
{/* Action Buttons */}
Labels Milestones New Issue
{/* Issues Container with Glass Effect */} {/* Gradient border effect */}
{/* Filter Header */}
{/* Issues List */}
{filteredIssues.length > 0 ? ( filteredIssues.map((issue, index) => (
{/* Expand/Collapse for Epics */} {groupByEpic && issue.type === 'epic' && issue.children && issue.children.length > 0 && ( )} {/* Status Icon */}
{issue.state === "open" ? (
) : (
)}
{/* Type Icon */}
{issue.type === "epic" ? ( ) : issue.type === "task" ? ( ) : (
)}
{/* Content */}
{issue.title} {/* Labels */} {issue.labels?.map(label => ( {label.name} ))}
#{issue.number} • opened {timeAgo(issue.createdAt)} • {issue.author.avatarUrl ? ( ) : (
{issue.author.username[0].toUpperCase()}
)} {issue.author.username}
{/* Comment Count */} {issue.commentCount > 0 && (
{issue.commentCount}
)} {/* Render Children (Sub-tasks) */} {groupByEpic && issue.type === 'epic' && expandedEpics[issue.id] && issue.children && ( )}
)) ) : (

{searchQuery ? "No matching issues" : "Welcome to issues!"}

{searchQuery ? "Try adjusting your search or filter to find what you're looking for." : "Issues are used to track todos, bugs, feature requests, and more."}

{!searchQuery && ( Create the first issue )} )}
); }