47e9b1781ed47068d194d4523f7c9f9b349397c8
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
# Terminal 1 – REST API (json-server na porcie 3001)
npm run api
# Terminal 2 – Vite dev server (port 5173)
npm run dev
# Testy jednostkowe (Vitest + RTL)
npm test
# Testy e2e (Playwright)
node test_rms.mjs
# Storybook (port 6006)
npm run storybook
Uwaga: przed uruchomieniem
test_rms.mjsoba serwery muszą być aktywne. Test automatycznie przywraca dane seed (r1, r2) do stanupendingprzed każdym przebiegiem.
Dane testowe
| Rola | 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– rezerwacjeGET /services– usługiGET/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:
type Query {
userProfile(id: ID!): UserProfile
serviceReviews(serviceId: String!): [Review!]!
userReviews(userId: String!): [Review!]!
}
type Mutation {
submitReview(reservationId: String!, serviceId: String!, ...): Review!
}
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
Vitest (jednostkowe): 25 passed, 0 failed
Playwright (e2e): 32 passed, 0 failed, 2 warnings
Description
Languages
JavaScript
64.2%
SCSS
34.3%
Just
1.2%
Dockerfile
0.2%
HTML
0.1%