commit 2593e81498cc2c3aa6900c10dbcabec01641613d Author: Krzysztof Cieślik Date: Sun Jun 21 06:08:47 2026 +0200 Inicjalna wersja systemu zarządzania rezerwacjami (RMS) SPA zbudowane w React 19 + Vite 8 z pełnym zestawem funkcjonalności: autentykacja z 2FA, kreator rezerwacji, panel admina, analityka, GraphQL (Apollo Client + SchemaLink), React Query, Storybook, testy jednostkowe (Vitest + RTL) i e2e (Playwright). diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f35b98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Environment +.env +.env.local +.env.*.local + +# Test artifacts +test-results/ +playwright-report/ + +# OS +Thumbs.db diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 0000000..bbdfd71 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,8 @@ +export default { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials'], + framework: { + name: '@storybook/react-vite', + options: {}, + }, +}; diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 0000000..08cf384 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,11 @@ +import '../src/styles/main.scss'; + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7c4220f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# ── Stage 1: build ────────────────────────────────────────────────────────── +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --ignore-scripts --legacy-peer-deps + +COPY . . + +# W produkcji zapytania API idą przez nginx proxy /api +ARG VITE_API_URL=/api +ENV VITE_API_URL=$VITE_API_URL + +RUN npm run build + +# ── Stage 2: serve ────────────────────────────────────────────────────────── +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..5da05d2 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,11 @@ +FROM node:20-alpine + +WORKDIR /app + +RUN npm install -g json-server + +COPY db.json ./db.json + +EXPOSE 3001 + +CMD ["json-server", "--watch", "db.json", "--host", "0.0.0.0", "--port", "3001"] diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..f58c861 --- /dev/null +++ b/Justfile @@ -0,0 +1,100 @@ +# Reservation Management System — Justfile +# Wymaga: just, node 20+, npm, docker, docker compose +# Instalacja just: https://github.com/casey/just#installation + +# ── Zmienne ───────────────────────────────────────────────────────────────── + +API_PORT := "3001" +DEV_PORT := "5173" + +# ── Domyślna komenda (lista wszystkich) ────────────────────────────────────── + +default: + @just --list + +# ── Tryb deweloperski ───────────────────────────────────────────────────────── + +# Zainstaluj zależności npm +install: + npm ci + +# Uruchom json-server (mock API) w tle na porcie 3001 +api: + npx json-server --watch db.json --host 0.0.0.0 --port {{API_PORT}} + +# Uruchom serwer deweloperski Vite +dev: + npm run dev + +# Uruchom API i Vite równolegle (wymaga GNU parallel lub mprocs) +start: install + #!/usr/bin/env bash + trap 'kill 0' EXIT + npx json-server --watch db.json --host 0.0.0.0 --port {{API_PORT}} & + npm run dev + +# ── Testy ──────────────────────────────────────────────────────────────────── + +# Uruchom testy jednostkowe (Vitest) +test: + npm run test + +# Uruchom testy e2e Playwright (wymaga działającego API i dev serwera) +e2e: + node test_rms.mjs + +# Uruchom Storybook +storybook: + npm run storybook + +# ── Budowanie ──────────────────────────────────────────────────────────────── + +# Zbuduj aplikację produkcyjną (VITE_API_URL=/api dla Dockera) +build: + VITE_API_URL=/api npm run build + +# Zbuduj i sprawdź lokalnie przez preview Vite (wymaga osobnego API) +preview: build + npm run preview + +# ── Docker ─────────────────────────────────────────────────────────────────── + +# Zbuduj obrazy Docker (frontend + api) +docker-build: + docker compose build + +# Uruchom aplikację w Dockerze (http://localhost) +docker-up: + docker compose up -d + @echo "" + @echo "Aplikacja dostępna na: http://localhost" + @echo "API (json-server): http://localhost:{{API_PORT}}" + @echo "" + @echo "Zatrzymaj: just docker-down" + +# Uruchom w trybie interaktywnym z logami +docker-up-logs: + docker compose up + +# Zatrzymaj kontenery +docker-down: + docker compose down + +# Zatrzymaj i usuń wolumeny (resetuje db.json do stanu z obrazu) +docker-clean: + docker compose down -v + +# Przebuduj i uruchom od zera +docker-rebuild: + docker compose down + docker compose build --no-cache + docker compose up -d + @echo "Aplikacja dostępna na: http://localhost" + +# Pokaż logi kontenerów +docker-logs: + docker compose logs -f + +# Status kontenerów +docker-ps: + docker compose ps diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b990ba --- /dev/null +++ b/README.md @@ -0,0 +1,247 @@ +# 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 + +```bash +# 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.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 ) +│ ├── 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
i
+``` + +--- + +## 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`: + +```graphql +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 ``, ``, `