尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Projekt zespołowy z przedmiotu Projektowanie i Architektura Systemów. Klient wybiera spektakl i miejsce, rezerwuje je na 15 minut, a potem kupuje bilet i dostaje kod. Etap I działa bez płatności online.
| Osoba | Zakres |
|---|---|
| Szymon | kontrakt wewnętrzny, setup, baza, rezerwacja i zakup (backend) |
| Olek | dane przykładowe, repertuar, plan widowni, ekran biletu |
| Paweł | lista spektakli (backend), walidacja, formularz z licznikiem, integracja, testy |
Zadania: Trello → workspace BiletowyProjekt, tablica Taski.
| Co | Czym |
|---|---|
| Front + API | Next.js (App Router) + TypeScript |
| Baza danych | MongoDB Atlas (darmowy klaster) + oficjalny sterownik mongodb |
| Wygląd | Tailwind CSS |
| Walidacja | Zod |
| Testy | Vitest |
| Jakość kodu | ESLint + Prettier |
| Paczki | pnpm |
| Hosting | Vercel |
Wszystko jest w jednym projekcie i jednym repo. Front i backend (app/api) działają razem w Next.js.
teatr-kasa/
├── app/
│ ├── page.tsx # repertuar
│ ├── spektakl/[id]/page.tsx # plan widowni
│ ├── rezerwacja/[id]/page.tsx # licznik + zakup
│ ├── bilet/[code]/page.tsx # bilet
│ └── api/
│ ├── events/route.ts # GET /api/events
│ ├── events/[id]/route.ts # GET /api/events/:id
│ ├── reservations/route.ts # POST /api/reservations
│ └── reservations/[id]/purchase/route.ts # POST /api/reservations/:id/purchase
├── components/ # komponenty UI
├── lib/
│ ├── db.ts # połączenie z MongoDB
│ ├── schemas.ts # schematy Zod + typy
│ ├── errors.ts # kody i format błędów
│ ├── reservation.ts # zasady ważności rezerwacji
│ └── stubs.ts # zaślepki danych (do usunięcia po podpięciu bazy)
├── scripts/
│ └── setup-db.ts # indeksy + dane przykładowe
├── tests/ # testy API (Vitest)
├── .github/workflows/ci.yml # CI: lint, formatowanie, testy, build
├── .env.example
└── pnpm-lock.yaml
corepack enable
pnpm -v
⚠️ Używamy tylko pnpm. Nie uruchamiamy
npm installaniyarn. Commitujemypnpm-lock.yaml, nie commitujemypackage-lock.json.
git clone <url-repo>
cd teatr-kasa
pnpm install
cp .env.example .env # wklej MONGODB_URI od Szymona
pnpm dev # http://localhost:3000
Baza jest wspólna, w chmurze (Atlas) — nie trzeba niczego instalować lokalnie.
| Komenda | Co robi |
|---|---|
pnpm dev | uruchamia aplikację |
pnpm build | buduje wersję produkcyjną |
pnpm lint | sprawdza kod |
pnpm format | formatuje kod (Prettier) |
pnpm test | testy API |
pnpm db:setup | tworzy indeksy i wgrywa dane przykładowe |
pnpm add <paczka> | dodaje zależność |
⚠️
pnpm db:setupusuwa wszystkie dokumenty z kolekcjieventsireservationsw bazie wskazanej przezMONGODB_DB, a następnie wgrywa dane przykładowe. Na wspólnej bazieteatrmoże go uruchamiać tylko osoba odpowiedzialna za setup (Szymon). Do własnych prób używajMONGODB_DB=teatr-test.
| Zmienna | Opis |
|---|---|
MONGODB_URI | adres klastra Atlas (z hasłem) |
MONGODB_DB | wspólna baza aplikacji: teatr |
MONGODB_DB_TEST | baza dla pnpm test: teatr-test (domyślnie) |
RESERVATION_TTL_MINUTES | czas ważności rezerwacji w minutach (15) |
RESERVATION_RATE_LIMIT | rezerwacje z 1 IP na 10 s, potem 429 (30) |
.env nie commitujemy. Adres bazy przekazujemy sobie prywatnie..env.example.pnpm test zawsze działa na bazie z MONGODB_DB_TEST, żeby nie czyścić danych reszcie zespołu. Jeśli wskazuje tę samą bazę co MONGODB_DB, testy w ogóle nie ruszą.expiresAt w bazie, a nie czekając na upływ czasu.pnpm db:setup na wspólnej bazie uruchamia tylko Szymon.| Kolekcja | Pola |
|---|---|
events | _id, title, date, time, stage |
reservations | _id, eventId, seatId, customerName, email, status (held / purchased), expiresAt, code |
Indeksy (tworzone przez pnpm db:setup):
reservations: unikalny { eventId: 1, seatId: 1 } — jedno miejsce, jedna rezerwacjareservations: unikalny { code: 1 } (tylko dla kupionych)Wspólny dla wszystkich zespołów — nie zmieniamy nazw endpointów ani pól.
| Endpoint | Wysyłamy | Dostajemy |
|---|---|---|
GET /api/events | — | lista spektakli |
GET /api/events/:id | — | { event, takenSeats } |
POST /api/reservations | { eventId, seatId, customerName, email } | { reservationId, expiresAt } |
POST /api/reservations/:id/purchase | — | { code } albo odmowa |
takenSeats = miejsca kupione + miejsca z ważną rezerwacjąseatId = rząd + numer, np. "F7" (rzędy A–H, miejsca 1–10)Błędy: { "error": "opis", "code": "SEAT_TAKEN" }
| Kod | Kiedy |
|---|---|
| 400 | błędne dane (walidacja Zod) |
| 404 | brak spektaklu / rezerwacji |
| 409 | miejsce zajęte |
| 410 | rezerwacja wygasła |
Pilnuje ich baza, a nie same if-y w kodzie:
| Zasada | Jak |
|---|---|
| Podwójna rezerwacja | unikalny indeks + transakcja (usuń wygasłą → wstaw nową) |
| Zakup tylko ważnej rezerwacji | findOneAndUpdate({ _id, status: "held", expiresAt: { $gt: now } }, …) |
| Podwójny zakup | ponowienie zwraca ten sam kod |
| Błędne dane | schematy Zod w każdym POST |
Dlaczego unikalny indeks wystarczy na podwójną rezerwację: nie sprawdzamy w kodzie, czy miejsce jest wolne. Od razu próbujemy zapisać rezerwację, a MongoDB przy indeksie unikalnym { eventId, seatId } przepuści tylko jeden dokument dla danego miejsca, nawet gdy 20 requestów przyjdzie w tej samej milisekundzie. Pozostałe dostają błąd duplikatu E11000, który zamieniamy na 409 SEAT_TAKEN. Transakcja jest potrzebna tylko po to, żeby usunąć wygasłą rezerwację tego miejsca i wstawić nową jako jedną niepodzielną operację.
main — zawsze działająca wersja, zmiany tylko przez Pull Request. Nie commitujemy na nim bezpośrednio, GitHub i tak odrzuci push.mainNazwa gałęzi: prefiks/krotki-opis
| Prefiks | Kiedy | Przykład |
|---|---|---|
feat/ | nowa funkcja lub ekran | feat/plan-widowni |
fix/ | poprawka błędu | fix/licznik-po-odswiezeniu |
test/ | same testy | test/kontrakt-api |
docs/ | tylko dokumentacja (README.md, CLAUDE.md) | docs/przeplyw-ekranow |
chore/ | konfiguracja, paczki, CI, porządki — bez funkcji | chore/setup |
-, bez polskich znaków i spacji (plan-widowni, nie Plan_Widowni ani plan-widowni-ąę)fix/..., nie wracamy do starej| Szymon | Olek | Paweł |
|---|---|---|
feat/kontrakt-wewnetrzny | feat/seed | feat/api-events |
chore/setup | feat/repertuar | feat/walidacja |
feat/wygasanie | feat/plan-widowni | feat/formularz-rezerwacji |
feat/api-rezerwacja | feat/ekran-biletu | feat/integracja |
feat/podwojna-rezerwacja | test/kontrakt-api | |
feat/api-zakup |
feat: dodaj endpoint rezerwacji
fix: blokuj przycisk zakupu w trakcie wysyłania
maingit switch main && git pull
git switch -c feat/plan-widowni
# ...praca...
git add . && git commit -m "feat: plan widowni"
git pull origin main # zaciągnij zmiany innych
pnpm install # jeśli zmienił się pnpm-lock.yaml
git push -u origin feat/plan-widowni
# otwórz Pull Request na GitHubie
Kiedy otworzyć:
ReviewTytuł i opis:
feat: plan widowni/spektakl/dziady, kliknij F7 → 409”)Wymagania do merge (pilnuje ich ruleset na main):
lint, format, test, build) musi być zielonelib/db.ts, lib/schemas.ts, scripts/ i kontrakcie API — review Szymonapnpm-lock.yaml w tym samym PRReview:
Approve albo Request changesgit commit + git push) — nowe commity same trafiają do PR, nowego PR nie otwieramyKonflikty / „This branch is out-of-date”:
git switch feat/plan-widowni
git pull origin main # rozwiąż konflikty w edytorze, jeśli są
pnpm install # jeśli zmienił się pnpm-lock.yaml
git add . && git commit # tylko gdy były konflikty
git push
Bez konfliktów wystarczy przycisk Update branch w PR.
Merge:
main ląduje jeden commit z tytułem PRDoneKiedy zamknąć PR bez merge (Close pull request): zmiana okazała się niepotrzebna, kierunek się zmienił albo PR zastępuje inny — wtedy w komentarzu napisz dlaczego i podlinkuj nowy PR.
Gałąź usuwamy zaraz po merge — jest już w main, a stara gałąź tylko myli.
git switch main && git pull
git branch -d feat/plan-widowni # -d odmówi, jeśli coś nie jest zmergowane
git fetch --prune # usuwa lokalne odnośniki do skasowanych gałęzi zdalnych
Po Squash and merge git branch -d może zgłosić, że gałąź „nie jest w pełni zmergowana” (bo na main jest nowy, spłaszczony commit). Jeśli PR na GitHubie ma status Merged, usuń ją przez git branch -D.
Nie usuwamy main i gałęzi z otwartym PR.
main = wersja produkcyjna, każdy PR = podgląd0.0.0.0/0 (Vercel nie ma stałego IP)To Do → Doing → Review (otwarty PR) → Done (zmergowane)
Karta jest Done, gdy kod jest na main, testy przechodzą, a checklista w karcie jest odhaczona.