fediversion/frontend/app/bugs/my-tickets/page.tsx
fullsizemalt b4cddf41ea feat: Initialize Fediversion multi-band platform
- Fork elmeg-demo codebase for multi-band support
- Add data importer infrastructure with base class
- Create band-specific importers:
  - phish.py: Phish.net API v5
  - grateful_dead.py: Grateful Stats API
  - setlistfm.py: Dead & Company, Billy Strings (Setlist.fm)
- Add spec-kit configuration for Gemini
- Update README with supported bands and architecture
2025-12-28 12:39:28 -08:00

146 lines
6 KiB
TypeScript

"use client"
/**
* My Tickets Page - View user's submitted tickets
*/
import { useEffect, useState } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { useAuth } from "@/contexts/auth-context"
import { getApiUrl } from "@/lib/api-config"
import Link from "next/link"
import { ArrowLeft, Clock, CheckCircle, AlertCircle, Loader2 } from "lucide-react"
interface Ticket {
id: number
ticket_number: string
type: string
status: string
priority: string
title: string
created_at: string
updated_at: string
}
const STATUS_STYLES: Record<string, { label: string; variant: "default" | "secondary" | "destructive" | "outline" }> = {
open: { label: "Open", variant: "default" },
in_progress: { label: "In Progress", variant: "secondary" },
resolved: { label: "Resolved", variant: "outline" },
closed: { label: "Closed", variant: "outline" },
}
const PRIORITY_COLORS: Record<string, string> = {
low: "bg-gray-500",
medium: "bg-yellow-500",
high: "bg-orange-500",
critical: "bg-red-500",
}
export default function MyTicketsPage() {
const { user } = useAuth()
const [tickets, setTickets] = useState<Ticket[]>([])
const [loading, setLoading] = useState(true)
useEffect(() => {
if (user) {
fetchTickets()
}
}, [user])
const fetchTickets = async () => {
try {
const token = localStorage.getItem("token")
const res = await fetch(`${getApiUrl()}/tickets/my-tickets`, {
headers: { Authorization: `Bearer ${token}` },
})
if (res.ok) {
const data = await res.json()
setTickets(data)
}
} catch (e) {
console.error("Failed to fetch tickets", e)
} finally {
setLoading(false)
}
}
if (!user) {
return (
<div className="container max-w-2xl py-16 text-center">
<h1 className="text-2xl font-bold mb-4">Please Log In</h1>
<p className="text-muted-foreground mb-6">
You need to be logged in to view your tickets.
</p>
<Link href="/login">
<Button>Log In</Button>
</Link>
</div>
)
}
return (
<div className="container max-w-4xl py-8">
<div className="flex items-center gap-4 mb-8">
<Link href="/bugs">
<Button variant="ghost" size="icon">
<ArrowLeft className="h-4 w-4" />
</Button>
</Link>
<div>
<h1 className="text-3xl font-bold">My Tickets</h1>
<p className="text-muted-foreground">Track your submitted issues</p>
</div>
</div>
{loading ? (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
) : tickets.length === 0 ? (
<Card>
<CardContent className="py-12 text-center">
<p className="text-muted-foreground mb-4">You haven't submitted any tickets yet.</p>
<Link href="/bugs">
<Button>Submit a Ticket</Button>
</Link>
</CardContent>
</Card>
) : (
<div className="space-y-4">
{tickets.map((ticket) => (
<Link key={ticket.id} href={`/bugs/ticket/${ticket.ticket_number}`}>
<Card className="hover:border-primary transition-colors cursor-pointer">
<CardContent className="py-4">
<div className="flex items-start justify-between gap-4">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-mono text-sm text-muted-foreground">
{ticket.ticket_number}
</span>
<span className={`inline-block w-2 h-2 rounded-full ${PRIORITY_COLORS[ticket.priority]}`} />
<Badge variant={STATUS_STYLES[ticket.status]?.variant || "default"}>
{STATUS_STYLES[ticket.status]?.label || ticket.status}
</Badge>
</div>
<h3 className="font-semibold truncate">{ticket.title}</h3>
<p className="text-sm text-muted-foreground">
{new Date(ticket.created_at).toLocaleDateString()}
</p>
</div>
<div className="flex items-center">
{ticket.status === "open" && <Clock className="h-4 w-4 text-muted-foreground" />}
{ticket.status === "resolved" && <CheckCircle className="h-4 w-4 text-green-500" />}
{ticket.status === "in_progress" && <AlertCircle className="h-4 w-4 text-yellow-500" />}
</div>
</div>
</CardContent>
</Card>
</Link>
))}
</div>
)}
</div>
)
}