feat: unanswered ticket badges, dashboard counts, cross-department ticket creation
- Add /tickets/unanswered-counts API endpoint for staff/admin - Show red badge with unanswered count on sidebar nav (staff tickets, admin tickets) - Add summary cards (unanswered/answered/total) to staff tickets page - Highlight unanswered count in admin tickets stats cards - Staff can create tickets to OTHER departments only (not their own) - Frontend hides own department from ticket creation dropdown - Sidebar badges auto-refresh every 30 seconds
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import Link from 'next/link';
|
||||
import api from '@/lib/api';
|
||||
import { useAuthStore } from '@/lib/store';
|
||||
import toast from 'react-hot-toast';
|
||||
import type { Ticket, CreateTicketDto, TicketDepartment, TicketPriority } from '@/types';
|
||||
|
||||
@@ -22,10 +23,21 @@ const priorityColors: Record<string, string> = {
|
||||
|
||||
export default function TicketsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
|
||||
// Staff can only create tickets to other departments
|
||||
const availableDepartments: { value: TicketDepartment; label: string }[] = [];
|
||||
if (user?.role !== 'technical') {
|
||||
availableDepartments.push({ value: 'technical', label: '🔧 Technical Support' });
|
||||
}
|
||||
if (user?.role !== 'sales') {
|
||||
availableDepartments.push({ value: 'sales', label: '💼 Sales' });
|
||||
}
|
||||
const canCreateTicket = availableDepartments.length > 0;
|
||||
const [form, setForm] = useState<CreateTicketDto>({
|
||||
subject: '',
|
||||
department: 'technical',
|
||||
department: availableDepartments[0]?.value || 'technical',
|
||||
priority: 'medium',
|
||||
message: '',
|
||||
});
|
||||
@@ -41,7 +53,7 @@ export default function TicketsPage() {
|
||||
toast.success('Ticket created successfully');
|
||||
queryClient.invalidateQueries({ queryKey: ['my-tickets'] });
|
||||
setShowCreate(false);
|
||||
setForm({ subject: '', department: 'technical', priority: 'medium', message: '' });
|
||||
setForm({ subject: '', department: availableDepartments[0]?.value || 'technical', priority: 'medium', message: '' });
|
||||
},
|
||||
onError: (err: any) => toast.error(err.response?.data?.message || 'Failed to create ticket'),
|
||||
});
|
||||
@@ -58,13 +70,15 @@ export default function TicketsPage() {
|
||||
<h1 className="text-2xl font-bold text-gray-900">My Tickets</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">Support tickets and their status</p>
|
||||
</div>
|
||||
<button onClick={() => setShowCreate(!showCreate)} className="btn-primary">
|
||||
{showCreate ? '✕ Cancel' : '+ New Ticket'}
|
||||
</button>
|
||||
{canCreateTicket && (
|
||||
<button onClick={() => setShowCreate(!showCreate)} className="btn-primary">
|
||||
{showCreate ? '✕ Cancel' : '+ New Ticket'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Create Ticket Form */}
|
||||
{showCreate && (
|
||||
{showCreate && canCreateTicket && (
|
||||
<form onSubmit={handleSubmit} className="card p-6 space-y-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Create New Ticket</h2>
|
||||
|
||||
@@ -89,8 +103,9 @@ export default function TicketsPage() {
|
||||
value={form.department}
|
||||
onChange={(e) => setForm({ ...form, department: e.target.value as TicketDepartment })}
|
||||
>
|
||||
<option value="technical">🔧 Technical Support</option>
|
||||
<option value="sales">💼 Sales</option>
|
||||
{availableDepartments.map((dept) => (
|
||||
<option key={dept.value} value={dept.value}>{dept.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user