Skip to content

About

Ves qué sala está libre y la tomas. Ejemplo abierto de una app en sube.sh

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Reserva de salas

Ves qué sala está libre y la tomas.

Dos pantallas: la grilla del día, donde un click toma una sala, y la lista de salas.

Corre en sube, que le pone la base de datos, el dominio, el TLS y el inicio de sesión. La app no trae código de autenticación.

Por qué existe este ejemplo

Por una carrera que casi todas las apps de reserva pierden.

Lo natural es guardar startsAt y endsAt, y antes de insertar preguntar si la sala está libre. Eso está mal, y no por descuido: entre la consulta y el insert hay una ventana. Dos personas reservando la misma sala en el mismo segundo consultan las dos, ven libre las dos, insertan las dos. La app acaba de duplicar una reunión comportándose exactamente como está escrita. Ninguna cantidad de cuidado en ese if cierra la ventana, porque la ventana está entre las dos sentencias.

Acá el tiempo se corta en bloques de media hora y el conflicto se convierte en un valor. Y un valor se puede volver imposible:

uniqueIndex('bookings_slot').on(table.roomId, table.onDate, table.slot)

Una reunión de 9:00 a 10:30 son tres filas con el mismo groupId. Postgres rechaza la segunda reserva bajo cualquier concurrencia, y la app se entera porque le avisan, no porque preguntó:

try {
  await db.insert(bookings).values(wanted)   // una sola sentencia: todo o nada
} catch (error) {
  if (isConflict(error)) redirect(`/?dia=${date}&choque=1`)
  throw error
}

Dos detalles que importan:

  • Un solo insert para toda la reunión. Si un bloque está tomado falla entera y no queda media reserva ocupando una sala para una reunión que nunca ocurrió.
  • Solo se atrapa el 23505. Ese es el código de Postgres para "un índice único dijo que no". Cualquier otro error se relanza: tragarse todo convertiría una base de datos caída en un amable "la sala está ocupada".

El precio es real y está en el esquema: las reservas caen en medias horas. Para una sala de reuniones eso es como la gente reserva igual. Si necesitas precisión arbitraria, la respuesta es un tstzrange con una exclusion constraint — la misma idea, la base de datos rechaza, con más maquinaria.

Quién puede soltar una reserva

Solo quien la tomó. La regla es el where, no un if:

.where(and(eq(bookings.groupId, groupId), eq(bookings.bookedBy, who.id)))

El dueño sale de la sesión y de la fila, nunca del formulario, así que mandar a mano el groupId de otra persona no calza con ninguna fila.

Correrlo

npm install
npm run build

Construye sin base de datos. Para verlo con datos hay que desplegarlo:

npm i -g @sube.sh/cli   # o usa npx
sube new "Reserva de salas"
sube deploy

Estructura

app/page.tsx         la grilla del día
app/salas/page.tsx   las salas
src/lib/slots.ts     el tiempo como números de bloque
src/db/schema.ts     dos tablas: rooms y bookings

middleware.ts y next.config.ts los pone la plataforma. AGENTS.md explica las reglas que un asistente de IA debe respetar al modificar esta app.

Licencia

MIT.

About

Ves qué sala está libre y la tomas. Ejemplo abierto de una app en sube.sh

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages