@@ -160,123 +160,142 @@ export function useReservar(
160160 [ reservationsSvc ]
161161 ) ;
162162
163- const reservar = React . useCallback (
164- async ( { vehicle, turn, dateISO } : ReserveArgs ) : Promise < ReserveResult > => {
165- // 0) Validación por tipo de turno respecto al usuario
163+ const reservar = React . useCallback (
164+ async ( { vehicle, turn, dateISO } : ReserveArgs ) : Promise < ReserveResult > => {
165+ if ( turn === 'Dia' ) {
166+ if ( await hasActiveReservationAnyTurnSameDay ( userMail , dateISO ) ) {
167+ return {
168+ ok : false ,
169+ message : `No puedes reservar día completo: ya tienes una reserva activa para el ${ dateISO } .` ,
170+ } ;
171+ }
172+ } else {
173+ if ( await hasActiveReservationSameDay ( userMail , dateISO , turn ) ) {
174+ return {
175+ ok : false ,
176+ message : `No puedes reservar: ya tienes una reserva activa para el ${ dateISO } en el turno de la ${ turn } .` ,
177+ } ;
178+ }
179+ }
180+
181+ const slotsFilter = [
182+ `(fields/Activa eq 'Activa')` ,
183+ `fields/TipoCelda eq '${ vehicle } '` ,
184+ `fields/Itinerancia eq 'Empleado Itinerante'` ,
185+ ] . join ( ' and ' ) ;
186+
187+ const slots = await slotsSvc . getAll ( { filter : slotsFilter , top : 2000 } ) ;
188+ if ( ! Array . isArray ( slots ) || slots . length === 0 ) {
189+ return { ok : false , message : `No existen celdas activas para ${ vehicle } .` } ;
190+ }
191+
192+ //IDs de celdas que van siempre al final
193+ const LAST_GROUP_IDS = new Set < number > ( [ 5 ] ) ;
194+
195+ const getSlotId = ( slot : unknown ) : number | null => {
196+ const rslot = slot as Record < string , unknown > ;
197+ const raw =
198+ ( rslot [ 'ID' ] as number | string | undefined ) ??
199+ ( rslot [ 'Id' ] as number | string | undefined ) ??
200+ ( rslot [ 'id' ] as number | string | undefined ) ;
201+ return raw == null ? null : Number ( raw ) ;
202+ } ;
203+
204+ // 🔹 Particionamos: primero normales, luego “último grupo”
205+ const primarySlots : unknown [ ] = [ ] ;
206+ const lastGroupSlots : unknown [ ] = [ ] ;
207+
208+ for ( const s of slots ) {
209+ const id = getSlotId ( s ) ;
210+ if ( id == null ) continue ;
211+
212+ if ( LAST_GROUP_IDS . has ( id ) ) lastGroupSlots . push ( s ) ;
213+ else primarySlots . push ( s ) ;
214+ }
215+
216+ const orderedSlots = [ ...primarySlots , ...lastGroupSlots ] ;
217+
218+ console . log ( '[useReservar] Slots ordenados: ' , orderedSlots ) ;
219+
220+ // 2) Turnos a validar capacidad
221+ const turnsToCheck : Exclude < TurnType , 'Dia' > [ ] =
222+ turn === 'Dia' ? [ 'Manana' , 'Tarde' ] : [ turn as Exclude < TurnType , 'Dia' > ] ;
223+
224+ // 👉 Ahora iteramos sobre orderedSlots en vez de slots
225+ for ( const slot of orderedSlots ) {
226+ const rslot = slot as Record < string , unknown > ;
227+ const slotId =
228+ ( rslot [ 'ID' ] as number | string | undefined ) ??
229+ ( rslot [ 'Id' ] as number | string | undefined ) ??
230+ ( rslot [ 'id' ] as number | string | undefined ) ;
231+ const code =
232+ ( rslot [ 'Title' ] as string | undefined ) ??
233+ ( rslot [ 'Code' ] as string | undefined ) ??
234+ ( rslot [ 'Name' ] as string | undefined ) ??
235+ slotId ;
236+
237+ if ( slotId == null ) continue ;
238+
239+ let available = true ;
240+
166241 if ( turn === 'Dia' ) {
167- // Día completo: bloquear si ya existe cualquier reserva del usuario ese día
168- if ( await hasActiveReservationAnyTurnSameDay ( userMail , dateISO ) ) {
169- return {
170- ok : false ,
171- message : `No puedes reservar día completo: ya tienes una reserva activa para el ${ dateISO } .` ,
172- } ;
173- }
242+ const any = await countReservations ( slotId , dateISO , [ 'Manana' , 'Tarde' , 'Día completo' ] ) ;
243+ if ( any > 0 ) available = false ;
174244 } else {
175- // Mañana/Tarde: bloquear si el usuario ya tiene reserva en ese mismo turno o un 'Día completo'
176- if ( await hasActiveReservationSameDay ( userMail , dateISO , turn ) ) {
177- return {
178- ok : false ,
179- message : `No puedes reservar: ya tienes una reserva activa para el ${ dateISO } en el turno de la ${ turn } .` ,
180- } ;
245+ for ( const t of turnsToCheck ) {
246+ const count = await countReservations ( slotId , dateISO , [ t , 'Día completo' ] ) ;
247+ if ( count >= 1 ) {
248+ available = false ;
249+ break ;
250+ }
181251 }
182252 }
183253
184- // 1) Traer celdas activas del tipo solicitado (itinerantes)
185- const slotsFilter = [
186- `(fields/Activa eq 'Activa')` ,
187- `fields/TipoCelda eq '${ vehicle } '` ,
188- `fields/Itinerancia eq 'Empleado Itinerante'` ,
189- ] . join ( ' and ' ) ;
254+ if ( ! available ) continue ;
190255
191- const slots = await slotsSvc . getAll ( { filter : slotsFilter , top : 2000 } ) ;
192- if ( ! Array . isArray ( slots ) || slots . length === 0 ) {
193- return { ok : false , message : `No existen celdas activas para ${ vehicle } .` } ;
194- }
195- console . log ( '[useReservar] Slots: ' , slots ) ;
196-
197- // 2) Turnos a validar capacidad (para 'Dia' valida mañana y tarde)
198- const turnsToCheck : Exclude < TurnType , 'Dia' > [ ] =
199- turn === 'Dia' ? [ 'Manana' , 'Tarde' ] : [ turn as Exclude < TurnType , 'Dia' > ] ;
200-
201- for ( const slot of slots ) {
202- const rslot = slot as Record < string , unknown > ;
203- const slotId =
204- ( rslot [ 'ID' ] as number | string | undefined ) ??
205- ( rslot [ 'Id' ] as number | string | undefined ) ??
206- ( rslot [ 'id' ] as number | string | undefined ) ;
207- const code =
208- ( rslot [ 'Title' ] as string | undefined ) ??
209- ( rslot [ 'Code' ] as string | undefined ) ??
210- ( rslot [ 'Name' ] as string | undefined ) ??
211- slotId ;
212-
213- if ( slotId == null ) continue ;
214-
215- // 3) Validar disponibilidad (capacidad por turno = 1, sin distinción por tipo de vehículo)
216- let available = true ;
217-
218- if ( turn === 'Dia' ) {
219- // Debe estar libre en ambos turnos y sin 'Día completo' previo
220- const any = await countReservations ( slotId , dateISO , [ 'Manana' , 'Tarde' , 'Día completo' ] ) ;
221- if ( any > 0 ) available = false ;
222- } else {
223- // Mañana/Tarde: considera también 'Día completo' como bloqueo del turno
224- for ( const t of turnsToCheck ) {
225- const count = await countReservations ( slotId , dateISO , [ t , 'Día completo' ] ) ;
226- if ( count >= 1 ) { available = false ; break ; }
227- }
228- }
256+ const turnValue : TurnDb = turn === 'Dia' ? 'Día completo' : ( turn as TurnDb ) ;
229257
230- if ( ! available ) continue ;
231-
232- // 4) Crear **una** sola reserva en la primera celda disponible
233- const turnValue : TurnDb = ( turn === 'Dia' ? 'Día completo' : ( turn as TurnDb ) ) ;
234-
235- try {
236- const payload = {
237- Title : userMail ,
238- Date : dateISO ,
239- Turn : turnValue ,
240- SpotIdLookupId : Number ( slotId ) ,
241- VehicleType : vehicle as VehicleType ,
242- Status : 'Activa' ,
243- NombreUsuario : userName ,
244- } satisfies ReservationCreate ;
245-
246- const created = await reservationsSvc . create ( payload ) ;
247-
248- // 5) Refrescar listas/estado externo
249- await opts ?. onAfterReserve ?.( ) ;
250-
251- const successMsg =
252- turn === 'Dia'
253- ? `Reserva de día completo creada en celda ${ code } para ${ dateISO } .`
254- : `Reserva creada en celda ${ code } para ${ dateISO } (${ turn } ).` ;
255-
256- return { ok : true , message : successMsg , reservation : created } ;
257- } catch ( e ) {
258- console . warn ( '[useReservar] Falló crear en celda' , code , e ) ;
259- // Si falla con esta celda, intenta con la siguiente
260- continue ;
261- }
258+ try {
259+ const payload = {
260+ Title : userMail ,
261+ Date : dateISO ,
262+ Turn : turnValue ,
263+ SpotIdLookupId : Number ( slotId ) ,
264+ VehicleType : vehicle as VehicleType ,
265+ Status : 'Activa' ,
266+ NombreUsuario : userName ,
267+ } satisfies ReservationCreate ;
268+
269+ const created = await reservationsSvc . create ( payload ) ;
270+ await opts ?. onAfterReserve ?.( ) ;
271+
272+ const successMsg =
273+ turn === 'Dia'
274+ ? `Reserva de día completo creada en celda ${ code } para ${ dateISO } .`
275+ : `Reserva creada en celda ${ code } para ${ dateISO } (${ turn } ).` ;
276+
277+ return { ok : true , message : successMsg , reservation : created } ;
278+ } catch ( e ) {
279+ console . warn ( '[useReservar] Falló crear en celda' , code , e ) ;
280+ continue ;
262281 }
263-
264- // 6) Si ninguna celda tuvo cupo
265- const turnoTexto = turn === 'Dia' ? 'día completo' : String ( turn ) . toLowerCase ( ) ;
266- const msg = `No hay parqueaderos disponibles para ${ vehicle } el ${ dateISO } en ${ turnoTexto } .` ;
267- return { ok : false , message : msg } ;
268- } ,
269- [
270- reservationsSvc ,
271- slotsSvc ,
272- hasActiveReservationAnyTurnSameDay ,
273- hasActiveReservationSameDay ,
274- countReservations ,
275- userMail ,
276- userName ,
277- opts ,
278- ]
279- ) ;
282+ }
283+
284+ const turnoTexto = turn === 'Dia' ? 'día completo' : String ( turn ) . toLowerCase ( ) ;
285+ const msg = `No hay parqueaderos disponibles para ${ vehicle } el ${ dateISO } en ${ turnoTexto } .` ;
286+ return { ok : false , message : msg } ;
287+ } ,
288+ [
289+ reservationsSvc ,
290+ slotsSvc ,
291+ hasActiveReservationAnyTurnSameDay ,
292+ hasActiveReservationSameDay ,
293+ countReservations ,
294+ userMail ,
295+ userName ,
296+ opts ,
297+ ]
298+ ) ;
280299
281300 return {
282301 minDate,
0 commit comments