v0.0.2 new updated to the vibecode-prompt

This commit is contained in:
2026-04-08 11:41:56 -04:00
parent 23382d0301
commit da7ca20228
22 changed files with 939 additions and 83 deletions

View File

@@ -1,4 +1,4 @@
# CLAUDE.md — PlayersEdge
# CLAUDE.md — StatSphere
This file tells Claude how to work in this codebase. Read it fully before making any changes.
@@ -6,13 +6,25 @@ This file tells Claude how to work in this codebase. Read it fully before making
## Current version
- `Version:` 0.0.1
- `Version:` 0.0.2
### Bumping the version
When releasing a new version, update **all three** of these together — they must stay in sync:
1. `src/version.js``export const APP_VERSION = 'X.Y.Z';`
2. `package.json``"version": "X.Y.Z"`
3. This file — `Version:` line above
The version is displayed in the app as a fixed bottom-right label via `src/components/Footer.jsx`.
---
## What This Project Is
PlayersEdge is a client-side Progressive Web App for tracking athlete biometric and sport-specific statistics. It supports five sports (American Football, Hockey, Baseball, Soccer, Basketball) with multi-sport athlete profiles. There is no backend, no database, and no API — all data lives in `localStorage`. The app is built with React 18 + Vite and deployed as static files behind Nginx on Ubuntu 24.04.
StatSphere is a Progressive Web App for tracking athlete biometric and sport-specific statistics. It supports five sports (American Football, Hockey, Baseball, Soccer, Basketball) with multi-sport athlete profiles. The app is built with React 18 + Vite and deployed as static files behind Nginx on Ubuntu 24.04.
**Current architecture:** All data lives in `localStorage` — there is no backend, no database, no API, and no server-side email or scraping. Authentication is simulated client-side. This is an intermediate state; the long-term direction (per `vibecode-prompt.md`) adds Postgres, real auth, per-match stats entry, email validation, CSV upload, URL scraping, and role-based access control. Do not add those backend features unless explicitly asked — build against the current localStorage architecture.
---
@@ -35,20 +47,25 @@ There are no tests. There is no linter config. There is no TypeScript.
All app state flows through a single React Context defined in `src/hooks/useStore.jsx`.
- `StoreProvider` wraps the entire app in `App.jsx`
- Data is loaded from `localStorage` key `PlayersEdge_users_v1` on first render; falls back to `SEED_USERS` from `seedData.js` if the key is absent or unparseable
- Every change to `users` is auto-saved to `localStorage` via a `useEffect`
- There is no per-session state beyond what's in localStorage — refreshing the page preserves all data
- Users are loaded from `localStorage` key `statsphere_users_v1` on first render; falls back to `SEED_USERS` from `seedData.js` if the key is absent or unparseable
- Auth state is loaded from `localStorage` key `statsphere_auth_v1`
- Every change to `users` or `auth` is auto-saved to `localStorage` via `useEffect`
**Do not use component-level state for anything that needs to persist.** Route it through `useStore`.
**The store API:**
```js
const { users, addUser, updateUser, deleteUser, getUserById, getUsersBySport, resetToSeed } = useStore();
const { users, auth, login, logout, register, addUser, updateUser, deleteUser, getUserById, getUsersBySport, resetToSeed } = useStore();
```
- `addUser(user)` — generates `id` (Date.now()) and `joinDate` automatically
- `auth``{ currentUser: User|null, isLoggedIn: Boolean }`
- `login(email, password)` — returns `{ success, user }` or `{ success: false, error }`; sets auth state on success
- `logout()` — clears auth state
- `register(userData)` — checks for duplicate email, calls `addUser`, returns `{ success, user }` or `{ success: false, error }`
- `addUser(user)` — generates `id` (Date.now()) and `joinDate` automatically; returns new user
- `updateUser(id, updates)` — shallow-merges updates into the matching user
- `deleteUser(id)` — removes by id
- `getUserById(id)` — returns single user or undefined
- `getUsersBySport(sport)` — filters `users` where `u.sports.includes(sport)`
- `resetToSeed()` — replaces all users with `SEED_USERS`
@@ -58,19 +75,34 @@ React Router v6. All routes are defined once in `App.jsx`. The Nginx config uses
```
/ → Home.jsx
/leaders → Leaders.jsx (accepts ?sport= query param)
/leaders → Leaders.jsx (accepts ?sport= query param)
/filter → Filter.jsx
/athletes → Athletes.jsx
/athletes/:id → AthleteDetail.jsx
/register → Register.jsx (accepts ?edit={id} query param)
/register → Register.jsx (accepts ?edit={id} query param)
/login → Login.jsx (redirects to /dashboard if already logged in)
/dashboard → Dashboard.jsx (redirects to /login if not authenticated; role-aware content)
— auth-protected placeholder routes (coming soon) —
/stats-entry → StatsEntry.jsx
/manage-athletes → ManageAthletes.jsx
/bulk-upload → BulkUpload.jsx
/manage-teams → ManageTeams.jsx
/team-roster → TeamRoster.jsx
/my-clients → MyClients.jsx
/client-contacts → ClientContacts.jsx
/admin/users → AdminUsers.jsx (administrator only)
/admin/settings → AdminSettings.jsx (administrator only)
/admin/reports → AdminReports.jsx (administrator only)
```
### Data Layer
`src/data/seedData.js` is the single source of truth for:
- `USER_ROLES` — role registry object: `athlete`, `manager`, `team_manager`, `agent`, `administrator`
- `SPORTS` — sport registry object
- `SPORT_STATS_DEFS` — stat definitions per sport (key, label, type)
- `BIOMETRIC_FIELDS` — 12 biometric field definitions
- `BIOMETRIC_FIELDS` — 13 biometric field definitions (includes both `DOB` date field and `age` number field)
- `SEED_USERS` — 125 generated fake athletes
- `getUsersBySport(sport)` — exported helper (also re-implemented in the store)
@@ -93,6 +125,8 @@ Stat definition shape:
```js
{
id: String, // Date.now() string for new users; '101''225' for seed users
role: String, // 'athlete' | 'manager' | 'team_manager' | 'agent' | 'administrator'
password: String, // plain text for now — no hashing in localStorage implementation
firstName: String,
lastName: String,
name: String, // firstName + ' ' + lastName — must be kept in sync
@@ -112,8 +146,10 @@ Stat definition shape:
primarySport: String, // one of the SPORTS keys
sports: String[], // array of sport keys — always contains primarySport
biometrics: {
height_cm, weight_kg, age, reach_cm,
dominant_hand, dominant_foot,
height_cm, weight_kg,
DOB: String, // date of birth, YYYY-MM-DD format
age: Number, // computed from DOB at generation time — kept for display convenience
reach_cm, dominant_hand, dominant_foot,
body_fat_pct, vo2_max, vertical_jump_cm,
'40_yard_dash', bench_press_reps, years_pro
},
@@ -123,6 +159,7 @@ Stat definition shape:
// only present for sports in the user's sports array
},
joinDate: String, // YYYY-MM-DD
lastLogin: String, // ISO datetime string, set on login
}
```
@@ -302,7 +339,7 @@ If the stat should appear in seed data, also update the relevant `gen{Sport}Stat
The PWA is configured in `vite.config.js` via `vite-plugin-pwa`. The service worker is auto-generated by Workbox and caches all `{js,css,html,ico,png,svg,woff2}` files. `registerType: 'autoUpdate'` means the SW updates silently on rebuild.
The install prompt (`PWABanner.jsx`) listens for `beforeinstallprompt`. Once dismissed, it sets `localStorage.getItem('pwa_dismissed')` and never shows again. This is separate from `PlayersEdge_users_v1`.
The install prompt (`PWABanner.jsx`) listens for `beforeinstallprompt`. Once dismissed, it sets `localStorage.getItem('pwa_dismissed')` and never shows again. This is separate from `statsphere_users_v1` and `statsphere_auth_v1`.
PWA icons (`public/icon-192.png`, `public/icon-512.png`, `public/apple-touch-icon.png`) are placeholder PNGs. Replace them with real images for production.
@@ -323,14 +360,33 @@ npm run build
# dist/ is ready — rsync or cp to web root
```
### When to rerun install.sh
**You do not need to rerun `install.sh` for normal code changes.** Only rerun it when:
| Change | Action required |
|---|---|
| New package added to `dependencies` or `devDependencies` in `package.json` | `npm install` on the server (or full `install.sh` rerun) |
| Node.js version requirement changes | Full `install.sh` rerun |
| Nginx config changes | Full `install.sh` rerun (or manually edit `/etc/nginx/sites-available/statsphere`) |
| New system-level dependency (e.g. ImageMagick, a native Node addon) | Full `install.sh` rerun |
**Claude will flag this:** Any time a task adds or removes a package from `package.json`, Claude will explicitly note that `npm install` (or `install.sh`) must be rerun on the server before the next deployment.
---
## Known Limitations / Things to Be Aware Of
- **Profile images are base64 in localStorage.** Large images can approach the ~5MB localStorage limit. There is no file size validation.
- **No authentication.** Any visitor can edit or delete any athlete.
- **Passwords are stored plain text.** The localStorage auth implementation has no hashing. This is intentional for the current client-side-only phase.
- **Auth is client-side only.** Anyone who clears localStorage or inspects it can bypass auth. This is a known limitation of the localStorage architecture.
- **`40_yard_dash` key has a leading digit.** Access it as `biometrics['40_yard_dash']`, not `biometrics.40_yard_dash`. This is intentional — the label reads naturally in the UI.
- **`user.name` is not auto-derived.** When updating `firstName` or `lastName`, also update `name`. The `addUser` function handles this; `updateUser` does not — it takes whatever you pass.
- **`user.age` and `user.biometrics.DOB` both exist.** `DOB` is the canonical date of birth (YYYY-MM-DD); `age` is derived from it at seed-generation time. Display `DOB` for logged-in users where personal details are shown; display `age` (or compute from DOB) for public views.
- **Seed data is regenerated randomly on every `npm run build`** (if localStorage is cleared). The randomness is seeded by `Math.random()` with no fixed seed. This is intentional — seed data is fake and disposable.
- **The Leaders table shows the first 10 numeric stats** for the selected sport. If a sport has stats that should be prioritised in the table, order them first in `SPORT_STATS_DEFS` (after the `position` text field).
- **`fmtVal` in Leaders shows `` for `0`** — this is a display choice, not a bug. Stats that are genuinely 0 (e.g. a QB's sack count as a defender stat) display as `` to reduce noise.
- **Dashboard placeholder routes exist but are not yet implemented.** `/stats-entry`, `/manage-athletes`, `/bulk-upload`, `/manage-teams`, `/team-roster`, `/my-clients`, `/client-contacts`, `/admin/users`, `/admin/settings`, `/admin/reports` all have stub pages that show "Coming soon." They are the starting point for the features described in `vibecode-prompt.md`.
- **Sport stats are not collected at registration.** The Register wizard (Personal Info → Biometrics → Review) skips sport stats. Athletes add stats after account setup via `/stats-entry`.
- **Football positions** in `seedData.js` (internal `positions` object): `QB, RB, WR, TE, FB, OC, OG, OT, DE, DT, LB, CB, S, P, K`. Register.jsx does not have its own positions object — sport stats are entered post-registration.
- **Hockey positions** in `seedData.js`: `C, LW, RW, RD, LD, G`.