Tutorial: Creación de una aplicación de reservas para museos con Next.js y la API de Timerise

Este tutorial te guiará en la creación de una aplicación de reserva de museos utilizando Next.js, TypeScript, Timerise API, Apollo Client y Tailwind CSS. Nos centraremos en la integración de la API headless de Timerise, demostrando las ventajas de una solución de este tipo.

Requisitos previos

  • Conocimientos básicos de TypeScript y React
  • Node.js instalado
  • Acceso a la API de Timerise

Paso 1: Configuración del proyecto Next.js

El primer paso para crear nuestra aplicación de reserva de museos es crear un nuevo proyecto Next.js. Next.js introduce un proceso de configuración mejorado, que permite configurar funciones esenciales como Tailwind CSS, ESLint, etc., desde el principio. Este enfoque racionalizado simplifica la configuración inicial del proyecto, lo que nos permite centrarnos en la creación de las funciones básicas de la aplicación.

Crear una nueva aplicación Next.js
Ejecute el comando para iniciar la creación de una nueva aplicación Next.js. Se te pedirá que elijas varias opciones de configuración, incluida la integración de Tailwind CSS, que utilizaremos para dar estilo a nuestra aplicación.

Bash
npx create-next-app@latest museum-booking-app --typescript
cd museum-booking-app

Cuando se le solicite, elija las configuraciones que prefiera:

  • ESLint: Elija‘Sí’ para las comprobaciones de calidad del código.
  • Tailwind CSS: Selecciona«Sí» para configurar Tailwind CSS automáticamente.
  • Utilice el directorio src/: Elija‘Sí’ para una estructura de proyecto más limpia.
  • App Router: Seleccione«Sí» para utilizar la nueva función App Router.
  • Personalizar alias de importación por defecto: Seleccione«Sí» si desea personalizar las rutas de importación.

Paso 2: Instalación de dependencias

Con la configuración mejorada de Next.js, las dependencias clave como Tailwind CSS ya están integradas. Sin embargo, aún necesitamos instalar paquetes adicionales que son cruciales para nuestra aplicación, particularmente Apollo Client para manejar consultas GraphQL y mutaciones con la API de Timerise.

Instalar el cliente Apollo
Apollo Client es una completa biblioteca de gestión de estados para JavaScript que permite gestionar datos locales y remotos con GraphQL. Esta será nuestra herramienta principal para interactuar con la API de Timerise.

Bash
npm install @apollo/client graphql

Verificar la configuración CSS de Tailwind (opcional)
Dado que el CSS de Tailwind se ha configurado durante la inicialización del proyecto, comprueba los archivos de configuración (tailwind.config.js y postcss.config.js) para asegurarte de que satisfacen las necesidades de estilo de tu proyecto. Puedes realizar los ajustes necesarios para personalizar la configuración de Tailwind.

Dependencias adicionales
Dependiendo de los requisitos de su proyecto, es posible que necesite otras dependencias. Por ejemplo, si planea manejar fechas y horas (común en una aplicación de reservas), considere instalar date-fns:

Bash
npm install date-fns

Paso 3: Configuración del cliente Apollo

Ahora que nuestro proyecto está configurado con las dependencias necesarias, es hora de configurar Apollo Client. Apollo Client es una herramienta potente y flexible para gestionar datos locales y remotos en aplicaciones JavaScript. Simplifica el proceso de interacción con las API GraphQL, como la API Timerise que estamos utilizando. Al configurar Apollo Client, permitimos que nuestra aplicación ejecute eficientemente consultas y mutaciones, gestione estados de carga, almacene datos en caché y maneje errores, que son aspectos esenciales de la interacción con nuestro sistema de reservas.

Configurar el cliente Apollo en lib/apolloClient.ts

Necesitamos crear una instancia de Apollo Client que apunte a nuestro endpoint de la API GraphQL. Esta instancia se utilizará en toda la aplicación para interactuar con la API de Timerise.

TypeScript
import { ApolloClient, InMemoryCache } from '@apollo/client';

const apolloClient = new ApolloClient({
  uri: 'https://sandbox-api.timerise.io/v1',
  cache: new InMemoryCache(),
});

export default apolloClient;
  • En uri se establece en el punto final de la API de Timerise.
  • Caché en memoria se utiliza para almacenar en caché los resultados de la consulta después de obtenerlos. Esto ayuda a optimizar el rendimiento de nuestra aplicación reduciendo el número de llamadas a la API necesarias.

Esta configuración es un paso crucial para permitir que nuestra aplicación se comunique eficazmente con la API de Timerise. Prepara el escenario para los siguientes pasos en los que crearemos y ejecutaremos consultas y mutaciones GraphQL para gestionar las reservas.

Envuelva su aplicación con ApolloProvider
En tu archivo app/page.tsx, importa ApolloProvider de @apollo/client y tu instancia de Apollo Client. A continuación, envuelve el componente raíz de tu aplicación con y pásale la instancia del cliente.

TypeScript
'use client'
import { ApolloProvider } from '@apollo/client';
import apolloClient from '../lib/apolloClient';
import SlotSelection from "@/components/SlotSelection";

export default function Home() {
  return (
    <ApolloProvider client={apolloClient}>
      <SlotSelection serviceId="YourServiceId" />
    </ApolloProvider>
  );
};

Paso 4: Creación de consultas y mutaciones GraphQL

Las consultas GraphQL nos permiten obtener datos, y las mutaciones nos permiten modificarlos. Aquí definimos las consultas y mutaciones GraphQL necesarias para interactuar con la API de Timerise para obtener servicios y reservar franjas horarias.

Definir operaciones GraphQL en graphql/queries.ts

TypeScript
import { gql } from '@apollo/client';

export const SERVICE_QUERY = gql`
  query Service($serviceId: ID!, $slotType: SlotType!) {
    service(serviceId: $serviceId) {
      serviceId
      title
      description
      media {
        title
        url
      }
      slots(slotType: $slotType) {
        slotId
        dateTimeFrom
        dateTimeTo
        duration
        quantity
      }
    }
  }
`;

export const BOOKING_QUERY = gql`
  query Booking($bookingId: ID!) {
    booking(bookingId: $bookingId) {
      bookingId
      shortId
      shortUrl
      qrUrl
      status
    }
  }
`;

Definir mutación en graphql/mutations.ts

TypeScript
import { gql } from '@apollo/client';

export const BOOKING_CREATE_MUTATION = gql`
  mutation BookingCreate($serviceId: ID!, $slots: [ID]) {
    bookingCreate(serviceId: $serviceId, slots: $slots) {
      bookingId
      qrUrl
      shortUrl
      status
    }
  }
`;

Paso 5: Creación de los componentes de la aplicación

Con nuestras interacciones API definidas, ahora nos centramos en construir los componentes React. Estos componentes proporcionarán la interfaz para seleccionar franjas horarias y confirmar reservas, utilizando las consultas y mutaciones que hemos configurado.

Componente de selección de ranuras (SlotSelection.tsx)
Código y explicación para crear un componente que seleccione las franjas horarias disponibles.

TypeScript
import React, { useState } from 'react';
import { useRouter } from 'next/navigation'
import { useQuery, useMutation } from '@apollo/client';
import { SERVICE_QUERY } from '../graphql/queries';
import { BOOKING_CREATE_MUTATION } from '../graphql/mutations';

const SlotSelection = ({ serviceId }: { serviceId: string }) => {
  const router = useRouter();
  const [selectedSlot, setSelectedSlot] = useState<string|null>(null);
  const { loading, error, data } = useQuery(SERVICE_QUERY, {
    variables: { serviceId: serviceId, slotType: "AVAILABLE" },
  });
  const [createBooking, { data: bookingData, loading: bookingLoading, error: bookingError }] = useMutation(BOOKING_CREATE_MUTATION);

  if (loading || bookingLoading) return <p>Loading...</p>;
  if (error || bookingError) return <p>Error loading.</p>;

  const handleSlotSelect = async (slotId: string) => {
    setSelectedSlot(slotId);
    try {
      const { data } = await createBooking({
        variables: { serviceId, slots: [slotId] }
      });
      if (data.bookingCreate && data.bookingCreate.bookingId) {
        router.push(`/confirmation/${data.bookingCreate.bookingId}`);
      }
    } catch (error) {
      console.error("Error creating booking:", error);
      // Handle booking error
    }
  };

  return (
    <div className="p-4">
      <h2 className="text-lg font-semibold">Select a Slot</h2>
      <ul>
        {data.service.slots.map((slot: { slotId: string, dateTimeFrom: Date }) => (
          <li key={slot.slotId} className="my-2">
            <button
              className={`p-2 border ${selectedSlot === slot.slotId ? 'border-blue-500' : 'border-gray-300'}`}
              onClick={() => handleSlotSelect(slot.slotId)}>
              {new Date(slot.dateTimeFrom).toLocaleString()}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default SlotSelection;

Componente de confirmación (ConfirmationView.tsx)
Código y explicación para crear un componente que muestre los detalles de la confirmación de la reserva, incluido el código QR.

TypeScript
import React from 'react';
import { useQuery } from '@apollo/client';
import { BOOKING_QUERY } from '@/graphql/queries';

const ConfirmationView = ({ bookingId }: { bookingId: string }) => {
  const { loading, error, data } = useQuery(BOOKING_QUERY, {
    variables: { bookingId: bookingId },
  });

  if (loading) return <p>Loading booking...</p>;
  if (error) return <p>Error loading booking.</p>;
  return (
    <div className="p-4">
      <h2 className="text-lg font-semibold">Booking status: {data.booking.status}</h2>
      <p className="my-2">Booking ID: {data.booking.shortId}</p>
      <div className="my-2">
        <img src={data.booking.qrUrl} alt="QR Code" className="w-32 h-32" />
      </div>
      <a href={data.booking.shortUrl} target="_blank" className="text-blue-500">View booking page</a>
    </div>
  );
};

export default ConfirmationView;

Paso 6: Desarrollo Front-End con Tailwind CSS

Este paso consiste en dar estilo a nuestra aplicación. Se utilizará Tailwind CSS, un framework CSS que da prioridad a la utilidad, para diseñar una interfaz de usuario limpia y con capacidad de respuesta. Este enfoque nos permite construir un front-end visualmente atractivo de forma rápida y eficaz, mejorando la experiencia del usuario.

En SlotSelection.tsx, aplicaremos las clases Tailwind para el diseño, el espaciado y la estética.

TypeScript
return (
  <div className="p-4 max-w-md mx-auto">
    <h2 className="text-2xl font-bold text-center text-white-800 mb-4">Select a Slot</h2>
    <ul className="list-none space-y-3">
        {data.service.slots.map((slot: { slotId: string, dateTimeFrom: Date }) => (
          <li key={slot.slotId} className="flex justify-between items-center p-3 border rounded-lg shadow-sm">
            <span className="text-white-600">{new Date(slot.dateTimeFrom).toLocaleString()}</span>
            <button
              className={`px-4 py-2 rounded-lg text-black ${selectedSlot === slot.slotId ? 'bg-blue-500' : 'bg-gray-300'}`}
              onClick={() => handleSlotSelect(slot.slotId)}>
              Select
            </button>
          </li>
        ))}
    </ul>
  </div>
);

En ConfirmationView.tsx, estilice los detalles de confirmación de la reserva.

TypeScript
return (
  <div className="p-4 max-w-md mx-auto">
    <h2 className="text-2xl font-bold text-center text-white-800 mb-4">BOOKING {data.booking.status}</h2>
    <div className="bg-white shadow overflow-hidden sm:rounded-lg p-4">
      <p className="text-center text-gray-600 mb-2">ID: <span className="text-gray-800 font-semibold">{data.booking.shortId}</span></p>
      <div className="text-center">
        <img src={data.booking.qrUrl} alt="QR Code" className="w-32 h-32 inline-block mb-3" />
        <p className="text-sm text-gray-600">Scan this QR code at the entrance</p>
      </div>
      <a href={data.booking.shortUrl} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:text-blue-700 text-center block mt-4">
        View booking page
      </a>
    </div>
  </div>
);

Paso 8: Despliegue con Vercel

El despliegue de nuestra aplicación la hace accesible a los usuarios en la web. Vercel, que armoniza bien con Next.js, ofrece una experiencia de despliegue sin fisuras. Automatiza el proceso desde la inserción del código hasta la producción, gestionando funciones sin servidor, el servicio de archivos estáticos y mucho más, garantizando un rendimiento y una escalabilidad óptimos.

Envíe su código a GitHub
Antes de desplegar, asegúrate de que tu proyecto está en un repositorio de GitHub:

Bash
git init
git add .
git commit -m "Init commit"
git branch -M main
git remote add origin https://github.com/your-username/your-repo-name.git
git push -u origin main

Configuración de la implantación en Vercel

  1. Inicia sesión en Vercel y conecta tu cuenta de GitHub.
  2. Elija el repositorio que acaba de insertar.
  3. Vercel detecta automáticamente que se trata de una aplicación Next.js y sugiere ajustes de compilación. Acéptelas o modifíquelas según sea necesario.
  4. Haga clic en«Desplegar» para iniciar el proceso de despliegue. Vercel se encarga de la creación y el despliegue, y le proporciona una URL activa.

Vercel ofrece funciones como HTTPS automático, compatibilidad con dominios personalizados y análisis en tiempo real. También puede establecer variables de entorno y gestionar otros ajustes directamente desde el panel de control de Vercel.

Acceso al código completo de la aplicación

Para complementar este tutorial y mejorar su experiencia de aprendizaje, hemos puesto a su disposición en GitHub el código fuente completo de la aplicación de reserva de museos. Esta es una gran oportunidad para explorar el código base, experimentar con modificaciones y comprender la estructura y funcionalidad de la aplicación con mayor detalle.

Acceder al repositorio
Todo el código fuente de la aplicación de reservas del museo está alojado en GitHub. Puede acceder a él en la siguiente URL https://github.com/timerise-io/museum-booking-app

Clonar el repositorio
Para trabajar con el código en tu máquina local, clona el repositorio utilizando Git:

Bash
git clone https://github.com/timerise-io/museum-booking-app.git
cd museum-booking-app

Explorar y modificar
Una vez que tengas el código, siéntete libre de explorarlo y modificarlo. Tanto si desea comprender funcionalidades específicas como añadir nuevas características o personalizar la interfaz de usuario, esta base de código puede servirle como práctico punto de partida.

Visite la demostración
Puede acceder a la demostración en directo de la Museum Booking App en la siguiente URL: https://museum-booking-app.vercel.app/

Conclusión

Este tutorial proporciona una guía paso a paso para configurar una aplicación Next.js, integrarla con una API headless como Timerise y desplegarla. Los fragmentos de código son partes cruciales de la aplicación, y demuestran cómo integrar la API de Timerise con una aplicación Next.js utilizando Apollo Client para GraphQL.