feat(ticket-list): added a route to fetch all tickets, filtered in a precis order

This commit is contained in:
Matthieu Haineault 2026-03-02 12:29:01 -05:00
parent d14924783d
commit f43d3ade19
8 changed files with 191 additions and 69 deletions

View File

@ -1,13 +1,17 @@
import { Module } from "@nestjs/common";
import { CreateTicketService } from "src/customer-support/tickets/services/create-ticket.service";
import { GetTicketListService } from "src/customer-support/tickets/services/get-ticket-list.service";
import { UpdateTicketService } from "src/customer-support/tickets/services/update-ticket.service";
import { TicketController } from "src/customer-support/tickets/ticket.controller";
import { TicketService } from "src/customer-support/tickets/ticket.service";
@Module({
controllers: [
TicketController
],
providers: [
TicketService
GetTicketListService,
CreateTicketService,
UpdateTicketService,
],
}) export class CustomerSupportModule { }

View File

@ -1 +1 @@
GET http://localhost:3000/tickets/OPEN?sortOrder=last_update&sortTypes=DESC&offset=0&limit=10&email=gilles@targointernet.com
GET http://localhost:3000/tickets/OPEN?sortOrder=last_update&sortTypes=DESC&offset=0&limit=30&email=gilles@targointernet.com

View File

@ -0,0 +1,11 @@
import { Injectable } from "@nestjs/common";
import { PrismaMariaDbService } from "prisma/mariadb/prisma-mariadb.service";
@Injectable()
export class CreateTicketService {
constructor(private readonly prisma: PrismaMariaDbService) { }
createTicket = async () => {
}
}

View File

@ -0,0 +1,132 @@
import { Injectable } from "@nestjs/common";
import { Result } from "src/common/errors/result-error.factory";
import { PrismaMariaDbService } from "prisma/mariadb/prisma-mariadb.service";
import { TicketList } from "src/customer-support/tickets/dtos/ticket-list.dto";
import { sortOrders, sortTypes } from "src/customer-support/tickets/dtos/ticket.dto";
import { Prisma } from "prisma/mariadb/generated/prisma/client/mariadb/client";
export const TIMESTAMP_MINIMUM = 1672531200; // 1er janvier 2023
@Injectable()
export class GetTicketListService {
constructor(private readonly prisma: PrismaMariaDbService) { }
getListOfAllTicketByFilters = async (
status: string[],
offset: number = 0,
limit: number = 25,
sortOrder: string = 'last_update',
sortType: 'DESC' | 'ASC' = 'DESC',
email?: string
): Promise<Result<TicketList[], string>> => {
try {
const staff = await this.prisma.staff.findFirst({
where: { email },
});
if (!staff) return { success: false, error: 'EMPLOYEE_NOT_FOUND' }
if (!sortOrders.includes(sortOrder) || !sortTypes.includes(sortType)) return { success: false, error: 'INVALID_FILTER' }
const statusList = Prisma.join(status);
const rawTicketList = await this.prisma.$queryRaw<TicketList[]>(
Prisma.sql`
SELECT
t.id AS id,
t.status AS status,
CONCAT(s.first_name,' ',s.last_name) AS assignTo,
a.address1 AS deliveryAddress,
t.subject AS subject,
d.name AS department,
t.parent AS parentTicketId,
CASE
WHEN t.due_date > 1672531200
THEN FROM_UNIXTIME(t.due_date, '%d/%m/%y')
ELSE NULL
END AS dueDate,
FROM_UNIXTIME(t.last_update, '%d/%m/%y') AS lastUpdate,
CASE
WHEN CAST(t.date_closed AS UNSIGNED) > 1672531200
THEN FROM_UNIXTIME(CAST(t.date_closed AS UNSIGNED), '%d/%m/%y')
ELSE NULL
END AS completedAt
FROM staff s
LEFT JOIN ticket t ON t.assign_to = s.id
LEFT JOIN account a ON t.account_id = a.id
LEFT JOIN ticket_dept d ON t.dept_id = d.id
WHERE s.id = ${staff.id}
AND t.status IN (${statusList})
ORDER BY ${Prisma.raw(sortOrder)} ${Prisma.raw(sortType)}
LIMIT ${limit} OFFSET ${offset}
`);
return { success: true, data: rawTicketList }
} catch (error) {
return { success: false, error: 'TICKET_LIST_NOT_FOUND, ' + error };
}
}
getListOfTickets = async (
email: string,
offset: number = 0,
limit: number = 25,
): Promise<Result<TicketList[], string>> => {
try {
const staff = await this.prisma.staff.findFirst({
where: { email },
});
if (!staff) return { success: false, error: 'EMPLOYEE_NOT_FOUND' }
const rawTicketList = await this.prisma.$queryRaw<TicketList[]>(
Prisma.sql`
SELECT
t.id AS id,
t.status AS status,
CONCAT(s.first_name,' ',s.last_name) AS assignTo,
a.address1 AS deliveryAddress,
t.subject AS subject,
d.name AS department,
t.parent AS parentTicketId,
CASE
WHEN t.due_date > 1672531200
THEN FROM_UNIXTIME(t.due_date, '%d/%m/%y')
ELSE NULL
END AS dueDate,
FROM_UNIXTIME(t.last_update, '%d/%m/%y') AS lastUpdate,
CASE
WHEN CAST(t.date_closed AS UNSIGNED) > 1672531200
THEN FROM_UNIXTIME(CAST(t.date_closed AS UNSIGNED), '%d/%m/%y')
ELSE NULL
END AS completedAt
FROM staff s
LEFT JOIN ticket t ON t.assign_to = s.id
LEFT JOIN account a ON t.account_id = a.id
LEFT JOIN ticket_dept d ON t.dept_id = d.id
WHERE s.id = ${staff.id}
ORDER BY
CASE
WHEN t.status = 'open' AND t.assign_to = 0 THEN 0
WHEN t.status = 'open' AND t.assign_to = s.id THEN 1
WHEN t.status = 'pending' AND t.assign_to = s.id THEN 2
WHEN t.status = 'open' AND t.assign_to != s.id THEN 3
WHEN t.status = 'pending' AND t.assign_to != s.id THEN 4
ELSE 5
END
LIMIT ${limit} OFFSET ${offset};
`);
return { success: true, data: rawTicketList }
} catch (error) {
return { success: false, error: 'TICKET_LIST_NOT_FOUND, ' + error };
}
}
}

View File

@ -0,0 +1,24 @@
import { Injectable } from "@nestjs/common";
import { PrismaMariaDbService } from "prisma/mariadb/prisma-mariadb.service";
import { Result } from "src/common/errors/result-error.factory";
@Injectable()
export class UpdateTicketService {
constructor(private readonly prisma: PrismaMariaDbService) { }
updateTicketById = async (
ticketId: number
): Promise<Result<boolean, string>> => {
try {
await this.prisma.ticket.update({
where: { id: ticketId },
data: {}
})
return { success: true, data: true }
} catch (error) {
console.error(error)
return { success: false, error: 'INVALID_TICKET_ID' }
}
}
}

View File

@ -2,12 +2,12 @@ import { Controller, Get, Param, ParseIntPipe, Query } from "@nestjs/common";
import { Access } from "src/common/decorators/module-access.decorators";
import { Result } from "src/common/errors/result-error.factory";
import { TicketList } from "src/customer-support/tickets/dtos/ticket-list.dto";
import { TicketService } from "src/customer-support/tickets/ticket.service";
import { GetTicketListService } from "src/customer-support/tickets/services/get-ticket-list.service";
@Controller('tickets')
export class TicketController {
constructor(private readonly getService: TicketService) { }
constructor(private readonly getService: GetTicketListService) { }
@Get(':status')
async findTicketByFilters(
@ -31,4 +31,17 @@ export class TicketController {
queryEmail,
);
}
@Get('list')
async findTicketList(
@Access('email') email:string,
@Query('offset', ParseIntPipe) offset: number,
@Query('limit', ParseIntPipe) limit: number,
) {
return await this.getService.getListOfTickets(
email,
offset,
limit
);
}
}

View File

@ -1,10 +1,10 @@
import { Module } from "@nestjs/common";
import { GetTicketListService } from "src/customer-support/tickets/services/get-ticket-list.service";
import { TicketController } from "src/customer-support/tickets/ticket.controller";
import { TicketService } from "src/customer-support/tickets/ticket.service";
@Module({
imports: [
TicketService
GetTicketListService
],
providers: [
TicketController

View File

@ -1,62 +0,0 @@
import { Injectable } from "@nestjs/common";
import { Result } from "src/common/errors/result-error.factory";
import { PrismaMariaDbService } from "prisma/mariadb/prisma-mariadb.service";
import { TicketList } from "src/customer-support/tickets/dtos/ticket-list.dto";
import { sortOrders, sortTypes } from "src/customer-support/tickets/dtos/ticket.dto";
import { Prisma } from "prisma/mariadb/generated/prisma/client/mariadb/client";
@Injectable()
export class TicketService {
constructor(private readonly prisma: PrismaMariaDbService) { }
getListOfAllTicketByFilters = async (
status: string[],
offset: number = 0,
limit: number = 25,
sortOrder: string = 'last_update',
sortType: 'DESC' | 'ASC' = 'DESC',
email?: string
): Promise<Result<TicketList[], string>> => {
try {
const staff = await this.prisma.staff.findFirst({
where: { email },
});
if (!staff) return { success: false, error: 'EMPLOYEE_NOT_FOUND' }
if (!sortOrders.includes(sortOrder) || !sortTypes.includes(sortType)) return { success: false, error: 'INVALID_FILTER' }
const statusList = Prisma.join(status);
const rawticketList = await this.prisma.$queryRaw<TicketList[]>(
Prisma.sql`
SELECT
t.id AS id,
t.status AS status,
CONCAT(s.first_name,' ',s.last_name) AS assignTo,
a.address1 AS deliveryAddress,
t.subject AS subject,
d.name AS department,
t.parent AS parentTicketId,
DATE_FORMAT(FROM_UNIXTIME(t.due_date / 1000), '%d/%m/%y') AS dueDate,
DATE_FORMAT(FROM_UNIXTIME(t.last_update / 1000), '%d/%m/%y') AS updatedAt,
CASE
WHEN t.date_closed IS NOT NULL
THEN DATE_FORMAT(FROM_UNIXTIME(t.date_closed / 1000), '%d/%m/%y')
ELSE NULL
END AS completedAt
FROM staff s
LEFT JOIN ticket t ON t.assign_to = s.id
LEFT JOIN account a ON t.account_id = a.id
LEFT JOIN ticket_dept d ON t.dept_id = d.id
WHERE s.email = ${email}
AND t.status IN (${statusList})
ORDER BY ${Prisma.raw(sortOrder)} ${Prisma.raw(sortType)}
LIMIT ${limit} OFFSET ${offset};
`);
return { success: true, data: rawticketList }
} catch (error) {
return { success: false, error: 'TICKET_LIST_NOT_FOUND, ' + error };
}
}
}