feat: flight-detail CRUD on logistics router
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { z } from 'zod'
|
||||
import { FlightDetailStatus } from '@prisma/client'
|
||||
import { router, adminProcedure } from '../trpc'
|
||||
import { logAudit } from '../utils/audit'
|
||||
|
||||
@@ -53,4 +54,102 @@ export const logisticsRouter = router({
|
||||
})
|
||||
return hotel
|
||||
}),
|
||||
|
||||
/**
|
||||
* List all attending members for CONFIRMED finalists in a program, with
|
||||
* their (optional) flight details. One row per attendee — even those
|
||||
* without a FlightDetail row yet, so the UI can render empty editors.
|
||||
*/
|
||||
listFlightDetails: adminProcedure
|
||||
.input(z.object({ programId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return ctx.prisma.attendingMember.findMany({
|
||||
where: {
|
||||
confirmation: {
|
||||
status: 'CONFIRMED',
|
||||
project: { programId: input.programId },
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
needsVisa: true,
|
||||
user: { select: { id: true, name: true, email: true, country: true } },
|
||||
confirmation: {
|
||||
select: {
|
||||
project: {
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
country: true,
|
||||
competitionCategory: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
flightDetail: true,
|
||||
},
|
||||
orderBy: [{ user: { name: 'asc' } }],
|
||||
})
|
||||
}),
|
||||
|
||||
/** Create or update a flight detail row for an attending member. */
|
||||
upsertFlightDetail: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
attendingMemberId: z.string(),
|
||||
arrivalAt: z.date().nullable().optional(),
|
||||
arrivalFlightNumber: z.string().max(20).nullable().optional(),
|
||||
arrivalAirport: z.string().max(10).nullable().optional(),
|
||||
departureAt: z.date().nullable().optional(),
|
||||
departureFlightNumber: z.string().max(20).nullable().optional(),
|
||||
departureAirport: z.string().max(10).nullable().optional(),
|
||||
adminNotes: z.string().max(1000).nullable().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const { attendingMemberId, ...rest } = input
|
||||
// Strip out undefineds so an upsert update doesn't blow away unset fields.
|
||||
const data: Record<string, unknown> = {}
|
||||
for (const [k, v] of Object.entries(rest)) {
|
||||
if (v !== undefined) data[k] = v
|
||||
}
|
||||
const detail = await ctx.prisma.flightDetail.upsert({
|
||||
where: { attendingMemberId },
|
||||
create: { attendingMemberId, ...(data as object) },
|
||||
update: data,
|
||||
})
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'FLIGHT_DETAIL_UPSERT',
|
||||
entityType: 'FlightDetail',
|
||||
entityId: detail.id,
|
||||
detailsJson: { attendingMemberId },
|
||||
})
|
||||
return detail
|
||||
}),
|
||||
|
||||
/** Toggle PENDING ↔ CONFIRMED on a flight detail. */
|
||||
setFlightStatus: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
flightDetailId: z.string(),
|
||||
status: z.nativeEnum(FlightDetailStatus),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const detail = await ctx.prisma.flightDetail.update({
|
||||
where: { id: input.flightDetailId },
|
||||
data: { status: input.status },
|
||||
})
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'FLIGHT_STATUS_SET',
|
||||
entityType: 'FlightDetail',
|
||||
entityId: detail.id,
|
||||
detailsJson: { status: input.status },
|
||||
})
|
||||
return detail
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user