feat(applicant): My Logistics shows assigned hotel + room
Replace the program-level hotel.findUnique (broken after removing @unique)
with the caller's HotelStay (include hotel) on their AttendingMember.
Returns hotel: {name,address,link,notes}|null and room: {roomNumber,
checkInAt,checkOutAt}|null. MyLogisticsCard renders the Room section
(number + Monaco-time check-in/out) when room is present.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -175,7 +175,7 @@ export function MyLogisticsCard() {
|
|||||||
|
|
||||||
if (!data) return null
|
if (!data) return null
|
||||||
|
|
||||||
const { hotel, myFlight, visaVisible, myVisa } = data
|
const { hotel, room, myFlight, visaVisible, myVisa } = data
|
||||||
|
|
||||||
const hasFlightData =
|
const hasFlightData =
|
||||||
myFlight &&
|
myFlight &&
|
||||||
@@ -227,6 +227,38 @@ export function MyLogisticsCard() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Room */}
|
||||||
|
{room && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-muted-foreground text-xs font-medium uppercase tracking-wide">
|
||||||
|
Room
|
||||||
|
</p>
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{room.roomNumber && (
|
||||||
|
<p className="text-sm font-medium">Room {room.roomNumber}</p>
|
||||||
|
)}
|
||||||
|
{room.checkInAt && (
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
Check-in:{' '}
|
||||||
|
<span className="text-foreground">
|
||||||
|
{formatMonacoTime(room.checkInAt)}
|
||||||
|
</span>{' '}
|
||||||
|
<span className="text-xs text-muted-foreground">(Monaco time)</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{room.checkOutAt && (
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
Check-out:{' '}
|
||||||
|
<span className="text-foreground">
|
||||||
|
{formatMonacoTime(room.checkOutAt)}
|
||||||
|
</span>{' '}
|
||||||
|
<span className="text-xs text-muted-foreground">(Monaco time)</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Flights */}
|
{/* Flights */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-muted-foreground text-xs font-medium uppercase tracking-wide">
|
<p className="text-muted-foreground text-xs font-medium uppercase tracking-wide">
|
||||||
|
|||||||
@@ -2895,22 +2895,33 @@ export const applicantRouter = router({
|
|||||||
const confirmationId = project.finalistConfirmation.id
|
const confirmationId = project.finalistConfirmation.id
|
||||||
const visaVisible = project.program.visaStatusVisibleToMembers
|
const visaVisible = project.program.visaStatusVisibleToMembers
|
||||||
|
|
||||||
// Hotel (1:1 per program)
|
// Caller's own AttendingMember + hotel stay + flight + visa
|
||||||
const hotelRow = await ctx.prisma.hotel.findUnique({
|
|
||||||
where: { programId },
|
|
||||||
select: { name: true, address: true, link: true, notes: true },
|
|
||||||
})
|
|
||||||
const hotel = hotelRow ?? null
|
|
||||||
|
|
||||||
// Caller's own AttendingMember + flight + visa
|
|
||||||
const attendee = await ctx.prisma.attendingMember.findFirst({
|
const attendee = await ctx.prisma.attendingMember.findFirst({
|
||||||
where: { confirmationId, userId: ctx.user.id },
|
where: { confirmationId, userId: ctx.user.id },
|
||||||
include: {
|
include: {
|
||||||
|
hotelStay: { include: { hotel: true } },
|
||||||
flightDetail: true,
|
flightDetail: true,
|
||||||
visaApplication: visaVisible,
|
visaApplication: visaVisible,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const stay = attendee?.hotelStay ?? null
|
||||||
|
const hotel = stay?.hotel
|
||||||
|
? {
|
||||||
|
name: stay.hotel.name,
|
||||||
|
address: stay.hotel.address,
|
||||||
|
link: stay.hotel.link,
|
||||||
|
notes: stay.hotel.notes,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
const room = stay
|
||||||
|
? {
|
||||||
|
roomNumber: stay.roomNumber,
|
||||||
|
checkInAt: stay.checkInAt,
|
||||||
|
checkOutAt: stay.checkOutAt,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
|
||||||
const fd = attendee?.flightDetail ?? null
|
const fd = attendee?.flightDetail ?? null
|
||||||
const myFlight = fd
|
const myFlight = fd
|
||||||
? {
|
? {
|
||||||
@@ -2931,6 +2942,7 @@ export const applicantRouter = router({
|
|||||||
projectTitle: project.title,
|
projectTitle: project.title,
|
||||||
confirmationStatus: project.finalistConfirmation.status,
|
confirmationStatus: project.finalistConfirmation.status,
|
||||||
hotel,
|
hotel,
|
||||||
|
room,
|
||||||
myFlight,
|
myFlight,
|
||||||
visaVisible,
|
visaVisible,
|
||||||
myVisa,
|
myVisa,
|
||||||
|
|||||||
@@ -66,8 +66,16 @@ async function buildConfirmedFinalist() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Hotel for the program (1:1)
|
const attendee = await prisma.attendingMember.create({
|
||||||
await prisma.hotel.create({
|
data: {
|
||||||
|
confirmationId: confirmation.id,
|
||||||
|
userId: user.id,
|
||||||
|
needsVisa: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Hotel for the program + HotelStay assigning the attendee to it
|
||||||
|
const hotel = await prisma.hotel.create({
|
||||||
data: {
|
data: {
|
||||||
programId: program.id,
|
programId: program.id,
|
||||||
name: 'Hotel Hermitage',
|
name: 'Hotel Hermitage',
|
||||||
@@ -77,11 +85,13 @@ async function buildConfirmedFinalist() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const attendee = await prisma.attendingMember.create({
|
await prisma.hotelStay.create({
|
||||||
data: {
|
data: {
|
||||||
confirmationId: confirmation.id,
|
attendingMemberId: attendee.id,
|
||||||
userId: user.id,
|
hotelId: hotel.id,
|
||||||
needsVisa: true,
|
roomNumber: '204',
|
||||||
|
checkInAt: new Date('2026-06-20T14:00:00Z'),
|
||||||
|
checkOutAt: new Date('2026-06-23T11:00:00Z'),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -125,6 +135,9 @@ describe('applicant.getMyLogistics', () => {
|
|||||||
await prisma.flightDetail.deleteMany({
|
await prisma.flightDetail.deleteMany({
|
||||||
where: { attendingMember: { confirmation: { project: { programId } } } },
|
where: { attendingMember: { confirmation: { project: { programId } } } },
|
||||||
})
|
})
|
||||||
|
await prisma.hotelStay.deleteMany({
|
||||||
|
where: { attendingMember: { confirmation: { project: { programId } } } },
|
||||||
|
})
|
||||||
await prisma.hotel.deleteMany({ where: { programId } })
|
await prisma.hotel.deleteMany({ where: { programId } })
|
||||||
await prisma.attendingMember.deleteMany({
|
await prisma.attendingMember.deleteMany({
|
||||||
where: { confirmation: { project: { programId } } },
|
where: { confirmation: { project: { programId } } },
|
||||||
@@ -154,6 +167,8 @@ describe('applicant.getMyLogistics', () => {
|
|||||||
expect(result!.confirmationStatus).toBe('CONFIRMED')
|
expect(result!.confirmationStatus).toBe('CONFIRMED')
|
||||||
expect(result!.hotel).not.toBeNull()
|
expect(result!.hotel).not.toBeNull()
|
||||||
expect(result!.hotel!.name).toBe('Hotel Hermitage')
|
expect(result!.hotel!.name).toBe('Hotel Hermitage')
|
||||||
|
expect(result!.room).not.toBeNull()
|
||||||
|
expect(result!.room!.roomNumber).toBe('204')
|
||||||
expect(result!.myFlight).not.toBeNull()
|
expect(result!.myFlight).not.toBeNull()
|
||||||
expect(result!.myFlight!.arrivalFlightNumber).toBe('AF1234')
|
expect(result!.myFlight!.arrivalFlightNumber).toBe('AF1234')
|
||||||
expect(result!.myFlight!.arrivalAirport).toBe('NCE')
|
expect(result!.myFlight!.arrivalAirport).toBe('NCE')
|
||||||
@@ -227,6 +242,9 @@ describe('applicant.updateMyVisaNationality', () => {
|
|||||||
await prisma.flightDetail.deleteMany({
|
await prisma.flightDetail.deleteMany({
|
||||||
where: { attendingMember: { confirmation: { project: { programId } } } },
|
where: { attendingMember: { confirmation: { project: { programId } } } },
|
||||||
})
|
})
|
||||||
|
await prisma.hotelStay.deleteMany({
|
||||||
|
where: { attendingMember: { confirmation: { project: { programId } } } },
|
||||||
|
})
|
||||||
await prisma.hotel.deleteMany({ where: { programId } })
|
await prisma.hotel.deleteMany({ where: { programId } })
|
||||||
await prisma.attendingMember.deleteMany({
|
await prisma.attendingMember.deleteMany({
|
||||||
where: { confirmation: { project: { programId } } },
|
where: { confirmation: { project: { programId } } },
|
||||||
|
|||||||
Reference in New Issue
Block a user