Seat block issue resolution

This commit is contained in:
Stephanos A
2026-07-15 21:11:23 +03:00
parent 8bda06b226
commit 2d2fcfbda9
2 changed files with 35 additions and 17 deletions

View File

@@ -224,16 +224,27 @@ export class SeatsService {
}
}
const availability = await this.segmentsService.getSeatAvailabilityMap(
scheduleId, seatIds, stopTimes, reqFrom, reqTo, journeyDirection || JourneyDirection.ONE_WAY,
);
const [availability, persistedSeats] = await Promise.all([
this.segmentsService.getSeatAvailabilityMap(
scheduleId, seatIds, stopTimes, reqFrom, reqTo, journeyDirection || JourneyDirection.ONE_WAY,
),
this.prisma.seat.findMany({
where: { id: { in: seatIds } },
select: { id: true, status: true },
}),
]);
const persistedStatus = new Map(persistedSeats.map(s => [s.id, s.status]));
// Every requested seat defaults to AVAILABLE — this also guards against a stale
// persisted Seat.status column (e.g. a leftover 'BOOKED'/'BLOCKED' value) bleeding
// through getSeatMap's own fallback, since that fallback only triggers when this
// map has no entry at all for a given seat.
for (const seatId of seatIds) {
statusMap.set(seatId, availability.get(seatId) ?? 'AVAILABLE');
const persisted = persistedStatus.get(seatId);
// BLOCKED and UNDER_MAINTENANCE are cross-schedule flags set by admins —
// always honour them regardless of hold/booking state.
if ((persisted as string) === 'BLOCKED' || (persisted as string) === 'UNDER_MAINTENANCE') {
statusMap.set(seatId, persisted!);
} else {
statusMap.set(seatId, availability.get(seatId) ?? 'AVAILABLE');
}
}
return statusMap;

View File

@@ -35,6 +35,7 @@ export default function SeatsPage() {
queryKey: ['seatmap', selectedSchedule],
queryFn: () => selectedSchedule ? seatsApi.getSeatMap(selectedSchedule) : Promise.resolve(null),
enabled: !!selectedSchedule,
staleTime: 0,
});
const { data: coachTypesData } = useQuery({
@@ -49,6 +50,7 @@ export default function SeatsPage() {
const { data: routeCoachesData, isLoading: routeCoachesLoading } = useQuery({
queryKey: ['routeCoaches', selectedRoute],
staleTime: 0,
queryFn: async () => {
if (!selectedRoute) return null;
const template: any[] = await routeCoachTemplatesApi.get(selectedRoute);
@@ -79,10 +81,15 @@ export default function SeatsPage() {
enabled: !!selectedRoute,
});
const invalidateSeatData = () => {
queryClient.refetchQueries({ queryKey: ['seatmap', selectedSchedule] });
queryClient.refetchQueries({ queryKey: ['routeCoaches', selectedRoute] });
};
const blockMutation = useMutation({
mutationFn: ({ seatId, reason }: any) => seatsApi.block(seatId, { reason }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
setShowBlockModal(false);
setSelectedSeat(null);
setBlockReason('');
@@ -92,14 +99,14 @@ export default function SeatsPage() {
const unblockMutation = useMutation({
mutationFn: (seatId: string) => seatsApi.unblock(seatId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
},
});
const removeSeatMutation = useMutation({
mutationFn: (seatId: string) => seatsApi.removeSeat(seatId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
setShowRemoveModal(false);
setSelectedSeat(null);
},
@@ -108,7 +115,7 @@ export default function SeatsPage() {
const undoRemoveMutation = useMutation({
mutationFn: (seatId: string) => seatsApi.undoRemove(seatId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
},
});
@@ -116,7 +123,7 @@ export default function SeatsPage() {
mutationFn: ({ seatId, reason }: { seatId: string; reason: string }) =>
seatsApi.setMaintenance(seatId, reason),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
setShowMaintenanceModal(false);
setSelectedSeat(null);
setMaintenanceReason('');
@@ -125,7 +132,7 @@ export default function SeatsPage() {
const clearMaintenanceMutation = useMutation({
mutationFn: (seatId: string) => seatsApi.clearMaintenance(seatId),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['seatmap'] }),
onSuccess: () => invalidateSeatData(),
});
const schedules = schedulesData?.items || schedulesData?.data || [];
@@ -139,7 +146,7 @@ export default function SeatsPage() {
return Promise.all(seatIds.map((seatId: string) => seatsApi.block(seatId, { reason })));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
setShowBlockCoachModal(false);
setSelectedCoach(null);
setBlockCoachReason('');
@@ -153,7 +160,7 @@ export default function SeatsPage() {
return Promise.all(seatIds.map((seatId: string) => seatsApi.unblock(seatId)));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['seatmap'] });
invalidateSeatData();
setShowUnblockCoachModal(false);
setCoachToUnblock(null);
},
@@ -1015,7 +1022,7 @@ function SeatIcon({
const color = getSeatColor(status);
const canBlock = status === 'AVAILABLE';
const canUnblock = status === 'BLOCKED';
const canMaintenance = status === 'AVAILABLE' || status === 'BLOCKED';
const canMaintenance = false;
const canClearMaintenance = status === 'UNDER_MAINTENANCE';
return (