A mobile‑first golf handicap tracker built with Vue 3 + Vite and Capacitor. It uses IndexedDB (via Dexie) for offline data, Chart.js for visualizations, and pdfjs‑dist for importing rounds directly from DGV Scoring Record PDFs. The project can be deployed as a static site (dist) or packaged as a native Android app via Capacitor.
HandyCap is a Vue 3 + Capacitor application for tracking your golf handicap index (HCPI) according to the World Handicap System (WHS) / DGV rules. It is designed to run in the browser as a PWA and can be compiled to a native Android application. The app leverages IndexedDB for persistent offline storage and provides charts for insights.
- Modern Vue 3 single‑page app using Vite for fast dev/build
- Fully strict Type‑safe codebase with TypeScript
- Premium modern UI with glassmorphism, responsive animations, and 'Inter' typography
- Offline‑first local storage with Dexie (IndexedDB)
- Handicap calculator view for computing Score Differentials and HCPI
- History view with handicap statistics (last HCPI, round count, lowest HI over 12 months, average of best 5 differentials) and a Chart.js time‑series chart
- PDF import (v1.1): import rounds directly from a DGV Scoring Record (Detailliert) PDF using pdfjs‑dist - no manual data entry required
- Internationalization via vue‑i18n (English & German)
- Material Design Icons for UI icons
- Android packaging with Capacitor
- Dockerfile for static hosting with NGINX
- Framework: Vue 3, vue‑router
- Build tooling: Vite
- Language: TypeScript
- Offline storage: Dexie (IndexedDB)
- Charts: Chart.js + chartjs‑adapter‑date‑fns
- PDF parsing: pdfjs‑dist
- Icons: material‑design‑icons‑iconfont
- Mobile: Capacitor (Android platform included)
- Node.js 20 or 22 (recommended) and npm
- Git
- Optional for Android builds:
- Java 21 (as used by the helper script)
- Android Studio (latest) + Android SDK/NDK
- Capacitor CLI
- Optional for Docker:
- Docker Engine 24+
Verify versions:
- node -v
- npm -v
- java -version (for Android)
- Clone the repository
- git clone https://github.com/bri-b-dev/handycap.git
- cd handycap
- Install dependencies
- npm install
- Start the dev server
- npm run dev
- Open the app
- Visit http://localhost:5173 (default Vite dev port)
- Vite config: vite.config.ts
- TypeScript config: tsconfig.json and tsconfig.app.json
- Capacitor config: capacitor.config.ts
- appId: com.bribdev.handycap
- appName: HandyCap
- webDir: dist
- Android project: android/ directory (generated/managed by Capacitor)
- NGINX config for static hosting: nginx.conf
Environment variables
VITE_APP_VERSION: Used to dynamically inject the app version into the "About" page (e.g.VITE_APP_VERSION=1.1.0). Note that Vite evaluates this at build time, so any changes to this variable require a freshnpm run buildandnpx cap sync androidto propagate into the Android APK.- If you add more custom environment variables, prefix them with
VITE_and document them here.
Android signing
- An example helper script exists at setup-android-release.sh which exports signing‑related environment variables. Adjust paths and secrets to your environment; do not commit actual secrets.
- Run dev server with HMR:
- npm run dev
- Type checking in watch/build pipelines:
- npm run type-check
Project entry points
- index.html
- src/ (application source)
Create a production build:
- npm run build
Preview the production build locally:
- npm run preview
The build outputs static files in dist/ suitable for serving by any static server or via the included Dockerfile/NGINX.
Capacitor workflows typically follow these steps:
- Build the web assets
- npm run build
- Sync web assets to native platform
- npx cap sync android
- Open Android Studio to build/run/sign
- npx cap open android
- Configure signing
- Use Android Studio or environment variables/gradle properties. The helper script setup-android-release.sh illustrates exporting:
- ANDROID_KEYSTORE_PATH
- ANDROID_KEYSTORE_PASSWORD
- ANDROID_KEY_ALIAS
- ANDROID_KEY_PASSWORD
- JAVA_HOME
- Build an APK/AAB in Android Studio (Build > Generate Signed Bundle/APK)
Notes
- Keep android/local.properties and private keystores out of version control.
- If the Android project becomes out of sync, re‑run npx cap sync android.
Build a production image:
- docker build -t handycap:latest .
Run the container (serves dist/ via NGINX):
- docker run --rm -p 8080:80 handycap:latest
Then open http://localhost:8080
From package.json:
- npm run dev - start Vite dev server
- npm run build - type‑check then build
- npm run build-only - build without type checking
- npm run type-check - run vue-tsc in build mode
- npm run preview - preview built app locally
The History view includes a "Import PDF" button that parses a DGV Scoring Record (Detailliert) PDF and loads all rounds into the local database automatically.
Requirements:
- Use the Scoring Record (Detailliert) export from your club's DGV handicap portal - not the Handicap History Sheet. The Scoring Record contains the official SD values already computed by the handicap server.
- The parser extracts: date, course name (club + tournament), number of holes, CR, Slope, HCPI at time of round, Adjusted Gross Score (GBE), and Score Differential (SD).
- SD values are taken directly from the PDF and are not recalculated.
- Duplicate detection is handled by the local database - re‑importing the same PDF is safe.
- After upgrading dependencies, delete node_modules and package-lock.json, then reinstall:
- rm -rf node_modules package-lock.json && npm install
- Vite dev server port conflicts: use PORT=5174 npm run dev or configure in vite.config.ts
- Capacitor Android build errors:
- Ensure Java 21 is configured (JAVA_HOME)
- Open the Android project from the android/ folder in Android Studio
- Re‑run npx cap sync android after web code changes that affect native assets
Contributions, issues, and feature requests are welcome. Please open a GitHub issue or pull request.
This project is licensed under the Apache License, Version 2.0.
- See the LICENSE file for details.
- You may obtain a copy at http://www.apache.org/licenses/LICENSE-2.0