v0.0.2 new updated to the vibecode-prompt
This commit is contained in:
86
CLAUDE.md
86
CLAUDE.md
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user