Samouczek: Tworzenie aplikacji do rezerwacji muzeów z Next.js i Timerise API

Ten samouczek poprowadzi Cię przez proces tworzenia aplikacji do rezerwacji muzeów przy użyciu Next.js, TypeScript, Timerise API, Apollo Client i Tailwind CSS. Skupimy się na integracji bezgłowego API Timerise, demonstrując korzyści płynące z takiego rozwiązania.

Wymagania wstępne

  • Podstawowa znajomość języków TypeScript i React
  • Zainstalowany Node.js
  • Dostęp do interfejsu API Timerise

Krok 1: Konfiguracja projektu Next.js

Pierwszym krokiem w tworzeniu naszej aplikacji do rezerwacji muzeów jest utworzenie nowego projektu Next.js. Next.js wprowadza ulepszony proces konfiguracji, umożliwiając skonfigurowanie podstawowych funkcji, takich jak Tailwind CSS, ESLint i innych, od samego początku. To usprawnione podejście upraszcza początkową konfigurację projektu, pozwalając nam skupić się na tworzeniu podstawowych funkcji aplikacji.

Tworzenie nowej aplikacji Next.js
Uruchom polecenie, aby zainicjować tworzenie nowej aplikacji Next.js. Zostaniesz poproszony o wybranie różnych opcji konfiguracji, w tym integracji Tailwind CSS, którego będziemy używać do stylizacji naszej aplikacji.

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

Po wyświetleniu monitu wybierz preferowane konfiguracje:

  • ESLint: Wybierz„Tak” dla kontroli jakości kodu.
  • Tailwind CSS: Wybierz„Tak„, aby automatycznie skonfigurować Tailwind CSS.
  • Użyj katalogu src/: Wybierz„Tak„, aby uzyskać czystszą strukturę projektu.
  • App Router: Wybierz„Tak„, aby korzystać z nowej funkcji App Router.
  • Dostosuj domyślny alias importu: Wybierz„Tak„, jeśli chcesz dostosować ścieżki importu.

Krok 2: Instalacja zależności

Dzięki ulepszonej konfiguracji Next.js kluczowe zależności, takie jak Tailwind CSS, są już zintegrowane. Wciąż jednak musimy zainstalować dodatkowe pakiety, które są kluczowe dla naszej aplikacji, w szczególności Apollo Client do obsługi zapytań GraphQL i mutacji z API Timerise.

Instalacja klienta Apollo
Apollo Client to kompleksowa biblioteka zarządzania stanem dla JavaScript, która umożliwia zarządzanie zarówno lokalnymi, jak i zdalnymi danymi za pomocą GraphQL. Będzie to nasze główne narzędzie do interakcji z API Timerise.

Bash
npm install @apollo/client graphql

Weryfikacja konfiguracji CSS Tailwind (opcjonalnie)
Ponieważ Tailwind CSS został skonfigurowany podczas inicjalizacji projektu, zweryfikuj pliki konfiguracyjne (tailwind.config.js i postcss.config.js), aby upewnić się, że spełniają one potrzeby stylizacji projektu. W razie potrzeby można wprowadzić zmiany, aby dostosować konfigurację Tailwind.

Dodatkowe zależności
W zależności od wymagań projektu mogą być potrzebne inne zależności. Na przykład, jeśli planujesz obsługiwać daty i godziny (powszechne w aplikacji do rezerwacji), rozważ zainstalowanie date-fns:

Bash
npm install date-fns

Krok 3: Konfiguracja klienta Apollo

Teraz, gdy nasz projekt jest skonfigurowany z niezbędnymi zależnościami, nadszedł czas, aby skonfigurować klienta Apollo. Apollo Client to potężne i elastyczne narzędzie do zarządzania zarówno lokalnymi, jak i zdalnymi danymi w aplikacjach JavaScript. Upraszcza to proces interakcji z interfejsami API GraphQL, takimi jak używany przez nas interfejs API Timerise. Konfigurując klienta Apollo, umożliwiamy naszej aplikacji wydajne wykonywanie zapytań i mutacji, zarządzanie stanami ładowania, buforowanie danych i obsługę błędów, które są istotnymi aspektami interakcji z naszym systemem rezerwacji.

Konfiguracja klienta Apollo w lib/apolloClient.ts

Musimy utworzyć instancję klienta Apollo, która wskazuje na nasz punkt końcowy GraphQL API. Ta instancja będzie używana w całej aplikacji do interakcji z interfejsem API 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;
  • The uri jest ustawiona na punkt końcowy interfejsu API Timerise.
  • InMemoryCache służy do buforowania wyników zapytań po ich pobraniu. Pomaga to zoptymalizować wydajność naszej aplikacji poprzez zmniejszenie liczby potrzebnych wywołań API.

Ta konfiguracja jest kluczowym krokiem w umożliwieniu naszej aplikacji skutecznej komunikacji z API Timerise. Ustawia to scenę dla kolejnych kroków, w których będziemy tworzyć i wykonywać zapytania GraphQL i mutacje do obsługi rezerwacji.

Zawijanie aplikacji za pomocą ApolloProvider
W pliku app/page.tsx zaimportuj ApolloProvider z @apollo/client i instancji klienta Apollo. Następnie należy opakować główny komponent aplikacji i przekazać do niego instancję klienta.

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>
  );
};

Krok 4: Tworzenie zapytań i mutacji GraphQL

Zapytania GraphQL pozwalają nam pobierać dane, a mutacje pozwalają nam je modyfikować. Tutaj definiujemy zapytania GraphQL i mutacje potrzebne do interakcji z interfejsem API Timerise w celu pobierania usług i rezerwowania slotów.

Definiowanie operacji GraphQL w pliku 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
    }
  }
`;

Zdefiniuj mutację w pliku 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
    }
  }
`;

Krok 5: Tworzenie komponentów aplikacji

Mając zdefiniowane interakcje API, skupiamy się teraz na budowaniu komponentów React. Komponenty te zapewnią interfejs do wybierania slotów i potwierdzania rezerwacji, wykorzystując zapytania i mutacje, które skonfigurowaliśmy.

Komponent wyboru gniazda (SlotSelection.tsx)
Kod i wyjaśnienie tworzenia komponentu do wybierania dostępnych slotów.

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;

Komponent potwierdzenia (ConfirmationView.tsx)
Kod i objaśnienia dotyczące tworzenia komponentu wyświetlającego szczegóły potwierdzenia rezerwacji, w tym kod 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;

Krok 6: Rozwój front-endu za pomocą Tailwind CSS

Ten krok dotyczy stylizacji naszej aplikacji. Tailwind CSS, framework CSS typu utility-first, zostanie wykorzystany do zaprojektowania przejrzystego i responsywnego interfejsu użytkownika. Takie podejście pozwala nam szybko i skutecznie tworzyć atrakcyjny wizualnie front-end, zwiększając komfort użytkowania.

W SlotSelection.tsx zastosujemy klasy Tailwind dla układu, odstępów i estetyki.

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>
);

W ConfirmationView.tsx, wpisz szczegóły potwierdzenia rezerwacji.

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>
);

Krok 7: Wdrożenie za pomocą Vercel

Wdrożenie naszej aplikacji sprawia, że jest ona dostępna dla użytkowników w sieci. Vercel, dobrze współgrający z Next.js, oferuje płynne wdrażanie. Automatyzuje proces od wypchnięcia kodu do produkcji, obsługując funkcje bezserwerowe, statyczne serwowanie plików i nie tylko, zapewniając optymalną wydajność i skalowalność.

Przesyłanie kodu do serwisu GitHub
Przed wdrożeniem upewnij się, że projekt został wypchnięty do repozytorium 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

Konfiguracja wdrożenia w Vercel

  1. Zaloguj się do Vercel i połącz swoje konto GitHub.
  2. Wybierz właśnie wypchnięte repozytorium.
  3. Vercel automatycznie wykrywa, że jest to aplikacja Next.js i sugeruje ustawienia kompilacji. Zaakceptuj je lub zmodyfikuj w razie potrzeby.
  4. Kliknij„Deploy„, aby rozpocząć proces wdrażania. Vercel zajmuje się kompilacją i wdrażaniem, zapewniając aktywny adres URL.

Vercel zapewnia funkcje takie jak automatyczny HTTPS, obsługa niestandardowych domen i analiza w czasie rzeczywistym. Możesz także ustawić zmienne środowiskowe i zarządzać innymi ustawieniami bezpośrednio z pulpitu nawigacyjnego Vercel.

Dostęp do pełnego kodu aplikacji

Aby uzupełnić ten samouczek i zwiększyć doświadczenie w nauce, udostępniliśmy pełny kod źródłowy aplikacji do rezerwacji muzeów w serwisie GitHub. Jest to świetna okazja do zbadania bazy kodu, eksperymentowania z modyfikacjami oraz bardziej szczegółowego zrozumienia struktury i funkcjonalności aplikacji.

Dostęp do repozytorium
Cały kod źródłowy aplikacji do rezerwacji muzeów znajduje się w serwisie GitHub. Dostęp do niego można uzyskać pod następującym adresem URL: https://github.com/timerise-io/museum-booking-app

Klonowanie repozytorium
Aby pracować z kodem na komputerze lokalnym, sklonuj repozytorium za pomocą Git:

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

Eksploruj i modyfikuj
Po uzyskaniu kodu można go swobodnie eksplorować i modyfikować. Niezależnie od tego, czy chcesz zrozumieć określone funkcje, dodać nowe funkcje, czy dostosować interfejs użytkownika, ta baza kodu może służyć jako praktyczny punkt wyjścia.

Odwiedź wersję demonstracyjną
Dostęp do wersji demonstracyjnej aplikacji Museum Booking można uzyskać pod następującym adresem URL: https://museum-booking-app.vercel.app/

Wnioski

Ten samouczek zawiera przewodnik krok po kroku dotyczący konfigurowania aplikacji Next.js, integracji z bezgłowym interfejsem API, takim jak Timerise, i jej wdrażania. Fragmenty kodu są kluczowymi częściami aplikacji, pokazującymi, jak zintegrować interfejs API Timerise z aplikacją Next.js przy użyciu klienta Apollo dla GraphQL.