# Cluedo HP Webapp

A small multiplayer web app that acts as a digital note sheet for a Harry Potter-inspired Cluedo game. Several logged-in players can join the same game and manage their personal clues independently.

## Features

- Login with user and admin roles
- Admin-managed user creation and deactivation
- Multiple games per user
- Join games using a short join code
- Host-controlled game start with automatic player chips
- Pre-game lobby with live player list, host status, and start confirmation
- Player chips are generated from the first name initial and first two surname letters, e.g. `SNE` for Sascha Nesterovic
- Automatic player list with host indication
- Personal note sheet for each player and game
- Categories for suspects, items, and locations
- Entry status tracking: empty, ruled out, present, or maybe
- Additional notes using `i`, `m`, and `s.<chip>`
- Winner selection by the game host
- Winner badge and confetti animation for all players
- Live updates for new players and winner changes
- Personal game statistics
- Password change functionality
- Harry Potter house themes: Default, Gryffindor, Slytherin, Ravenclaw, and Hufflepuff
- Installable as a Progressive Web App

## Technology

### Frontend

- React 18
- Vite
- Nginx as the production web server
- `canvas-confetti` for the winner animation
- `vite-plugin-pwa` for PWA support

### Backend

- Python 3.12
- FastAPI
- SQLAlchemy 2
- SQLite 3
- Passlib and bcrypt for password hashing

### Deployment

- Docker Compose
- Frontend on port `8081`
- Backend on port `8080`
- SQLite database stored in a persistent Docker volume

## Quick start with Docker

Requirements:

- Docker
- Docker Compose

1. Copy `.env.example` to `.env` and set a strong secret:

```env
BACKEND_SECRET_KEY=use-a-long-random-secret-value
```

2. Build and start the containers:

```bash
docker compose up --build
```

3. Open the application:

- Frontend: http://localhost:8081
- Backend/API: http://localhost:8080

On the first startup, the application opens a setup screen where you create the first administrator with an email address, display name, and password. The default suspects, items, and locations are seeded automatically as well.

The SQLite database is stored in the Docker volume `cluedo-data` and survives normal container restarts.

Start the containers in the background:

```bash
docker compose up -d --build
```

View logs:

```bash
docker compose logs -f backend
```

Stop the containers:

```bash
docker compose down
```

## Local development without Docker

### Backend

```bash
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

export DATABASE_URL=sqlite:///./data/cluedo.db
export SECRET_KEY=use-a-long-random-secret-value

uvicorn app.main:app --reload --port 8080
```

On Windows PowerShell, set the environment variables like this:

```powershell
$env:DATABASE_URL = "sqlite:///./data/cluedo.db"
$env:SECRET_KEY = "use-a-long-random-secret-value"
uvicorn app.main:app --reload --port 8080
```

### Frontend

```bash
cd frontend
npm install
npm run dev
```

The frontend is normally available at http://localhost:5173. During development, the API must be reachable under `/api`; the production Docker setup provides this through Nginx.

Create a production build:

```bash
npm run build
```

## Project structure

```text
.
├── backend/
│   ├── app/
│   │   ├── main.py              # FastAPI app, startup, seed data
│   │   ├── models.py            # SQLAlchemy models
│   │   ├── db.py                # SQLite engine and sessions
│   │   ├── security.py          # Password and cookie logic
│   │   └── routes/              # Auth, admin, and game APIs
│   ├── Dockerfile
│   └── requirements.txt
├── frontend/
│   ├── src/
│   │   ├── App.jsx              # Main UI and game flow
│   │   ├── api/client.js         # API client
│   │   ├── components/           # Pages, cards, and modals
│   │   ├── styles/               # Themes and inline styles
│   │   └── utils/                # Small helpers and storage utilities
│   ├── Dockerfile
│   └── nginx.conf
├── docker-compose.yml
└── .env
```

## Data model

- `users`: users, roles, status, display names, and themes
- `games`: game name, join code, host, and winner
- `game_members`: assignment of users to games
- `entries`: seeded suspects, items, and locations
- `sheet_state`: personal status and notes for an entry

Each player has their own set of `sheet_state` records for every game. A player's notes are therefore not visible to other players.

## API overview

### Authentication

- `POST /auth/login`
- `POST /auth/logout`
- `GET /auth/me`
- `PATCH /auth/password`
- `PATCH /auth/theme`
- `GET /auth/me/stats`

### First-run setup

- `GET /setup/status`
- `POST /setup/admin`

The setup endpoint is available only while no administrator exists. After the first administrator has been created, the setup screen is disabled automatically.

### Administration

- `GET /admin/users`
- `POST /admin/users`
- `DELETE /admin/users/{user_id}` – deactivates a user

### Games

- `GET /games`
- `POST /games`
- `POST /games/join`
- `GET /games/{game_id}`
- `GET /games/{game_id}/members`
- `PATCH /games/{game_id}/winner`
- `POST /games/{game_id}/start`
- `GET /games/{game_id}/chips`
- `GET /games/{game_id}/sheet`
- `PATCH /games/{game_id}/sheet/{entry_id}`

## SQLite notes

SQLite is a good fit for this application: the data volume is small, the app is intended for internal use, and write operations are short. The database file is stored inside the container at `/app/data/cluedo.db` and persisted through the `cluedo-data` volume.

For larger deployments with many concurrent write operations, PostgreSQL would still be the more robust choice. For the intended private or small-group use case, SQLite is sufficient.

## Security notes

- The values in `.env` are examples and should be changed before production use.
- When using HTTPS, set `COOKIE_SECURE` to `true`.
- The application is intended for an internal or small user group.
- The automatic database migration is intentionally pragmatic and does not replace a migration system such as Alembic.
