Reservation Management System
Aplikacja SPA do zarządzania rezerwacjami zbudowana w React 19 + Vite 8. Umożliwia klientom przeglądanie dostępnych terminów, dokonywanie rezerwacji z płatnością depozytu, zarządzanie historią rezerwacji oraz wystawianie opinii. Administratorzy mają dostęp do panelu zarządzania, kalendarza rezerwacji, analityki i eksportu danych.
Uruchomienie
Uwaga: przed uruchomieniem test_rms.mjs oba serwery muszą być aktywne. Test automatycznie przywraca dane seed (r1, r2) do stanu pending przed każdym przebiegiem.
Dane testowe
| Rola |
E-mail |
Hasło |
| Admin |
admin@reservations.dev |
Admin1234! |
| Klient |
anna.kowalski@example.com |
Client1234! |
Logowanie zawsze uruchamia weryfikację 2FA – kod jest wyświetlany bezpośrednio w interfejsie (symulowany e-mail).
Stos technologiczny
| Warstwa |
Technologia |
| UI |
React 19, Vite 8, SCSS Modules |
| Routing |
React Router v7 |
| Zapytania REST |
TanStack React Query v5 + Axios |
| Zapytania GraphQL |
Apollo Client v4 + SchemaLink (in-memory) |
| Formularze |
Formik + Zod (zod-formik-adapter) |
| Ikony |
Lucide React |
| REST mock |
json-server (db.json) |
| Testy jednostkowe |
Vitest + React Testing Library |
| Testy e2e |
Playwright |
| Dokumentacja |
Storybook 8 |
Struktura projektu
web_projekt/
├── db.json # Baza danych json-server (users, services, reservations)
├── test_rms.mjs # Testy e2e Playwright (32 testy)
├── src/
│ ├── main.jsx # Punkt wejścia: Apollo Client, QueryClient, renderowanie
│ ├── App.jsx # Router, lazy loading, ErrorBoundary, Suspense, ProtectedRoute
│ ├── index.css # Reset + CSS custom properties (tryb ciemny/jasny)
│ ├── styles/
│ │ ├── _variables.scss # Tokeny projektu: kolory, spacing, typografia, cienie, media queries
│ │ └── main.scss # Globalne style bazowe
│ ├── api/
│ │ ├── reservations.js # CRUD rezerwacji przez Axios (json-server :3001)
│ │ ├── services.js # Pobieranie listy usług
│ │ └── users.js # Pobieranie i aktualizacja użytkowników
│ ├── context/
│ │ ├── AuthContext.jsx # Stan uwierzytelnienia, 2FA, role, sesja w localStorage
│ │ └── ThemeContext.jsx # Przełącznik jasny/ciemny motyw (data-theme na <html>)
│ ├── hocs/
│ │ └── withRole.jsx # HOC: renderuje komponent tylko dla dozwolonych ról
│ ├── hooks/
│ │ ├── useCreateReservation.js # useMutation: POST /reservations
│ │ ├── useDeleteReservation.js # useMutation: DELETE /reservations/:id
│ │ ├── useReservations.js # useQuery: GET /reservations (wszystkie)
│ │ ├── useServices.js # useQuery: GET /services
│ │ ├── useSlotReservations.js # useQuery: GET /reservations?serviceId&date
│ │ ├── useUpdateReservation.js # useMutation: PATCH /reservations/:id
│ │ └── useUserReservations.js # useQuery: GET /reservations?userId
│ ├── pages/
│ │ ├── LoginPage.jsx # Formularz logowania (Formik+Zod) + ekran 2FA z podglądem kodu
│ │ ├── LoginPage.module.scss
│ │ ├── RegisterPage.jsx # Rejestracja z walidacją Zod i weryfikacją e-mail
│ │ ├── RegisterPage.module.scss
│ │ ├── DashboardPage.jsx # Panel klienta: AvailabilitySearch, SlotFinder, BookingWizard, MyReservations, ProfileEditModal
│ │ ├── DashboardPage.module.scss
│ │ ├── ReservationsPage.jsx # Historia rezerwacji z filtrami, anulowaniem, RescheduleModal, ReviewsSection, Google Calendar, re-book
│ │ ├── ReservationsPage.module.scss
│ │ ├── AdminPage.jsx # Panel admina: tabela rezerwacji, UserManagement, AnalyticsDashboard, AdminCalendar, blokowanie terminów (useTransition)
│ │ └── AdminPage.module.scss
│ ├── components/
│ │ ├── AvailabilitySearch.jsx # Wyszukiwanie wolnych slotów z synchronizacją URL (useSearchParams)
│ │ ├── AvailabilitySearch.module.scss
│ │ ├── BookingWizard.jsx # 4-krokowy kreator rezerwacji (Usługa → Data → Wymagania → Podsumowanie)
│ │ ├── BookingWizard.module.scss
│ │ ├── BookingForm.jsx # Uproszczony formularz rezerwacji (react-hook-form)
│ │ ├── BookingForm.module.scss
│ │ ├── SlotFinder.jsx # Inteligentny finder slotów z punktacją (scoreSlot, findSlots)
│ │ ├── SlotFinder.module.scss
│ │ ├── PaymentModal.jsx # Modal płatności: Karta / Google Pay / Przelew bankowy (Portal)
│ │ ├── PaymentModal.module.scss
│ │ ├── RescheduleModal.jsx # Modal zmiany terminu rezerwacji (Portal)
│ │ ├── RescheduleModal.module.scss
│ │ ├── ProfileEditModal.jsx # Modal edycji profilu i preferencji powiadomień (Portal, Formik+Zod)
│ │ ├── ProfileEditModal.module.scss
│ │ ├── ReviewsSection.jsx # Sekcja opinii (GraphQL: useQuery + useMutation przez Apollo)
│ │ ├── ReviewsSection.module.scss
│ │ ├── ProfileView.jsx # Widok profilu (GraphQL: userProfile query)
│ │ ├── ProfileView.module.scss
│ │ ├── MyReservations.jsx # Sidebar z aktywnymi rezerwacjami użytkownika
│ │ ├── MyReservations.module.scss
│ │ ├── AdminCalendar.jsx # Widok kalendarza dla admina (miesiąc, kliknięcie dnia, panel boczny)
│ │ ├── AdminCalendar.module.scss
│ │ ├── AnalyticsDashboard.jsx # Statystyki rezerwacji z eksportem CSV (DataRenderer, useMemo)
│ │ ├── AnalyticsDashboard.module.scss
│ │ ├── UserManagement.jsx # Zarządzanie użytkownikami przez admina (tabela, dezaktywacja, reset hasła)
│ │ ├── UserManagement.module.scss
│ │ ├── Modal.jsx # Bazowy komponent modala (Portal, useCallback, Escape key, aria)
│ │ ├── Modal.module.scss
│ │ ├── StatusBadge.jsx # Odznaka statusu rezerwacji (warianty kolorów, rozmiary, aria-label)
│ │ ├── StatusBadge.module.scss
│ │ ├── StatusBadge.test.jsx # Testy RTL dla StatusBadge
│ │ ├── ThemeToggle.jsx # Przycisk przełączania motywu (jasny/ciemny)
│ │ ├── ThemeToggle.module.scss
│ │ ├── DataRenderer.jsx # Render-prop: opakowuje useQuery, przekazuje dane przez children()
│ │ ├── ErrorBoundary.jsx # Class component: łapie błędy runtime, wyświetla fallback UI
│ │ ├── ErrorBoundary.module.scss
│ │ ├── LoadingSpinner.jsx # Animowany spinner ładowania
│ │ ├── LoadingSpinner.module.scss
│ │ └── ProtectedRoute.jsx # Guard routingu: sprawdza rolę i przekierowuje
│ ├── stories/
│ │ ├── StatusBadge.stories.jsx # Storybook: wszystkie warianty odznaki statusu
│ │ ├── Modal.stories.jsx # Storybook: modal informacyjny i destrukcyjny
│ │ └── ThemeToggle.stories.jsx # Storybook: przełącznik motywu
│ └── tests/
│ ├── LoginPage.test.jsx # RTL: formularz logowania, błędy walidacji
│ ├── BookingForm.test.jsx # RTL: formularz rezerwacji, react-hook-form
│ ├── ProtectedRoute.test.jsx # RTL: ochrona tras, przekierowania
│ └── StatusBadge.test.jsx # RTL: warianty statusu
└── public/
└── index.html # Zawiera <div id="root"> i <div id="portal-root">
Architektura danych
REST (json-server :3001)
GET/POST /reservations – rezerwacje
GET /services – usługi
GET/PATCH /users – użytkownicy
- Filtrowanie przez query params:
?userId=, ?serviceId=, ?date=
GraphQL (Apollo Client – in-memory, bez zewnętrznego serwera)
Schema i resolvery zdefiniowane w main.jsx, wykonywane lokalnie przez SchemaLink:
Lista wymagań i ich realizacja
Wymagania funkcjonalne (11)
| # |
Kryterium |
Realizacja |
Status |
| F1 |
System uwierzytelniania i autoryzacji |
AuthContext.jsx – logowanie e-mail + 2FA (kod wyświetlany w UI); role client/admin; sesja w localStorage; rejestracja z weryfikacją e-mail (RegisterPage.jsx); ProtectedRoute blokuje dostęp wg roli |
[OK] |
| F2 |
Zarządzanie profilami użytkowników |
ProfileEditModal.jsx – edycja danych (Formik+Zod, PATCH /users), 5 preferencji powiadomień (toggle switches: potwierdzenie, anulowanie, przypomnienie 24h, zmiana terminu, promocje); ProfileView.jsx – dane z GraphQL; historia w ReservationsPage.jsx |
[OK] |
| F3 |
Wyszukiwanie i przeglądanie dostępności |
AvailabilitySearch.jsx – filtracja po dacie i usłudze, siatka slotów, URL sync (useSearchParams); SlotFinder.jsx – ranking slotów w zakresie dat z preferencjami (rano/południe/wieczór), pasek score |
[OK] |
| F4 |
Proces rezerwacji |
BookingWizard.jsx – 4 kroki: wybór usługi, data+czas (blokada zajętych slotów), wymagania specjalne, podsumowanie z ceną; PaymentModal.jsx – 3 metody płatności, potwierdzenie z paragonem |
[OK] |
| F5 |
System płatności online |
PaymentModal.jsx – karta kredytowa (Formik, walidacja), Google Pay (symulacja), przelew bankowy (IBAN/BIC, numer referencyjny); depozyt 50% ceny; transactionId zapisywany w rezerwacji |
[OK] |
| F6 |
Zarządzanie rezerwacjami przez użytkowników |
ReservationsPage.jsx – filtry (wszystkie/nadchodzące/minione/anulowane), anulowanie z potwierdzeniem, RescheduleModal.jsx (zmiana terminu), "Book again" (re-book), Google Calendar link |
[OK] |
| F7 |
Panel zarządzania rezerwacjami (admin) |
AdminPage.jsx – tabela rezerwacji (Confirm/Cancel/Delete), AdminCalendar.jsx (miesiąc, panel dnia), blokowanie i zwalnianie terminów z datepickerem i listą |
[OK] |
| F8 |
Zarządzanie użytkownikami (admin) |
UserManagement.jsx – tabela użytkowników, dezaktywacja konta (toggle), symulacja resetu hasła; dostęp chroniony przez HOC withRole(['admin']) |
[OK] |
| F9 |
Analityka i raportowanie |
AnalyticsDashboard.jsx – statystyki (łączna liczba, potwierdzone, anulowane, przychód z depozytów, średnia ocena); wykresy słupkowe CSS; eksport CSV z nagłówkami |
[OK] |
| F10 |
Integracja z zewnętrznymi systemami |
Google Calendar: link eventedit generowany dynamicznie z datą/godziną/tytułem dla każdej nadchodzącej rezerwacji; pełne REST API dostępne dla zewnętrznych integracji |
[OK] |
| F11 |
System opinii i ocen |
ReviewsSection.jsx – ocena gwiazdkowa (1–5) + komentarz, zapis przez GraphQL mutation submitReview; pobieranie przez serviceReviews query; widoczne po rozwinięciu minionej rezerwacji |
[OK] |
Wymagania wizualne BIU ZAO (6)
| # |
Kryterium |
Realizacja |
Status |
| V1 |
Spójny i nowoczesny interfejs |
Jeden design system: tokeny w _variables.scss (paleta primary/accent/gray, spacing 4px scale, typografia, cienie), spójne BEM, Lucide icons, karty z border-radius, modale z overlay |
[OK] |
| V2 |
Pełna responsywność |
12 breakpointów w SCSS ($bp-xs 480px–$bp-2xl 1536px); grid w DashboardPage stackuje kolumny, tabela admina scrolluje poziomo, wizard adaptuje układ pól |
[OK] |
| V3 |
Animacje i efekty przejścia |
CSS transition na przyciskach/kartach/modalach; @keyframes pulse na wyróżnionej karcie rezerwacji; spinner ładowania CSS; hover lift (box-shadow + transform) |
[OK] |
| V4 |
Wizualny feedback dla interakcji |
StatusBadge kolorystycznie odróżnia statusy; :disabled/:hover/:focus-visible na wszystkich elementach interaktywnych; błędy inline w formularzach; spinner na przyciskach pending |
[OK] |
| V5 |
Estetyczne wykorzystanie SCSS |
SCSS Modules z BEM, @use '../styles/variables' as *, zagnieżdżone reguły &__, &--, CSS custom properties dla motywu (--clr-bg, --clr-text etc.), media queries przez zmienne |
[OK] |
| V6 |
Wykorzystanie wbudowanych komponentów |
Natywne <input type="date">, <input type="time">, <select>, <textarea>, <dialog role="dialog">; <dl>/<dt>/<dd> dla danych rezerwacji; semantyczny HTML5 (<header>, <main>, <aside>) |
[OK] |
Wymagania techniczne BIU ZAO (10)
| # |
Kryterium |
Realizacja |
Status |
| T1 |
Podstawowe hooki React |
useState – stan formularzy, UI, modali; useEffect – inicjalizacja auth z localStorage, scroll do karty przez useRef; useContext – useAuth(), useTheme() w całej aplikacji |
[OK] |
| T2 |
Custom hooki |
7 hooków w src/hooks/: useServices, useUserReservations, useSlotReservations, useReservations, useCreateReservation, useUpdateReservation, useDeleteReservation |
[OK] |
| T3 |
React Router |
React Router v7: BrowserRouter, Routes, Route, Navigate, Outlet, Link, useNavigate, useLocation, useSearchParams; chronione trasy przez ProtectedRoute z allowedRoles |
[OK] |
| T4 |
Zarządzanie stanem (Context API) |
AuthContext – użytkownik, 2FA (pendingUser/twoFACode/verify2FA), role, sesja; ThemeContext – motyw jasny/ciemny z localStorage i data-theme na <html> |
[OK] |
| T5 |
Obsługa zapytań API |
Axios w src/api/ dla REST (json-server); Apollo Client SchemaLink dla GraphQL (in-memory schema z resolverami w main.jsx) |
[OK] |
| T6 |
Techniki optymalizacyjne |
React.lazy + Suspense (DashboardPage, AdminPage); useMemo (statystyki w AnalyticsDashboard, maxScore w SlotFinder); useCallback (auth functions, Escape handler w Modal) |
[OK] |
| T7 |
Wzorce kompozycji komponentów |
HOC withRole(['admin'])(Component) z displayName; Render props DataRenderer – delegacja renderowania przez children jako funkcję; oba używane w komponentach produkcyjnych |
[OK] |
| T8 |
Obsługa formularzy i walidacja |
Formik + Zod (toFormikValidationSchema): LoginPage, RegisterPage, ProfileEditModal; react-hook-form + zodResolver: BookingForm; walidacja krokowa w BookingWizard |
[OK] |
| T9 |
Konsekwentny styl kodu |
SCSS BEM, camelCase hooks/functions, PascalCase komponenty, styles['block__element--modifier'] we wszystkich SCSS Modules, ESLint, jednolite formatowanie |
[OK] |
| T10 |
Nowoczesny JavaScript |
Destrukturyzacja: const { user, logout } = useAuth(), const { password: _omit, ...safeUser } = match; spread: { ...prev, [key]: value }; optional chaining ?.; nullish ??; template literals |
[OK] |
Wymagania dodatkowe BIU ZAO (11)
| # |
Kryterium |
Realizacja |
Status |
| D1 |
React Suspense i Error Boundaries |
ErrorBoundary.jsx (class component z componentDidCatch, fallback UI + przycisk reload); <Suspense fallback={<LoadingSpinner/>}> w App.jsx opakowuje lazy-loaded strony |
[OK] |
| D2 |
Zaawansowana obsługa formularzy |
Formik + toFormikValidationSchema(zodSchema): LoginPage, RegisterPage, ProfileEditModal; react-hook-form + zodResolver: BookingForm; oba podejścia w jednym projekcie |
[OK] |
| D3 |
GraphQL z Apollo Client |
Apollo Client v4 + makeExecutableSchema + SchemaLink; in-memory resolvery dla userProfile, serviceReviews, userReviews, submitReview (store w pamięci); useQuery/useMutation z @apollo/client/react |
[OK] |
| D4 |
React Portals |
4 komponenty mountowane do #portal-root przez createPortal(): Modal.jsx, PaymentModal.jsx, ProfileEditModal.jsx, RescheduleModal.jsx |
[OK] |
| D5 |
React.lazy i dynamic imports |
lazy(() => import('./pages/DashboardPage')) i lazy(() => import('./pages/AdminPage')) w App.jsx; ładowanie on-demand przy pierwszym wejściu na trasę |
[OK] |
| D6 |
Render props i HOC |
DataRenderer.jsx – render prop (children jako funkcja), używany w AnalyticsDashboard; withRole.jsx – HOC z displayName, używany w AdminPage; oba wzorce produkcyjne |
[OK] |
| D7 |
React Query |
TanStack React Query v5: 7 custom hooków, staleTime 5 min, invalidateQueries po mutacjach (lista rezerwacji odświeżana po każdej zmianie), isPending dla UX |
[OK] |
| D8 |
React DevTools |
WithRole.displayName dla czytelności drzewa; QueryClient kompatybilny z React Query Devtools; Apollo InMemoryCache widoczna w Apollo DevTools |
[OK] |
| D9 |
Testy z React Testing Library |
5 plików testowych, 25+ testów (Vitest): LoginPage (formularz, błędy, submit), BookingForm (walidacja RHF), ProtectedRoute (przekierowania, role), StatusBadge (warianty x2) |
[OK] |
| D10 |
Storybook |
3 pliki stories w src/stories/: StatusBadge (wszystkie statusy + rozmiary), Modal (Default + Danger z useState wrapper), ThemeToggle (z etykietą); Storybook 8 + @storybook/react-vite |
[OK] |
| D11 |
React Concurrent Mode |
useTransition w AdminPage.jsx: przełączanie zakładek (Tabela/Kalendarz/Analityka/Użytkownicy) przez startTransition(() => setMainTab(key)) – nie blokuje UI przy ciężkich re-renderach |
[OK] |
Wyniki testów