174 lines
7.7 KiB
Markdown
174 lines
7.7 KiB
Markdown
# Bowler
|
|
|
|
A multi-user website for managing shared bowler charts — tracking project status and KPIs.
|
|
|
|
This is the Phase 1 foundation: Docker environment, Hello World landing page, login, and user administration. Bowler chart functionality comes next.
|
|
|
|
## Requirements
|
|
|
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) (or Docker Engine + Compose v2)
|
|
|
|
Nothing else — no local PHP, Composer, or Node needed. The app container installs its own dependencies on first boot.
|
|
|
|
## Quick start
|
|
|
|
1. Copy the environment template and set your own passwords:
|
|
|
|
```
|
|
copy .env.example .env
|
|
```
|
|
|
|
Edit `.env` and set:
|
|
- `DB_PASSWORD` / `DB_ROOT_PASSWORD` — Percona database credentials
|
|
- `ADMIN_EMAIL` / `ADMIN_PASSWORD` — the initial site administrator login
|
|
- `APP_KEY` — a random base64 key. Generate one with:
|
|
```
|
|
docker run --rm php:8.4-cli php -r "echo 'base64:' . base64_encode(random_bytes(32)) . PHP_EOL;"
|
|
```
|
|
|
|
*(A ready-to-use development `.env` is already included in this repo.)*
|
|
|
|
2. Start the stack:
|
|
|
|
```
|
|
docker-compose up -d --build
|
|
```
|
|
|
|
First boot takes a few minutes: the app container runs `composer install`, waits for the
|
|
database, runs migrations, and seeds the initial administrator. Watch progress with
|
|
`docker-compose logs -f app`.
|
|
|
|
3. Open [http://localhost:8080](http://localhost:8080) and log in with `ADMIN_EMAIL` / `ADMIN_PASSWORD`.
|
|
|
|
## What's included
|
|
|
|
| URL | Description |
|
|
|---|---|
|
|
| `/` | Hello World landing page |
|
|
| `/login` | Login (rejects disabled accounts, throttled to 10 attempts/min) |
|
|
| `/charts` | Bowler charts of your groups (group owners and admins manage them) |
|
|
| `/my-bowler` | Your personal bowler chart — all of your own KPIs and projects |
|
|
| `/kpis` | My KPIs: threshold-based metrics with auto-computed Green/Yellow/Red status |
|
|
| `/projects` | My projects: manually-statused G/Y/R with notes, headwinds, tailwinds, highlights, blockers |
|
|
| `/teams`, `/categories` | Personal lists used to group chart rows (Team → Category) |
|
|
| `/api-keys` | Personal API keys (multiple per user) for the automation API |
|
|
| `/api/docs` | Swagger UI for the REST API |
|
|
| `/admin/users` | User management (admins only): create users, reset passwords, enable/disable accounts |
|
|
| `/admin/groups` | Group management (admins only): create groups, assign members with member/owner roles |
|
|
|
|
Admins cannot disable their own account, so you can't lock yourself out.
|
|
|
|
### User types
|
|
|
|
Every user is either **standard** (default) or **admin** (`users.type`). Admins manage users
|
|
and groups, see every bowler chart, and can edit any user's content; standard users see only
|
|
their own content plus what's shared with their groups. New abilities can be gated on
|
|
`$user->isAdmin()` (or new `UserType` enum cases) as the app grows.
|
|
|
|
### KPIs and entries
|
|
|
|
A KPI is defined once (measurement type: number / percentage / duration, plus an evaluation
|
|
mode) and then receives dated entries; each entry's status is computed from the KPI's thresholds:
|
|
|
|
- **Higher is better** — green when value ≥ green threshold, yellow when ≥ yellow threshold, else red
|
|
- **Lower is better** — the reverse
|
|
- **Target band** — green within ± green tolerance of target, yellow within ± yellow tolerance, else red
|
|
- **Milestone** — met (green) or not met (red)
|
|
|
|
Projects work the same way but with manually chosen statuses and narrative fields.
|
|
|
|
### Bowler charts
|
|
|
|
Charts belong to groups; group **owners** (and site admins) can create any number of named
|
|
charts per group. Members' KPIs and projects **appear automatically** on all of their groups'
|
|
charts — including charts created later and groups joined later — unless the item's owner
|
|
deselects specific charts on the KPI/project form (stored as per-chart exclusions).
|
|
|
|
Every KPI and project belongs to one of its owner's **Teams** (e.g. "Website Development")
|
|
and **Categories** (e.g. "Performance") — required on create/edit; the chart groups rows
|
|
Team → Category, merging same-named teams from different owners case-insensitively.
|
|
A team or category **cannot be deleted while KPIs/projects still use it** — reassign the
|
|
items first. (Items grandfathered from before teams existed appear under "No Team" /
|
|
"Uncategorized".)
|
|
|
|
The grid shows the trailing 12 months; each cell is the **most recent entry in that month**
|
|
(color + value for KPIs, color for projects, tooltip with date and notes). Rows expand in place:
|
|
**click a KPI row** for an inline SVG trend chart with its threshold zones shaded (line + status-colored
|
|
points for numeric KPIs, a met/missed timeline for milestones); **click a project's month cell** for that
|
|
month's notes/headwinds/tailwinds/highlights/blockers. Clicking anywhere else collapses the row.
|
|
Group members can click through to any charted item's entries read-only; only the owner (or an
|
|
admin) can edit. Everyone also has a personal chart at `/my-bowler` covering all of their own items.
|
|
|
|
### REST API
|
|
|
|
Create a key on `/api-keys`, then call `/api/v1/...` with `Authorization: Bearer <key>`.
|
|
Full interactive documentation at `/api/docs` (OpenAPI spec: `/api-docs/openapi.json`).
|
|
|
|
```sh
|
|
curl -H "Authorization: Bearer $KEY" http://localhost:8080/api/v1/kpis
|
|
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
|
|
-d '{"entry_date":"2026-07-09","value":96.2}' \
|
|
http://localhost:8080/api/v1/kpis/1/entries
|
|
```
|
|
|
|
## Project layout
|
|
|
|
```
|
|
docker-compose.yml # app (PHP 8.4 + Apache) + db (Percona latest)
|
|
.env # all credentials & app config (never commit real secrets)
|
|
docker/app/ # app image: Dockerfile + startup script
|
|
src/ # the Laravel application (bind-mounted into the container)
|
|
db-data/ # Percona data files (created on first run)
|
|
```
|
|
|
|
## Styling (Tailwind CSS)
|
|
|
|
The UI is styled with [Tailwind CSS](https://tailwindcss.com) v4 using the **standalone CLI**
|
|
baked into the app image — no Node.js/npm required anywhere. The source file is
|
|
`src/resources/css/app.css`; the compiled stylesheet `src/public/css/app.css` is generated
|
|
automatically on every container start.
|
|
|
|
While editing Blade views, run the optional watcher so CSS rebuilds live:
|
|
|
|
```sh
|
|
docker compose --profile dev up -d # starts the app + a tailwind --watch container
|
|
```
|
|
|
|
Or rebuild once by hand:
|
|
|
|
```sh
|
|
docker compose exec app tailwindcss -i resources/css/app.css -o public/css/app.css --minify
|
|
```
|
|
|
|
## Common operations
|
|
|
|
```sh
|
|
docker-compose logs -f app # tail application logs
|
|
docker-compose exec app php artisan migrate # run new migrations
|
|
docker-compose exec app php artisan tinker # interactive REPL
|
|
docker-compose down # stop (data persists in db-data/)
|
|
```
|
|
|
|
## Performance notes
|
|
|
|
The stack is tuned for running from Windows bind mounts (the slowest part of this setup):
|
|
|
|
- **OPcache + realpath cache** (`docker/app/php-perf.ini`): compiled PHP stays in memory and
|
|
source files are re-checked at most every 60 s — after editing PHP code, wait up to a
|
|
minute or restart the app container.
|
|
- **Laravel caches**: the entrypoint runs `php artisan optimize` (config/route/view caches)
|
|
on every container start. While actively developing routes or config, run
|
|
`docker compose exec app php artisan optimize:clear` to work uncached; restart to re-cache.
|
|
Keep routes controller-based — closures break route caching.
|
|
- **Percona flags** (docker-compose.yml): redo log flushed once per second instead of per
|
|
commit (dev-appropriate durability trade-off), larger buffer pool, no reverse-DNS.
|
|
|
|
If you ever want a substantially faster database and can accept the data living inside
|
|
Docker's managed storage instead of `./db-data`, switch the db volume to a named volume.
|
|
|
|
## Roadmap
|
|
|
|
- Bowler chart data model and CRUD
|
|
- Multiple charts with per-chart owners and collaborators
|
|
- PDF / printer export of bowler summaries
|