// Shared · Account Settings — members edit their own profile. Used as the // "Account" tab in BOTH the student and admin/coach portals. // Exposes window.AccountSettings and window.cgDisplayName. // // LOCKED: registered name + email. They are identity of record, tied to // payments and account recovery; only the main admin can change a name. // // All writes go through update_my_profile() / update_my_coach_profile(). // There is deliberately NO RLS policy letting a member UPDATE their own row — // one would also expose tier, vip and status, and RLS cannot be scoped to a // single column. The RPCs write exactly the fields below and nothing else. // // birthdate lives in member_private, a table whose only policy is // "member reads own". Staff `select *` on students cannot reach it. (function(){ const { useState, useEffect } = React; function displayName(fullName, nickname){ const n=(nickname||'').trim(), f=(fullName||'').trim(); if(!n) return f; if(!f) return n; return f+' ('+n+')'; } window.cgDisplayName = displayName; // Zones our members actually live in, so nobody scrolls a 400-entry list. const ZONES = [ ['Asia/Manila','Philippines (PHT)'], ['Asia/Dubai','UAE (GST)'], ['Asia/Singapore','Singapore'], ['Asia/Hong_Kong','Hong Kong'], ['Asia/Tokyo','Japan'], ['Asia/Seoul','South Korea'], ['Asia/Qatar','Qatar'], ['Asia/Riyadh','Saudi Arabia'], ['Asia/Kuwait','Kuwait'], ['Asia/Almaty','Kazakhstan'], ['Europe/London','United Kingdom'], ['Europe/Rome','Italy'], ['Europe/Madrid','Spain'], ['Europe/Berlin','Germany'], ['America/Toronto','Canada (Eastern)'], ['America/Vancouver','Canada (Pacific)'], ['America/New_York','USA (Eastern)'], ['America/Los_Angeles','USA (Pacific)'], ['Australia/Sydney','Australia (Sydney)'], ['Pacific/Auckland','New Zealand'], ]; // 7:00 AM PH shown in the member's own zone — the whole point of storing it. function localEquivalent(tz){ if(!tz) return null; try{ const now = new Date(); const ph = new Date(now.toLocaleString('en-US',{timeZone:'Asia/Manila'})); const d = new Date(ph); d.setHours(7,0,0,0); const shift = d.getTime() - ph.getTime() + now.getTime(); return new Date(shift).toLocaleTimeString('en-US', {timeZone:tz, hour:'numeric', minute:'2-digit'}); }catch(e){ return null; } } const Row = ({label, hint, children}) =>
{children} {hint && {hint}}
; function AccountSettings({ client, role, profile, email, onSaved }){ const isCoach = role === 'coach'; const [f,setF] = useState({ nickname:'', phone:'', country:'', birthdate:'', telegram:'', timezone:'' }); const [start,setStart] = useState(f); const [busy,setBusy]=useState(false); const [msg,setMsg]=useState(''); const [err,setErr]=useState(''); useEffect(()=>{ if(!profile) return; const next = { nickname: profile.nickname||'', phone: profile.phone||'', country: profile.country||'', birthdate: profile.birthdate||'', telegram: profile.telegram_username||'', timezone: profile.timezone||'', }; setF(next); setStart(next); },[profile]); const set = k => e => { setF(s=>({...s,[k]:e.target.value})); setErr(''); setMsg(''); }; const dirty = JSON.stringify(f) !== JSON.stringify(start); const fullName = isCoach ? (profile&&profile.name) : (profile&&profile.full_name); async function save(){ setBusy(true); setErr(''); setMsg(''); const args = isCoach ? { p_nickname:f.nickname, p_phone:f.phone, p_birthdate:f.birthdate||null, p_telegram:f.telegram, p_timezone:f.timezone } : { p_nickname:f.nickname, p_phone:f.phone, p_country:f.country, p_birthdate:f.birthdate||null, p_telegram:f.telegram, p_timezone:f.timezone }; const { data, error } = await client.rpc(isCoach?'update_my_coach_profile':'update_my_profile', args); setBusy(false); if(error){ setErr(error.message||'Could not save your changes.'); return; } setStart(f); setMsg('Saved'); setTimeout(()=>setMsg(''),2400); if(onSaved) onSaved(data||{}); } const ro = { width:'100%', padding:'11px 13px', borderRadius:'var(--radius-sm)', background:'var(--surface-2)', border:'1px dashed var(--border)', color:'var(--ink-dim)', fontSize:'.92rem' }; const local = localEquivalent(f.timezone); return
Account

Your profile

Keep your details current so your coach can reach you and your session times are right.

{/* locked identity */}
{fullName || '—'}
{email||'—'}

🔒 These are locked. They're tied to your membership record, payments and account recovery. If either is wrong, message the admin and they'll correct it for you.

{/* editable */}
{!isCoach && }
{err &&
{err}
}
{dirty && !busy && } {msg && ✓ {msg}}

Need to change your password? Log out and use Forgot password on the sign-in page — we'll email you a secure reset link.

; } window.AccountSettings = AccountSettings; })();