No description
  • TypeScript 86.1%
  • CSS 11.5%
  • Shell 0.8%
  • JavaScript 0.8%
  • HTML 0.5%
  • Other 0.3%
Find a file
artiom a08851bf3f Bottom tab bar on phones instead of the hamburger menu
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>
2026-10-08 13:53:38 +03:00
prisma Bind session folders to a team for exact auto-attaching 2026-10-06 10:22:06 +03:00
public Save upcoming session plans for offline use at training 2026-10-05 18:14:15 +03:00
scripts Add pull-prod script to copy prod database and uploads locally 2026-10-05 15:36:29 +03:00
src Bottom tab bar on phones instead of the hamburger menu 2026-10-08 13:53:38 +03:00
.dockerignore Build training-session PDF generator app 2026-07-24 00:03:47 +03:00
.gitignore Add pull-prod script to copy prod database and uploads locally 2026-10-05 15:36:29 +03:00
AGENTS.md Initial commit from Create Next App 2026-07-23 20:00:37 +03:00
CLAUDE.md Initial commit from Create Next App 2026-07-23 20:00:37 +03:00
docker-compose.yml Wire ADMIN_PASSWORD through docker-compose; show exercise count in People tab 2026-07-30 20:38:01 +03:00
docker-entrypoint.sh Build training-session PDF generator app 2026-07-24 00:03:47 +03:00
Dockerfile Add coach display name, fix Docker PDF generation, add drag-and-drop reordering 2026-07-24 15:29:27 +00:00
eslint.config.mjs Initial commit from Create Next App 2026-07-23 20:00:37 +03:00
next.config.ts Save upcoming session plans for offline use at training 2026-10-05 18:14:15 +03:00
package-lock.json Animate dropped exercise cards flying into their target instead of snapping back 2026-07-28 17:01:11 +03:00
package.json Add pull-prod script to copy prod database and uploads locally 2026-10-05 15:36:29 +03:00
prisma.config.ts Build training-session PDF generator app 2026-07-24 00:03:47 +03:00
README.md Add pull-prod script to copy prod database and uploads locally 2026-10-05 15:36:29 +03:00
tsconfig.json Initial commit from Create Next App 2026-07-23 20:00:37 +03:00

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.db
  • SESSION_SECRET — random 32+ char string used to encrypt session cookies
  • UPLOADS_DIR — directory where uploaded exercise images are stored
  • SMTP_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_BRANDING in src/lib/exercise-limits.ts) with the real logo and footer text.