Go to file
Brian Fertig a76cc39494 Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
docker/app Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
src Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
.env.example first commit 2026-07-08 16:31:44 -06:00
.gitattributes first commit 2026-07-08 16:31:44 -06:00
.gitignore Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
README.md Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
docker-compose.yml Added Charts, Graphs, Types, Categories, etc. 2026-07-09 12:46:19 -06:00
software.md first commit 2026-07-08 16:31:44 -06:00

README.md

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

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 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.

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. Items whose team/category was deleted 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).

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 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:

docker compose --profile dev up -d      # starts the app + a tailwind --watch container

Or rebuild once by hand:

docker compose exec app tailwindcss -i resources/css/app.css -o public/css/app.css --minify

Common operations

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