Files
PinPoint-Golf/DEVELOPER_GUIDE.md

249 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🛠️ PinPoint Golf — Developer & Architecture Guide
This guide is written for engineers looking to maintain, customize, or extend **PinPoint Golf**. It explains the full-stack architecture, user authentication flows, OAuth/SSO integration, database persistence, community benchmark mathematics, and outlines the prioritized roadmap of future features.
---
## 1. Project Directory Structure
```
.
├── server.ts # Full-stack Express server (API endpoints + Vite middleware / static files)
├── Dockerfile # Multi-stage production container build (Node.js -> Node.js Runner)
├── docker-compose.yaml # Compose file with persistent volume and OAuth environment vars
├── .env.example # Template for Google Maps, JWT, Google OAuth, and Authentik OIDC
├── index.html # HTML entry point with Plus Jakarta Sans & JetBrains Mono
├── package.json # Dependencies, esbuild scripts, and npm scripts
├── vite.config.ts # Vite 8 configuration with Tailwind v4 & path aliases
├── data/
│ └── pinpoint_db.json # Atomic JSON persistent database (users, rounds, settings, courses)
├── src/
│ ├── main.tsx # React root mount point
│ ├── App.tsx # Root coordinator, global state, persistence, sync & modal hubs
│ ├── index.css # Tailwind CSS v4 setup & custom scrollbars
│ ├── context/
│ │ └── AuthContext.tsx # User session state, JWT tokens, login, register, and SSO hooks
│ ├── types/
│ │ ├── golf.ts # Core golf interfaces (Hole, Course, Round, Stats, GPS)
│ │ └── auth.ts # User profiles, PublicGolferStats, CommunityComparison schemas
│ ├── data/
│ │ ├── courses.ts # Default course presets (Pebble Cove, Pine Valley, St Andrews)
│ │ └── sampleRounds.ts # Preloaded 18-hole demonstration round with full stats
│ ├── services/
│ │ ├── api.ts # Full client HTTP client with Bearer auth headers
│ │ ├── golfApi.ts # OpenGolfAPI client for searching 32,000+ public courses
│ │ └── weatherService.ts # Open-Meteo Open Weather API client & GPS geolocation service
│ ├── server/
│ │ ├── db.ts # Database manager (atomic file writes, seed golfers, user scoping)
│ │ ├── authMiddleware.ts # JWT verification & request user injection
│ │ └── routes/
│ │ ├── authRoutes.ts # Local register/login, user profile, settings, and SSO endpoints
│ │ ├── roundsRoutes.ts # Scoped rounds CRUD, sync-batch, and custom course endpoints
│ │ └── communityRoutes.ts # Global public leaderboard and comparative benchmark engine
│ ├── utils/
│ │ ├── golfStats.ts # Pure statistical calculation engine & scoring formulas
│ │ ├── geoUtils.ts # Haversine distance, bearings, green edges & hole auto-detect
│ │ └── achievements.ts # Automated trophy & achievements evaluator (23 achievements)
│ └── components/
│ ├── Navbar.tsx # 3-Zone top bar + user avatar profile dropdown + mobile tab bar
│ ├── AuthModal.tsx # Tabbed modal: Local Auth (Email/Pass), Register, and SSO/Authentik
│ ├── CommunityComparisonModal.tsx # Head-to-head comparison cards & global public leaderboard
│ ├── HoleEntry.tsx # Tactile on-course scoring UI with large touch hitboxes
│ ├── GolfGpsRangefinder.tsx # Google Maps satellite map + Front/Mid/Back digital HUD
│ ├── Scorecard.tsx # Paper-style 18-hole digital scorecard with Out/In splits
│ ├── Analytics.tsx # Performance dashboard (FIR radar, GIR misses, bunker saves)
│ ├── Settings.tsx # Granular stat switchboard & 1-click tracking presets
│ ├── NewRoundModal.tsx # Start round dialog with OpenGolfAPI search & tees
│ ├── CourseEditorModal.tsx # Builder to create new courses or duplicate existing ones
│ ├── RoundsHistoryModal.tsx # Round history, round switching, and CSV/text export
│ └── AchievementsModal.tsx # Trophy cabinet with tier filters and progress tracking
```
---
## 2. Authentication & Identity Architecture
PinPoint Golf supports dual-mode authentication:
1. **Local Authentication**:
- Passwords hashed with `bcryptjs` (salt rounds: 10).
- Generates signed JWTs (30-day expiry) containing user `id`, `email`, and `username`.
- Passwords and sensitive fields are stripped before transmitting user objects.
2. **OAuth & SSO Providers**:
- **Google OAuth 2.0**: Compatible with Google Identity Services. Configurable via `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
- **Authentik (OIDC)**: Designed for homelab setups, enterprise Identity Providers, and Keycloak. Configurable via `AUTHENTIK_ISSUER_URL`, `AUTHENTIK_CLIENT_ID`, and `AUTHENTIK_CLIENT_SECRET`.
- **GitHub OAuth**: Configurable via `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`.
- **Zero-Friction Test Mode**: If external secrets are not yet provisioned in `.env`, the modal allows one-click instant testing using authentic golfer SSO credentials.
### API Auth Endpoints:
| Endpoint | Method | Auth Required | Description |
|---|---|---|---|
| `/api/auth/register` | POST | No | Create local account with email, username, password, handicap |
| `/api/auth/login` | POST | No | Authenticate with email/username + password |
| `/api/auth/me` | GET | Bearer | Retrieve profile and synchronized tracking settings |
| `/api/auth/profile` | PUT | Bearer | Update name, handicap index, home club, avatar, or `isPublic` flag |
| `/api/auth/settings` | GET/PUT | Bearer | Read or persist user tracking switchboard preferences |
| `/api/auth/oauth/providers` | GET | No | Discovery endpoint for configured OAuth options |
| `/api/auth/oauth/sso-login` | POST | No | Ingest OAuth token / SSO identity and return JWT |
---
## 3. Persistent Database (`data/pinpoint_db.json`)
The datastore in `src/server/db.ts` uses atomic temp-file write-and-rename semantics (`fs.renameSync`) to ensure durability against crashes. In Docker, it mounts to the named volume `pinpoint_data:/app/data`.
### Database Schema:
```typescript
interface DatabaseSchema {
users: Array<User & { passwordHash?: string }>;
settings: Record<string, TrackingSettings>; // userId -> settings
rounds: Record<string, Round[]>; // userId -> rounds
customCourses: Record<string, Course[]>; // userId -> courses
publicSeedGolfers: PublicGolferStats[]; // Seed public players for comparison
}
```
---
## 4. Community Benchmark & Public Comparison Engine
Users can toggle their profile between **Public** (default) and **Private** via the profile dropdown or the Community modal:
- When `isPublic: true`, their rounds automatically compute into the global database metrics:
- **Scoring Average** (18-hole stroke average)
- **FIR %** (Fairways in Regulation)
- **GIR %** (Greens in Regulation)
- **Putts Per Round** (PPR)
- **Scramble %** (Up & Down par saves after missing GIR)
- **Sand Save %** (Greenside bunker par conversion)
- **Penalties Per Round** (Strokes lost to hazards/OB)
- The `/api/community/compare` endpoint calculates:
- Global community averages across all public scorecards.
- Handicap bracket averages: **Scratch (< 5.0)**, **Mid-Cap (5.0–15.0)**, and **High-Cap (> 15.0)**.
- Percentile rankings (0–100%) for each metric.
- Automated coaching insights identifying the golfer's highest-leverage scoring improvements.
---
## 5. Real-Time Weather Forecast Integration (Open Weather API)
Before teeing off, golfers evaluate live environmental conditions—temperature, wind velocity, heading, and precipitation probability—to select appropriate clubs, prepare rain equipment, and adjust for air density effects on ball carry.
### Architecture & Service Design (`src/services/weatherService.ts`)
- **Open Weather API Provider**: Uses the **Open-Meteo Meteorological API** (`https://api.open-meteo.com/v1/forecast`), an open-source, non-commercial and commercial-ready meteorological service operated with Open Database License (ODbL).
- **Zero API-Key Requirement**: Works out-of-the-box with zero paid subscription dependencies or API keys.
- **Dual Geolocation Resolution Strategy**:
1. **Active Device GPS Position**: Calls browser `navigator.geolocation.getCurrentPosition` with `enableHighAccuracy: true` to get the player's immediate physical coordinates.
2. **Course Location Fallback**: If the player is planning a round indoors, has GPS disabled, or denies browser permission, the system automatically falls back to coordinates from the selected course's Hole 1 tee box or course center (e.g. Pebble Beach, Pine Valley, St Andrews).
3. **Seamless Toggle**: Users can switch between `[ 📍 My GPS ]` and `[ ⛳ Course GPS ]` at any time with a 1-click refresh button.
### Real-Time Weather Metrics Captured
| Metric | Unit | API Parameter | Relevance to Golf |
|---|---|---|---|
| **Current Temperature** | °C (with °F converted) | `current.temperature_2m` | Ball compression and air density (colder air = less carry distance) |
| **Feels-Like Temperature** | °C | `current.apparent_temperature` | Golfer comfort, layering, and thermal prep |
| **Wind Speed** | km/h | `current.wind_speed_10m` | Club adjustment (rule of thumb: ~1 club per 15 km/h wind) |
| **Wind Direction & Heading** | 16-point Compass (`SW`) + Degrees (0–360°) | `current.wind_direction_10m` | Aim lines and shot shaping (crosswind, headwind, tailwind) |
| **Wind Intensity Category** | `Calm` / `Breezy` / `Strong` / `Gusty` | Auto-classified based on speed | Stored directly in Round summary for fast filtering |
| **Precipitation Probability** | % (0–100) | `hourly.precipitation_probability` | Rain risk assessment and green speed (wet greens roll slower) |
| **Weather Condition & WMO Code** | WMO 4677 code | `current.weather_code` | Mapped to UI icons (`Sun`, `CloudSun`, `CloudRain`, `CloudLightning`) |
| **Relative Humidity** | % | `current.relative_humidity_2m` | Atmospheric moisture and swing comfort |
### Scorecard Persistence & Data Flow
When a player starts a new round via `NewRoundModal`:
1. The forecast is automatically preloaded via GPS.
2. The user can review the live weather card and make optional manual adjustments or notes.
3. Upon clicking **Tee Off**, the full `WeatherForecast` object is attached to `round.weatherData`, and summary strings (`weather` and `wind`) are recorded.
4. Saved rounds in `Scorecard`, `RoundsHistoryModal`, and exports reflect the actual weather encountered during the round.
---
## 6. How-To Guides for Extending the App
### Guide A: Adding a New Tracked Stat (e.g. Approach Shot Club)
1. **Update Data Types (`src/types/golf.ts`)**: Add `approachClub?: string` to `HoleScore` and `trackApproachClub: boolean` to `TrackingSettings`.
2. **Update Settings Defaults (`src/data/courses.ts`)**: Set default in `DEFAULT_TRACKING_SETTINGS`.
3. **Add Toggle Switch (`src/components/Settings.tsx`)**: Add toggle under the relevant category.
4. **Add Input Controls (`src/components/HoleEntry.tsx`)**: Render input when `settings.trackApproachClub` is active.
5. **Scorecard & Analytics (`Scorecard.tsx` & `golfStats.ts`)**: Display row in scorecard and aggregate in round stats.
### Guide B: Adding a New Achievement / Trophy
1. Add definition in `ALL_ACHIEVEMENTS` in `src/utils/achievements.ts`.
2. Add rule evaluation in `evaluateAchievements()`.
3. Add icon mapping in `src/components/AchievementsModal.tsx`.
---
## 7. 🎯 Wanted / Future Features Roadmap
Here is the prioritized list of planned future enhancements:
### High Priority
1. **Strokes Gained (SG) Analytics Engine**:
- Compare shot performance against PGA Tour and scratch golfer baselines.
- Categorize strokes gained into **SG: Off-the-Tee (OTT)**, **SG: Approach**, **SG: Around-the-Green (ARG)**, and **SG: Putting**.
2. **Official World Handicap System (WHS) Calculator**:
- Automatic calculation of **Handicap Differential**: $\text{Differential} = \frac{113}{\text{Slope}} \times (\text{Adjusted Gross Score} - \text{Course Rating} - \text{PCC})$.
- Automatic 20-round scoring record maintaining the official index from the best 8 differentials.
3. **Multiplayer Live Round & Foursome Matchplay**:
- Real-time WebSockets synchronization so groups playing together can view live leaderboards, skins, match play, and stableford games hole-by-hole.
### Medium Priority
4. **AI Caddie & Shot Recommender**:
- Based on user's logged club distances, live GPS yardage to the pin, wind speed, and elevation, automatically suggest club selection and target aim line.
5. **Ball Flight & Dispersion Heatmaps on GPS Green Overlays**:
- Plot approach shot landing spots visually on the satellite map green to detect left/right slice or pull patterns.
6. **Progressive Web App (PWA) Offline Service Worker**:
- Installable on iOS/Android home screens with offline caching for courses with no cellular reception.
7. **Apple Watch / Wear OS Companion App**:
- High-contrast wrist interface showing Bushnell-style Front/Center/Back yardages and quick 1-tap score increments.
### Community & Social
8. **Golf Society & Tournament Director Mode**:
- Flight divisions (A/B/C), net and gross prizes, two-man scrambles, and printable tournament scorecards.
9. **Course Review & Condition Notes**:
- Golfer feedback on green speeds (Stimp reading), aerated greens notices, and course ratings.
---
## 8. Scripts Reference
```bash
# Start full-stack development server on port 3000
npm run dev
# Run TypeScript typechecks
npm run lint
# Build production bundle (client + server.cjs)
npm run build
# Clean temporary files
npm run clean
```
---
## 9. Clean Deployment & Data Integrity Rules
To guarantee enterprise-grade data hygiene and tour-level integrity, PinPoint Golf enforces strict separation between development testing and production deployment:
### 1. Mock Data Elimination in Production
- **Server Store**: In production (`NODE_ENV=production` or `ENABLE_MOCK_DATA!=true`), the database manager `src/server/db.ts` suppresses demo accounts (`usr_demo_golfer`) and seeded community golfers (`user_seed_*`). Only real registered users and scorecards exist.
- **Client App**: The client initializes rounds state to an empty list `[]` instead of pre-seeding `SAMPLE_ROUND`. The "Load Dev Sample Round" button is stripped in production builds (`import.meta.env.PROD`).
### 2. Authentication-Gated Features
The following features are exclusively unlocked for registered golfers:
- **Community Benchmarks & Leaderboards**: Public comparison of handicap, FIR%, GIR%, putts, and scrambles against peers.
- **Career Achievements & Trophies**: 22 tier-ranked achievements across scoring, accuracy, and short game.
- **Satellite GPS Rangefinder**: Real-time Google Maps satellite flyover, Front/Center/Back green distance calculation, pin targeting, and shot distance measurement.
- *Guests & Unauthenticated Users*: Can start rounds, view course scorecards, and check local play, but receive clear in-app prompts to sign in or create a free account to unlock GPS, community leaderboards, and trophies.
### 3. Achievement Lifecycle Rules
- **Empty Initial State**: New registered users start with zero achievements unlocked (`unlockedIds: []`).
- **Finalization Requirement**: Achievements are never awarded during live play; they are evaluated and awarded **strictly upon finishing and saving a completed round** (`isCompleted: true`).
### 4. Round Immutability & Deletion Workflow
- **Confirmation Modal**: Clicking "Finalize & Save Round" triggers `SaveRoundModal`, showing a complete scoring summary and an explicit warning that the round will be permanently locked.
- **Post-Save Immutability**: Once saved (`isCompleted: true`), hole scores and statistical inputs are locked against modification.
- **Deletion Only**: The only permitted post-save action on a finalized round is deletion from the golfer's history with confirmation.