- TypeScript 86.1%
- CSS 11.5%
- Shell 0.8%
- JavaScript 0.8%
- HTML 0.5%
- Other 0.3%
Under 720px the top bar shrinks to the logo and navigation moves to a fixed tab bar: Home, Sessions, Exercises, Teams (Library when teams are off) and More, a sheet with the remaining pages, the account name and Log out. Toasts sit above the bar; viewport-fit=cover lets it clear the iPhone home indicator. Tablet and desktop keep the existing nav. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| prisma | ||
| public | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| prisma.config.ts | ||
| README.md | ||
| tsconfig.json | ||
Training Session PDF Generator
Internal web app for a small group of football coaches to generate training-session PDFs from a fixed template. Coaches log in with individual accounts, maintain a personal/shared exercise library, assemble sessions, preview them live, and generate a PDF that matches the reference template.
Local development
npm install
npx prisma migrate dev
npm run create-user -- <username> <password> # create your first coach account
npm run dev
Open http://localhost:3000 and log in with the account you created.
Working with prod data locally
To make local data identical to production, add the VPS details to .env:
PROD_SSH="root@1.2.3.4" # ssh target (or a ~/.ssh/config alias)
PROD_DIR="/root/tac-pdf" # repo checkout on the VPS, where docker-compose.yml lives
then, with npm run dev stopped:
npm run pull-prod
This takes a consistent snapshot of the prod SQLite database and the uploads
volume and replaces dev.db / uploads/ with them (the old ones are kept as
dev.db.bak / uploads.bak/). It only reads from prod — local changes never
reach the server. Re-run it whenever you want fresh data. Log in locally with
your prod account.
Environment variables
See .env for local dev defaults. Required:
DATABASE_URL— SQLite file path, e.g.file:./dev.dbSESSION_SECRET— random 32+ char string used to encrypt session cookiesUPLOADS_DIR— directory where uploaded exercise images are storedSMTP_USER/SMTP_PASS— Gmail address + Google App Password used to send registration verification codes (requires 2FA on that Google account; your normal Gmail password won't work here)
Deployment on an Ubuntu VPS
git clone <this repo's URL>
cd tac-pdf
./scripts/deploy.sh
deploy.sh checks whether Docker is installed (installs it via the official
script if not), generates a SESSION_SECRET into a local .env on first run,
then builds and starts the app with docker compose.
Create your first coach account once it's running:
docker compose exec app npm run create-user -- <username> <password>
The SQLite database and uploaded images persist in named Docker volumes (db-data, uploads-data) across rebuilds/restarts. Put a reverse proxy (nginx/Caddy) in front for TLS — this app only serves plain HTTP on port 3000.
Manual deployment (if you'd rather not use the script)
export SESSION_SECRET=$(openssl rand -hex 32)
export SMTP_USER="you@gmail.com"
export SMTP_PASS="your-16-char-app-password"
docker compose up -d --build
docker compose exec app npm run create-user -- <username> <password>
Open items before real use
- Tune the max-line-count caps on exercise Description/Coaching Points (
src/lib/exercise-limits.ts) against the real reference template. - Replace the placeholder club branding (
CLUB_BRANDINGinsrc/lib/exercise-limits.ts) with the real logo and footer text.