All files / src/app/api/admin/support/stats route.ts

0% Statements 0/163
100% Branches 0/0
0% Functions 0/1
0% Lines 0/163

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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164                                                                                                                                                                                                                                                                                                                                       
export const dynamic = "force-dynamic";

/**
 * Admin Support Statistics API
 * GET /api/admin/support/stats - Get support dashboard statistics
 */

import { NextRequest, NextResponse } from 'next/server';
import { requireAdminRole, handleAuthError } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import { TicketStatus, TicketCategory, TicketPriority, SupportStats } from '@/types/support';
import { handleError } from '@/lib/error-handler';
import { logger } from '@/lib/logging';

export async function GET(request: NextRequest) {
  try {
    await requireAdminRole();

    // Parse date range from query params
    const { searchParams } = new URL(request.url);
    const daysBack = parseInt(searchParams.get('days') || '30', 10);
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - daysBack);

    // Get total ticket counts
    const totalTickets = await prisma.supportTicket.count();

    const openTickets = await prisma.supportTicket.count({
      where: {
        status: {
          in: [
            TicketStatus.OPEN,
            TicketStatus.IN_PROGRESS,
            TicketStatus.AWAITING_CUSTOMER,
            TicketStatus.AWAITING_AGENT,
          ]}}});

    const resolvedTickets = await prisma.supportTicket.count({
      where: {
        status: {
          in: [TicketStatus.RESOLVED, TicketStatus.CLOSED]},
        resolvedAt: { gte: startDate }}});

    // Get tickets by status
    const ticketsByStatus: Record<TicketStatus, number> = {} as Record<TicketStatus, number>;
    for (const status of Object.values(TicketStatus)) {
      ticketsByStatus[status] = await prisma.supportTicket.count({
        where: { status }});
    }

    // Get tickets by category
    const ticketsByCategory: Record<TicketCategory, number> = {} as Record<
      TicketCategory,
      number
    >;
    for (const category of Object.values(TicketCategory)) {
      ticketsByCategory[category] = await prisma.supportTicket.count({
        where: { category }});
    }

    // Get tickets by priority
    const ticketsByPriority: Record<TicketPriority, number> = {} as Record<
      TicketPriority,
      number
    >;
    for (const priority of Object.values(TicketPriority)) {
      ticketsByPriority[priority] = await prisma.supportTicket.count({
        where: { priority }});
    }

    // Calculate average response time (in seconds)
    const ticketsWithResponse = await prisma.supportTicket.findMany({
      where: {
        firstResponseAt: { not: null },
        createdAt: { gte: startDate }},
      select: {
        createdAt: true,
        firstResponseAt: true}});

    let avgResponseTime: number | null = null;
    if (ticketsWithResponse.length > 0) {
      const totalResponseTime = ticketsWithResponse.reduce((sum, ticket) => {
        if (ticket.firstResponseAt) {
          return sum + (ticket.firstResponseAt.getTime() - ticket.createdAt.getTime());
        }
        return sum;
      }, 0);
      avgResponseTime = Math.round(totalResponseTime / ticketsWithResponse.length / 1000);
    }

    // Calculate average resolution time (in seconds)
    const resolvedTicketsWithTime = await prisma.supportTicket.findMany({
      where: {
        resolvedAt: { not: null },
        createdAt: { gte: startDate }},
      select: {
        createdAt: true,
        resolvedAt: true}});

    let avgResolutionTime: number | null = null;
    if (resolvedTicketsWithTime.length > 0) {
      const totalResolutionTime = resolvedTicketsWithTime.reduce((sum, ticket) => {
        if (ticket.resolvedAt) {
          return sum + (ticket.resolvedAt.getTime() - ticket.createdAt.getTime());
        }
        return sum;
      }, 0);
      avgResolutionTime = Math.round(
        totalResolutionTime / resolvedTicketsWithTime.length / 1000
      );
    }

    // Calculate customer satisfaction
    const surveys = await prisma.ticketSurvey.findMany({
      where: {
        createdAt: { gte: startDate }},
      select: { rating: true }});

    let customerSatisfaction: number | null = null;
    if (surveys.length > 0) {
      const totalRating = surveys.reduce((sum, survey) => sum + survey.rating, 0);
      customerSatisfaction = Math.round((totalRating / surveys.length) * 10) / 10;
    }

    // Get recent trends (tickets per day for chart)
    const ticketTrends = await prisma.$queryRaw<{ date: Date; count: bigint }[]>`
      SELECT DATE(createdAt) as date, COUNT(*) as count
      FROM support_tickets
      WHERE createdAt >= ${startDate}
      GROUP BY DATE(createdAt)
      ORDER BY date ASC
    `;

    const stats: SupportStats = {
      totalTickets,
      openTickets,
      resolvedTickets,
      avgResponseTime,
      avgResolutionTime,
      customerSatisfaction,
      ticketsByStatus,
      ticketsByCategory,
      ticketsByPriority};

    return NextResponse.json({
      success: true,
      data: {
        ...stats,
        trends: ticketTrends.map((t) => ({
          date: t.date,
          count: Number(t.count)})),
        period: {
          start: startDate,
          end: new Date(),
          days: daysBack}}});
  } catch (error) {
    const authResponse = handleAuthError(error as Error);
    if (authResponse) return authResponse;

    logger.error('Error fetching support stats', error instanceof Error ? error : new Error(String(error)), { category: 'ADMIN_SUPPORT' });
    return handleError(error);
  }
}