Add repair list filters, template management page, and backend filter support
Repairs list now has a filter panel with status (defaults to active only), condition, batch/individual toggle, and date range filters for intake and promised dates. Added Batch column to the repairs table. Backend list endpoint accepts filter query params for status, condition, dates, and batch membership. Template management page (admin only) with CRUD for common repair services (rehair, string change, etc.) with instrument type, size, and default pricing. Sidebar updated with Repair Templates link gated on repairs.admin permission.
This commit is contained in:
@@ -27,8 +27,23 @@ export const repairRoutes: FastifyPluginAsync = async (app) => {
|
||||
})
|
||||
|
||||
app.get('/repair-tickets', { preHandler: [app.authenticate, app.requirePermission('repairs.view')] }, async (request, reply) => {
|
||||
const params = PaginationSchema.parse(request.query)
|
||||
const result = await RepairTicketService.list(app.db, request.companyId, params)
|
||||
const query = request.query as Record<string, string | undefined>
|
||||
const params = PaginationSchema.parse(query)
|
||||
|
||||
const filters = {
|
||||
status: query.status?.split(',').filter(Boolean),
|
||||
conditionIn: query.conditionIn?.split(',').filter(Boolean),
|
||||
isBatch: query.isBatch === 'true' ? true : query.isBatch === 'false' ? false : undefined,
|
||||
batchNumber: query.batchNumber,
|
||||
intakeDateFrom: query.intakeDateFrom,
|
||||
intakeDateTo: query.intakeDateTo,
|
||||
promisedDateFrom: query.promisedDateFrom,
|
||||
promisedDateTo: query.promisedDateTo,
|
||||
completedDateFrom: query.completedDateFrom,
|
||||
completedDateTo: query.completedDateTo,
|
||||
}
|
||||
|
||||
const result = await RepairTicketService.list(app.db, request.companyId, params, filters)
|
||||
return reply.send(result)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eq, and, count, type Column } from 'drizzle-orm'
|
||||
import { eq, and, count, inArray, isNull, isNotNull, gte, lte, type Column, type SQL } from 'drizzle-orm'
|
||||
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
|
||||
import {
|
||||
repairTickets,
|
||||
@@ -83,18 +83,50 @@ export const RepairTicketService = {
|
||||
return ticket ?? null
|
||||
},
|
||||
|
||||
async list(db: PostgresJsDatabase<any>, companyId: string, params: PaginationInput) {
|
||||
const baseWhere = eq(repairTickets.companyId, companyId)
|
||||
const searchCondition = params.q
|
||||
? buildSearchCondition(params.q, [
|
||||
repairTickets.ticketNumber,
|
||||
repairTickets.customerName,
|
||||
repairTickets.customerPhone,
|
||||
repairTickets.instrumentDescription,
|
||||
repairTickets.serialNumber,
|
||||
])
|
||||
: undefined
|
||||
const where = searchCondition ? and(baseWhere, searchCondition) : baseWhere
|
||||
async list(db: PostgresJsDatabase<any>, companyId: string, params: PaginationInput, filters?: {
|
||||
status?: string[]
|
||||
conditionIn?: string[]
|
||||
isBatch?: boolean
|
||||
batchNumber?: string
|
||||
intakeDateFrom?: string
|
||||
intakeDateTo?: string
|
||||
promisedDateFrom?: string
|
||||
promisedDateTo?: string
|
||||
completedDateFrom?: string
|
||||
completedDateTo?: string
|
||||
}) {
|
||||
const conditions: SQL[] = [eq(repairTickets.companyId, companyId)]
|
||||
|
||||
if (params.q) {
|
||||
const search = buildSearchCondition(params.q, [
|
||||
repairTickets.ticketNumber,
|
||||
repairTickets.customerName,
|
||||
repairTickets.customerPhone,
|
||||
repairTickets.instrumentDescription,
|
||||
repairTickets.serialNumber,
|
||||
])
|
||||
if (search) conditions.push(search)
|
||||
}
|
||||
|
||||
if (filters?.status?.length) {
|
||||
conditions.push(inArray(repairTickets.status, filters.status as any))
|
||||
}
|
||||
if (filters?.conditionIn?.length) {
|
||||
conditions.push(inArray(repairTickets.conditionIn, filters.conditionIn as any))
|
||||
}
|
||||
if (filters?.isBatch === true) {
|
||||
conditions.push(isNotNull(repairTickets.repairBatchId))
|
||||
} else if (filters?.isBatch === false) {
|
||||
conditions.push(isNull(repairTickets.repairBatchId))
|
||||
}
|
||||
if (filters?.intakeDateFrom) conditions.push(gte(repairTickets.intakeDate, new Date(filters.intakeDateFrom)))
|
||||
if (filters?.intakeDateTo) conditions.push(lte(repairTickets.intakeDate, new Date(filters.intakeDateTo)))
|
||||
if (filters?.promisedDateFrom) conditions.push(gte(repairTickets.promisedDate, new Date(filters.promisedDateFrom)))
|
||||
if (filters?.promisedDateTo) conditions.push(lte(repairTickets.promisedDate, new Date(filters.promisedDateTo)))
|
||||
if (filters?.completedDateFrom) conditions.push(gte(repairTickets.completedDate, new Date(filters.completedDateFrom)))
|
||||
if (filters?.completedDateTo) conditions.push(lte(repairTickets.completedDate, new Date(filters.completedDateTo)))
|
||||
|
||||
const where = and(...conditions)
|
||||
|
||||
const sortableColumns: Record<string, Column> = {
|
||||
ticket_number: repairTickets.ticketNumber,
|
||||
|
||||
Reference in New Issue
Block a user