Pad'Time is a full-stack reservation platform for padel clubs. Players book courts and join public matches, admins steer the club from a rich KPI dashboard, and Pad'AI — our Gemini-powered assistant — suggests the best time slots and surfaces booking trends.
It is built as a modular .NET 10 monolith with Clean Architecture + CQRS behind a standalone Angular 21 SPA, with centralized authentication handled by Duende IdentityServer 7.
- 📅 Book a court — sites, courts, multi-step calendar with conflict prevention
- 👥 Public & private matches — create or join open matches, share with friends
- 🤖 Pad'AI suggestions — smart slot recommendations based on availability and patterns
- 💳 Frictionless payment — pay your share after booking, with a payment success overlay
- 🏆 Match history — see upcoming, past and cancelled bookings at a glance
- 🌍 i18n — FR / EN / NL / DE
- 📊 KPI dashboard — revenue, occupancy, member activity in real time
- 📈 AI trends panel — booking trends visualized via Gemini
- 👤 Member management — categories, debts, activity history
- 🔔 Operational alerts — incomplete matches, automatic debt creation
- 💶 Analytics & revenue — turnover by site, court, period
- 🔐 OAuth2 / OIDC — Authorization Code + PKCE via Duende IdentityServer
- 🏥 Health endpoints —
/healthand/readyfor orchestration - 🚀 CI/CD — GitHub Actions pipeline (build, test, deploy)
- 📦 Containerized —
docker-compose upand the whole platform is online
pad-time/
├── src/
│ ├── IdentityServer/ 🔐 Duende IdentityServer 7 + ASP.NET Identity
│ ├── BackendApi/ ⚙️ .NET 10 modular monolith
│ │ ├── PadTime.API/ ↳ ASP.NET Core minimal-API host
│ │ ├── PadTime.Application/ ↳ CQRS (MediatR), validators, behaviors
│ │ ├── PadTime.Domain/ ↳ Entities, value objects, domain rules
│ │ └── PadTime.Infrastructure/ ↳ EF Core, Gemini client, persistence
│ └── AngularApp/ 🎨 Angular 21 + PrimeNG + Tailwind
├── infra/ ☁️ Bicep + deployment scripts (Azure)
├── docs/ 📚 Architecture, API reference, user manual
├── .github/workflows/ 🤖 CI / CD pipelines
└── docker-compose.yml 🐳 Local orchestration
┌────────────────┐ commands/queries ┌────────────────┐
│ Angular 21 │ ───────────────────▶ │ PadTime.API │
│ (SPA, OIDC) │ ◀─── DTOs (JSON) ── │ (Minimal API) │
└────────────────┘ └───────┬────────┘
│ MediatR
▼
┌──────────────────────────────┐
│ PadTime.Application │
│ Handlers · Validators · DTOs│
└────────┬────────────┬────────┘
│ │
┌──────────▼──┐ ┌─────▼─────────────┐
│ Domain │ │ Infrastructure │
│ Entities │ │ EF Core + Gemini │
└─────────────┘ └───────┬───────────┘
│
┌─────▼─────┐
│ PostgreSQL│
└───────────┘
| Layer | Technology |
|---|---|
| Backend | .NET 10 · ASP.NET Core · MediatR 12 · FluentValidation · EF Core 10 |
| Auth | Duende IdentityServer 7.4 · ASP.NET Core Identity · OIDC + PKCE |
| Frontend | Angular 21 (standalone) · PrimeNG 21 · Tailwind · angular-auth-oidc-client |
| AI | Google Gemini API (slot suggestions, trend analysis) |
| Database | PostgreSQL 18 (two schemas: identity + business) |
| Testing | xUnit · FluentAssertions · NSubstitute (BE) · Cucumber + Playwright (FE) |
| Infra | Docker Compose · Azure Bicep · GitHub Actions |
| i18n | @jsverse/transloco (FR, EN, NL, DE) |
- Docker Desktop
- .NET 10 SDK
- Node.js ≥ 20 (only for local Angular dev)
Create a .env file at the project root with the following content:
# Identity Database (identity-db)
POSTGRES_USER=identity_user
POSTGRES_PASSWORD=Passw0rd!
POSTGRES_DB=identity_db
# API Database (api-db)
API_POSTGRES_USER=padtime_user
API_POSTGRES_PASSWORD=Passw0rd!
API_POSTGRES_DB=padtime
# Identity Server → identity-db connection
DB_HOST=identity-db
DB_PORT=5432
DB_NAME=identity_db
DB_USER=identity_user
DB_PASSWORD=Passw0rd!
# Backend API → api-db connection
API_DB_HOST=api-db
API_DB_PORT=5432
API_DB_NAME=padtime
API_DB_USER=padtime_user
API_DB_PASSWORD=Passw0rd!
# Gemini AI (optional — app works without it)
GEMINI_API_KEY=your_gemini_api_key_hereThe Identity Server and Backend API require HTTPS certificates to start. Generate them with the .NET CLI:
# Create the certs directory
mkdir .docker/certs
# Generate a dev HTTPS certificate as .pfx
dotnet dev-certs https --export-path .docker/certs/identity-server.pfx --password "Passw0rd!" --trust
# Copy it for the API container (same cert is fine for local dev)
copy .docker/certs/identity-server.pfx .docker/certs/backend-api.pfxNote: On Linux/macOS, replace
copywithcpandmkdirwithmkdir -p .docker/certs.
docker-compose up --build| Service | URL | Description |
|---|---|---|
| Angular SPA | http://localhost:4200 | Web client |
| Backend API | http://localhost:5002 | REST API + Swagger |
| Identity Server | http://localhost:5001 | OIDC authority |
| identity-db | localhost:5433 |
PostgreSQL (users) |
| api-db | localhost:5434 |
PostgreSQL (business) |
All demo accounts use the password Passw0rd!
| Role | Description | |
|---|---|---|
| admin@test.be | Admin global | Full access — KPI dashboard, member management, analytics, AI trends panel |
| Matricule | Scenario | |
|---|---|---|
| alice@test.be | G1001 | Main demo player — organizes matches, books courts |
| bob@test.be | G1002 | Has 15 € debt (1 incomplete match) — can still book but debt is visible |
| claire@test.be | G1003 | Regular player |
| david@test.be | G1004 | Regular player |
| helene@test.be | G1005 | Regular player |
| kevin@test.be | G1006 | Regular player |
| nathalie@test.be | G1007 | Regular player |
| Matricule | Site | Scenario | |
|---|---|---|---|
| emma@test.be | S10001 | Brussels Padel Center | Site-affiliated player |
| francois@test.be | S10002 | Liege Sport Complex | Site-affiliated player |
| ibrahim@test.be | S10003 | Brussels Padel Center | Site-affiliated player |
| lea@test.be | S10004 | Liege Sport Complex | Site-affiliated player |
| Matricule | Scenario | |
|---|---|---|
| georges@test.be | L10001 | Blocked — 45 € debt (3 incomplete matches) — cannot create new matches |
| julie@test.be | L10002 | Free member |
| marc@test.be | L10003 | Free member |
- Admin dashboard — Log in as
admin@test.beto see KPIs, revenue, occupancy, AI trends - Book a court — Log in as
alice@test.be, pick a site, court, date and time slot - Public match — Create an open match as Alice, then join it as another player (e.g.
claire@test.be) - Debt enforcement — Log in as
georges@test.beand try to create a match — blocked by 45 € debt - Pad'AI suggestions — Any player can ask for smart slot recommendations (requires
GEMINI_API_KEY)
cd src/BackendApi
dotnet restore
dotnet run --project src/PadTime.APIcd src/AngularApp
npm install
npm start # http://localhost:4200cd src/IdentityServer
dotnet watch run# Backend — unit tests (Domain + Application + behaviors)
cd src/BackendApi
dotnet test
# Frontend — unit tests
cd src/AngularApp
npm test
# Frontend — E2E (Cucumber + Playwright)
npm run e2eThe backend suite covers the CQRS handlers, validators, domain entities, and pipeline behaviors (logging + validation). See src/BackendApi/tests/PadTime.Tests/ for the full layout.
# Apply migrations locally
cd src/BackendApi/src/PadTime.API
dotnet ef database update
# Seed demo data (members, sites, courts, sample bookings)
dotnet run -- --seedThe DemoSeeder generates a complete club state — members across categories, sites in Brussels, courts, future and past bookings — perfect to demo the admin dashboard.
Pad'AI relies on the Google Gemini API. Provide a key via environment variable:
# .env
GEMINI_API_KEY=your_gemini_api_key_here
GEMINI_MODEL=gemini-2.0-flashIf the key is missing, the app falls back gracefully — booking still works, AI panels show a friendly "AI offline" notice.
The docs/ folder contains the full bachelor project documentation:
- 📘 API Reference
- 📗 User Manual
- 📐 P0 — Project Charter
- 🧭 P1 — Compréhension métier formalisée
- 🏛️ P3 — Architecture cible
- 🔒 P4 — Security model
- 📊 P5 — Stratégie Data & Analytics
- ✅ P7 — Qualité, CI/CD et critères de livraison
docker-compose up -d --build # Rebuild + detached
docker-compose down -v # Stop + drop volumes
docker-compose logs -f api # Tail API logs
docker exec -it pad-time-api bash # Shell into the API container| Symptom | Fix |
|---|---|
connection refused on db |
Wait for the health check — docker ps should show (healthy) |
| Port already in use | Edit the host-side port in docker-compose.yml |
Angular shows 401 on every call |
Re-login — your OIDC session has expired or cookies were cleared |
| AI panel shows "AI offline" | Set GEMINI_API_KEY in .env and restart the API |
See CHANGELOG.md for the full release history.
Nicolas Denoel — Bachelor project, 2026.
Released under the MIT License.