From 6e88a2a19a8ac76a0d6becc0b8ebb0c0470ccb00 Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Wed, 8 Apr 2026 12:02:25 -0400 Subject: [PATCH] minor rule changes for viewing athletes --- CLAUDE.md | 11 ++++++++++- install.sh | 28 ++++++++++++++-------------- src/pages/AthleteDetail.jsx | 30 ++++++++++++++++++------------ src/pages/Athletes.jsx | 3 ++- src/pages/Filter.jsx | 3 ++- src/pages/Home.jsx | 3 ++- src/pages/Leaders.jsx | 3 ++- src/utils/formatName.js | 12 ++++++++++++ 8 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 src/utils/formatName.js diff --git a/CLAUDE.md b/CLAUDE.md index 646240a..9947cd0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -180,8 +180,17 @@ src/components/ — shared, reusable, no page logic src/pages/ — one file per route, owns that route's state src/hooks/ — useStore only (for now) src/data/ — seedData only +src/utils/ — pure helper functions shared across pages ``` +### Athlete Name Display + +**Always use `fmtName(user)` from `src/utils/formatName.js` in all lists, tables, and cards.** This displays `"Lastname, F."` (e.g. `"Thompson, M."`). + +The only place full names appear is `AthleteDetail.jsx`, and only when `auth.isLoggedIn` is true. Non-logged-in visitors see the formatted name even on the profile page. + +Contact details (email, phone) and social media handles are also hidden on `AthleteDetail.jsx` unless `auth.isLoggedIn`. + ### Import Style All imports use explicit `.jsx` extensions. No index barrel files. Example: @@ -368,7 +377,7 @@ npm run build |---|---| | 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`) | +| Nginx config changes | Full `install.sh` rerun (or manually edit `/etc/nginx/sites-available/playersedge`) | | 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. diff --git a/install.sh b/install.sh index b9e656f..974b1a6 100644 --- a/install.sh +++ b/install.sh @@ -1,15 +1,15 @@ #!/bin/bash -# StatSphere - Ubuntu 24.04 LXC Install Script +# PlayersEdge - Ubuntu 24.04 LXC Install Script # Run as root or with sudo set -e -APP_DIR="/opt/statsphere" -APP_USER="statsphere" +APP_DIR="/opt/playersedge" +APP_USER="playersedge" NODE_VERSION="20" echo "============================================" -echo " StatSphere Athlete Stats Platform Installer" +echo " PlayersEdge Athlete Stats Platform Installer" echo "============================================" # 1. System update @@ -26,7 +26,7 @@ echo " NPM: $(npm --version)" # 3. Create app user echo "[3/8] Creating app user..." -id -u $APP_USER &>/dev/null || useradd -r -s /bin/false -d $APP_DIR $APP_USER +id -u $APP_USER &>/dev/null || useradd -r -s /bin/false -d $APP_DIR playersedge # 4. Copy app files echo "[4/8] Setting up application directory..." @@ -44,13 +44,13 @@ sudo -u $APP_USER npm run build # 6. Configure Nginx echo "[7/8] Configuring Nginx..." -cat > /etc/nginx/sites-available/statsphere <<'NGINX' +cat > /etc/nginx/sites-available/playersedge <<'NGINX' server { listen 80; listen [::]:80; server_name _; - root /opt/statsphere/dist; + root /opt/playersedge/dist; index index.html; # Gzip @@ -77,7 +77,7 @@ server { NGINX # Enable site -ln -sf /etc/nginx/sites-available/statsphere /etc/nginx/sites-enabled/ +ln -sf /etc/nginx/sites-available/playersedge /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default nginx -t systemctl restart nginx @@ -85,15 +85,15 @@ systemctl enable nginx # 7. Setup systemd service for dev server (optional) echo "[8/8] Setting up systemd service (dev preview mode)..." -cat > /etc/systemd/system/statsphere-dev.service <<'SERVICE' +cat > /etc/systemd/system/playersedge-dev.service <<'SERVICE' [Unit] -Description=StatSphere Development Server +Description=PlayersEdge Development Server After=network.target [Service] Type=simple -User=statsphere -WorkingDirectory=/opt/statsphere +User=playersedge +WorkingDirectory=/opt/playersedge ExecStart=/usr/bin/npm run preview Restart=on-failure RestartSec=5 @@ -123,6 +123,6 @@ echo " npm run build" echo " systemctl restart nginx" echo "" echo " Dev server (optional, port 4173):" -echo " systemctl start statsphere-dev" -echo " systemctl enable statsphere-dev" +echo " systemctl start playersedge-dev" +echo " systemctl enable playersedge-dev" echo "" diff --git a/src/pages/AthleteDetail.jsx b/src/pages/AthleteDetail.jsx index 367a966..1e75a9d 100644 --- a/src/pages/AthleteDetail.jsx +++ b/src/pages/AthleteDetail.jsx @@ -4,11 +4,13 @@ import { useStore } from '../hooks/useStore.jsx'; import { SPORTS, SPORT_STATS_DEFS, BIOMETRIC_FIELDS } from '../data/seedData.js'; import Avatar from '../components/Avatar.jsx'; import SportBadge from '../components/SportBadge.jsx'; +import { fmtName } from '../utils/formatName.js'; export default function AthleteDetail() { const { id } = useParams(); - const { getUserById, deleteUser } = useStore(); + const { getUserById, deleteUser, auth } = useStore(); const navigate = useNavigate(); + const isLoggedIn = auth?.isLoggedIn; const [activeSport, setActiveSport] = useState(null); const user = getUserById(id); @@ -37,7 +39,9 @@ export default function AthleteDetail() {
-

{user.name}

+

+ {isLoggedIn ? user.name : fmtName(user)} +

{user.city}, {user.country} · Joined {user.joinDate}
{user.sports?.map(s => )} @@ -50,10 +54,10 @@ export default function AthleteDetail() {
- {/* Social media */} - {user.socials && Object.keys(user.socials).length > 0 && ( + {/* Social media — logged-in only */} + {isLoggedIn && user.socials && Object.values(user.socials).some(Boolean) && (
- {Object.entries(user.socials).map(([platform, handle]) => ( + {Object.entries(user.socials).filter(([, handle]) => handle).map(([platform, handle]) => (
{socialIcons[platform] || '🔗'} {handle} @@ -63,14 +67,16 @@ export default function AthleteDetail() { )}
- {/* Contact */} -
-
Contact
-
-
Email
{user.email}
-
Phone
{user.phone}
+ {/* Contact — logged-in only */} + {isLoggedIn && ( +
+
Contact
+
+
Email
{user.email}
+
Phone
{user.phone}
+
-
+ )} {/* Biometrics */}
diff --git a/src/pages/Athletes.jsx b/src/pages/Athletes.jsx index e479032..3d8287a 100644 --- a/src/pages/Athletes.jsx +++ b/src/pages/Athletes.jsx @@ -4,6 +4,7 @@ import { useStore } from '../hooks/useStore.jsx'; import { SPORTS } from '../data/seedData.js'; import Avatar from '../components/Avatar.jsx'; import SportBadge from '../components/SportBadge.jsx'; +import { fmtName } from '../utils/formatName.js'; export default function Athletes() { const { users } = useStore(); @@ -48,7 +49,7 @@ export default function Athletes() {
-
{u.name}
+
{fmtName(u)}
{u.city}, {u.country}
diff --git a/src/pages/Filter.jsx b/src/pages/Filter.jsx index 6da8bd7..429740d 100644 --- a/src/pages/Filter.jsx +++ b/src/pages/Filter.jsx @@ -4,6 +4,7 @@ import { useStore } from '../hooks/useStore.jsx'; import { SPORTS, SPORT_STATS_DEFS, BIOMETRIC_FIELDS } from '../data/seedData.js'; import Avatar from '../components/Avatar.jsx'; import SportBadge from '../components/SportBadge.jsx'; +import { fmtName } from '../utils/formatName.js'; export default function Filter() { const { users } = useStore(); @@ -226,7 +227,7 @@ export default function Filter() { >
-
{u.name}
+
{fmtName(u)}
{u.biometrics?.age} yrs · {u.biometrics?.height_cm}cm · {u.city}, {u.country}
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 440bb0b..1074d80 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -2,6 +2,7 @@ import { Link } from 'react-router-dom'; import { useStore } from '../hooks/useStore.jsx'; import { SPORTS } from '../data/seedData.js'; import Avatar from '../components/Avatar.jsx'; +import { fmtName } from '../utils/formatName.js'; export default function Home() { const { users } = useStore(); @@ -96,7 +97,7 @@ export default function Home() { >
-
{u.name}
+
{fmtName(u)}
{u.city}, {u.country}
diff --git a/src/pages/Leaders.jsx b/src/pages/Leaders.jsx index a19da28..5062332 100644 --- a/src/pages/Leaders.jsx +++ b/src/pages/Leaders.jsx @@ -3,6 +3,7 @@ import { useSearchParams, Link } from 'react-router-dom'; import { useStore } from '../hooks/useStore.jsx'; import { SPORTS, SPORT_STATS_DEFS } from '../data/seedData.js'; import Avatar from '../components/Avatar.jsx'; +import { fmtName } from '../utils/formatName.js'; export default function Leaders() { const { getUsersBySport } = useStore(); @@ -120,7 +121,7 @@ export default function Leaders() {
-
{u.name}
+
{fmtName(u)}
{u.city}
diff --git a/src/utils/formatName.js b/src/utils/formatName.js new file mode 100644 index 0000000..764eef9 --- /dev/null +++ b/src/utils/formatName.js @@ -0,0 +1,12 @@ +/** + * Returns "Lastname, F." for public display. + * Use on all lists, tables, and cards. + * Only show the full name (user.name) on AthleteDetail when the viewer is logged in. + */ +export function fmtName(user) { + if (!user) return ''; + const last = user.lastName || ''; + const first = user.firstName || ''; + const initial = first ? `${first[0]}.` : ''; + return initial ? `${last}, ${initial}` : last; +}