'use client'; import { useState, useEffect } 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 { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import DataTable from '@/components/admin/DataTable'; import DetailModal from '@/components/admin/DetailModal'; import { MessageSquare, ArrowLeft, RefreshCw, Filter, Calendar, User } from 'lucide-react'; import Link from 'next/link'; const NOTE_TYPE: Record = { 1: 'Task Detail', 2: 'Time Entry Note', 3: 'Ticket Detail', }; const PUBLISH_MAP: Record = { 1: { label: 'All Users', cls: 'bg-green-500/15 text-green-600 border border-green-500/30' }, 2: { label: 'Internal', cls: 'bg-amber-500/15 text-amber-700 border border-amber-500/30' }, 4: { label: 'Internal Only', cls: 'bg-slate-500/15 text-slate-500 border border-slate-500/30' }, }; export default function TicketNotesPage() { const [notes, setNotes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selected, setSelected] = useState(null); const [showModal, setShowModal] = useState(false); const [totalCount, setTotalCount] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(100); const [search, setSearch] = useState(''); const [sortBy, setSortBy] = useState('create_date_time'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const fetchNotes = async (page = 1, overrideSortBy = sortBy, overrideSortOrder = sortOrder) => { setLoading(true); setError(null); try { const params = new URLSearchParams(); if (search) params.append('search', search); params.append('limit', pageSize.toString()); params.append('offset', ((page - 1) * pageSize).toString()); params.append('sort_by', overrideSortBy); params.append('sort_order', overrideSortOrder); const res = await fetch(`/api/data/ticket-notes?${params}`); if (!res.ok) throw new Error(res.statusText); const data = await res.json(); setNotes(data.ticketNotes || []); setTotalCount(data.pagination?.total || 0); setCurrentPage(page); } catch (e) { setError(e instanceof Error ? e.message : 'Unknown error'); } finally { setLoading(false); } }; useEffect(() => { fetchNotes(1); }, [pageSize]); const handleSort = (col: string, dir: 'asc' | 'desc') => { setSortBy(col); setSortOrder(dir); fetchNotes(1, col, dir); }; const columns = [ { key: 'ticket_number', label: 'Ticket', sortable: false, render: (v: string) => v ? {v} : , }, { key: 'create_date_time', label: 'Date', sortable: true, render: (v: string) => v ? {new Date(v).toLocaleDateString()} : , }, { key: 'creator_name', label: 'Creator', sortable: false, render: (v: string) => v ? {v} : , }, { key: 'publish', label: 'Visibility', sortable: true, render: (v: number) => { const p = PUBLISH_MAP[v]; return p ? {p.label} : {v}; }, }, { key: 'note_type', label: 'Type', sortable: true, render: (v: number) => {NOTE_TYPE[v] ?? `Type ${v}`}, }, { key: 'title', label: 'Title', sortable: true, render: (v: string) =>
{v || No title}
, }, { key: 'description', label: 'Preview', sortable: false, render: (v: string) =>
{v || '—'}
, }, ]; return (

Ticket Notes

Browse notes attached to tickets

Filters
setSearch(e.target.value)} onKeyDown={e => e.key === 'Enter' && fetchNotes(1)} />
Ticket Notes ({totalCount.toLocaleString()} total) Showing {notes.length} of {totalCount.toLocaleString()} • Click a row to view details {error && (

{error}

)} { setSelected(row); setShowModal(true); }} totalCount={totalCount} page={currentPage} pageSize={pageSize} onPageChange={p => fetchNotes(p)} onSort={handleSort} />
); }