Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 'use client'; /** * TicketList - List of customer's support tickets */ import React from 'react'; import Link from 'next/link'; import TicketCard from './TicketCard'; import { SupportTicketWithRelations, TicketStatus } from '@/types/support'; import { Icon } from '@/components/ui/icons'; export interface TicketListProps { /** List of tickets to display */ tickets: SupportTicketWithRelations[]; /** Currently active status filter */ statusFilter?: TicketStatus | 'all'; /** Handler for status filter change */ onStatusFilterChange?: (status: TicketStatus | 'all') => void; /** Whether data is loading */ isLoading?: boolean; } const STATUS_FILTERS: { value: TicketStatus | 'all'; label: string }[] = [ { value: 'all', label: 'All Tickets' }, { value: TicketStatus.OPEN, label: 'Open' }, { value: TicketStatus.IN_PROGRESS, label: 'In Progress' }, { value: TicketStatus.RESOLVED, label: 'Resolved' }, { value: TicketStatus.CLOSED, label: 'Closed' }, ]; export default function TicketList({ tickets, statusFilter = 'all', onStatusFilterChange, isLoading = false}: TicketListProps) { return ( <div> {/* Header */} <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6"> <h1 className="text-2xl font-bold text-gray-900">My Support Tickets</h1> <Link href="/support/tickets/new" className=" inline-flex items-center justify-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg font-medium text-sm hover:bg-blue-700 transition-colors " > <Icon name="plus" size={16} /> New Ticket </Link> </div> {/* Status Filter */} <div className="flex flex-wrap gap-2 mb-6"> {STATUS_FILTERS.map((filter) => ( <button key={filter.value} type="button" onClick={() => onStatusFilterChange?.(filter.value)} className={` px-4 py-2 rounded-lg text-sm font-medium transition-colors ${ statusFilter === filter.value ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' } `} > {filter.label} </button> ))} </div> {/* Loading State */} {isLoading && ( <div className="space-y-4"> {[1, 2, 3].map((i) => ( <div key={i} className="bg-white rounded-lg border border-gray-200 p-6 animate-pulse" > <div className="h-4 bg-gray-200 rounded w-1/4 mb-4" /> <div className="h-6 bg-gray-200 rounded w-3/4 mb-2" /> <div className="h-4 bg-gray-200 rounded w-1/2" /> </div> ))} </div> )} {/* Empty State */} {!isLoading && tickets.length === 0 && ( <div className="bg-white rounded-lg border border-gray-200 p-12 text-center"> <div className="w-16 h-16 mx-auto mb-4 bg-gray-100 rounded-full flex items-center justify-center"> <Icon name="clipboard-list" size={32} className="text-gray-400" /> </div> <h3 className="text-lg font-medium text-gray-900 mb-2">No tickets found</h3> <p className="text-gray-500 mb-6"> {statusFilter === 'all' ? "You haven't created any support tickets yet." : `No tickets with "${statusFilter}" status.`} </p> <Link href="/support/tickets/new" className=" inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg font-medium text-sm hover:bg-blue-700 transition-colors " > Create Your First Ticket </Link> </div> )} {/* Ticket Cards */} {!isLoading && tickets.length > 0 && ( <div className="space-y-4"> {tickets.map((ticket) => ( <TicketCard key={ticket.id} ticket={ticket} /> ))} </div> )} </div> ); } |