Skip to main content

Overview

Patients can view all their reservations, check upcoming appointments, review past sessions, and cancel bookings when needed. The reservation management system provides a complete history and status tracking.

Viewing Your Reservations

Dashboard Overview

The main dashboard (/dashboard) displays:
  • Current/Next Appointment: The upcoming reservation
  • Reservation History: Past and all scheduled appointments
// From: app/core/dashboard/components/home/DashboardUser.tsx
export const DashboardUser = () => {
  const currentSession = useAuthStore(state => state.session);
  const [reservations, setReservations] = useState<Reservation[]>([]);
  const [currentReservation, setCurrentReservation] = useState<Reservation>();
  const currentGetReservations = useReservationsStore(
    state => state.getReservations
  );

  useEffect(() => {
    if (!currentSession) return;

    const fetchReservations = async () => {
      const client_id = currentSession.user.id;
      const localReservations = await currentGetReservations({ client_id });
      setReservations(localReservations);

      if (!localReservations.length) return;

      const nextReservation: Reservation = getCurrentReservation(
        localReservations
      );
      setCurrentReservation(nextReservation);
    };

    fetchReservations();
  }, [currentSession]);

  return (
    <LayoutDashboardUser
      titleSection={`Hola, bienvenido ${firstName}`}
      subtitleSection="Gestioná tus turnos y revisá tu historial de sesiones"
    >
      {!currentReservation ? (
        <EmptyTurn />
      ) : (
        <CurrentTurn reservation={currentReservation} />
      )}
      <HistoryTurns reservations={reservations} />
    </LayoutDashboardUser>
  );
};

Fetching Reservations

API Endpoint

Reservations are fetched using the user-specific endpoint:
GET /reservations/user/:client_id
Response:
{
  "status": 200,
  "data": [
    {
      "id": "res789",
      "client_id": "abc123",
      "professional_id": "prof456",
      "start_time": "2026-03-15T14:00:00",
      "end_time": "2026-03-15T14:45:00",
      "status": "CONFIRMED",
      "session_modality": "Virtual",
      "created_at": "2026-03-10T10:30:00"
    }
  ],
  "message": "SOLICITASTE TODAS LAS RESERVAS DEL USUARIO CON EL ID: abc123 EXITOSAMENTE"
}

Backend Implementation

// From: server/src/modules/reservations/reservations.controller.ts
@Get('/user/:client_id')
@ApiOperation({
  summary: 'Obtener reservas por usuario',
  description: 'Retorna todas las reservas asociadas a un usuario específico según su ID de cliente.',
})
async findAllByUser(@Param('client_id') client_id: string) {
  try {
    return {
      status: 200,
      data: await this.reservationsService.findAllByUser({ client_id }),
      message: `SOLICITASTE TODAS LAS RESERVAS DEL USUARIO CON EL ID: ${client_id} EXITOSAMENTE`
    }
  } catch (error) {
    throw new HttpException(error, HttpStatus.BAD_REQUEST);
  }
}
// From: server/src/modules/reservations/reservations.service.ts
async findAllByUser({ client_id }: { client_id: string }) {
  const { data, error } = await this.supabaseService
    .getClient()
    .from('reservations')
    .select('*')
    .eq('client_id', client_id);

  if (error) {
    throw new Error(error.message);
  }

  return data;
}

Reservation Store

The client uses Zustand for state management with persistence:
// From: store/ReservationsStore.ts
interface ReservationsStore {
  reservations: Reservation[];
  currentReservation: Reservation | null;
  allReservations: Reservation[];
  getReservations: (params: { client_id: string }) => Promise<Reservation[]>;
  setCurrentReservation: (reservation: Reservation) => setCurrentReservationResponse;
  deleteReservation: (params: { reservationId: string }) => void;
}

export const useReservationsStore = create<ReservationsStore>()(persist(
  (set) => ({
    reservations: [],
    currentReservation: null,
    allReservations: [],

    getReservations: async ({ client_id }) => {
      const localUrl = serverConfig.reservations.fetchReservationsByUser({
        client_id
      });

      try {
        const response = await axiosClient(localUrl);
        const reservations: Reservation[] = response.data.data;

        // Sort by start_time descending (newest first)
        const sortReservations = [...reservations].sort(
          (a, b) =>
            new Date(b.start_time).getTime() -
            new Date(a.start_time).getTime()
        );

        set({ reservations: sortReservations });
        return sortReservations;
      } catch (error) {
        set({ reservations: [] });
        throw error;
      }
    },
  }),
  {
    name: "reservations-store",
    partialize: (state) => ({
      reservations: state.reservations,
      currentReservation: state.currentReservation,
      allReservations: state.allReservations,
    }),
  }
));
Reservations are automatically sorted by start_time in descending order, showing the most recent first.

Reservation Statuses

Each reservation has one of three possible statuses:
export type ReservationStatus = "PENDING" | "CONFIRMED" | "CANCELLED";

PENDING

Awaiting payment confirmation or professional approval

CONFIRMED

Reservation is confirmed and scheduled

CANCELLED

Reservation has been cancelled

Canceling Reservations

Delete Endpoint

Patients can cancel reservations using the delete endpoint:
DELETE /reservations/:id

Client Implementation

// From: store/ReservationsStore.ts
deleteReservation: async ({ reservationId }) => {
  const localUrl = `${serverConfig.reservations.common}/${reservationId}`;

  try {
    await axiosClient.delete(localUrl);
  } catch (error) {
    return error;
  }
}

Backend Implementation

// From: server/src/modules/reservations/reservations.controller.ts
@Delete(':id')
@ApiOperation({
  summary: 'Eliminar una reserva',
  description: 'Elimina permanentemente una reserva del sistema según su ID.',
})
async remove(@Param('id') id: string) {
  const data = await this.reservationsService.remove(id);
  return { data, status: 200 }
}
// From: server/src/modules/reservations/reservations.service.ts
async remove(id: string) {
  const { data, error } = await this.supabaseService
    .getClient()
    .from("reservations")
    .delete()
    .eq("id", id)
    .select();

  if (error) {
    throw error;
  }

  return data;
}
Cancellation requests should be made at least 48 hours in advance according to the platform’s policy. Check with your professional for their specific cancellation policy.

Reservation Details

Each reservation includes comprehensive information:
FieldTypeDescription
idstringUnique reservation identifier
client_idstringPatient’s user ID
professional_idstringProfessional’s user ID
start_timestringAppointment start time (ISO format)
end_timestringAppointment end time (ISO format)
statusReservationStatusCurrent status of the reservation
session_modalitySessionModalityToUser”Virtual” or “Presencial”
created_atstringWhen the reservation was created

Retrieving a Single Reservation

To get details of a specific reservation:
GET /reservations/:id
// Client configuration
findOne: ({ reservationId }: { reservationId: string }) => {
  return `${serverUrl}/reservations/${reservationId}`
}

Authentication

All reservation endpoints require authentication using the AuthGuard. The access token is automatically added to requests via the axios interceptor.
// From: lib/axiosClient.ts
export const axiosClient = axios.create();

axiosClient.interceptors.request.use((config) => {
  const session = useAuthStore.getState().session;
  if (session?.access_token) {
    config.headers.Authorization = `Bearer ${session.access_token}`;
  }
  return config;
});

Best Practices

Automatic Refresh

Reservations are fetched on component mount and stored in persistent state

Error Handling

Always handle errors when fetching or deleting reservations

Sorted Display

Reservations are sorted by date to show upcoming appointments first

Empty State

Provide a clear UI when patients have no reservations

Next Steps

Book Appointment

Learn how to book a new appointment

Payment Process

Understand how payments work for reservations

Build docs developers (and LLMs) love