From 0ecf037b5a1aad4667312d795a8408eb48bd72d5 Mon Sep 17 00:00:00 2001 From: artheru Date: Wed, 10 Jun 2026 05:39:56 +0800 Subject: [PATCH] account/settings page (0.35.0) - new #/account: profile card (avatar initials + role badge + quick links), change-password, and agent keys consolidated in one place - topbar username now opens Account; separate logout button - password change moved out of the tavern signed-in panel into a proper settings surface --- index.html | 2 +- package.json | 2 +- src/Account.jsx | 48 +++++++++++++++++++++++++++++++++++++++++++ src/AgentPlatform.jsx | 2 +- src/Scene2D.jsx | 11 +++++++--- src/styles.css | 15 ++++++++++++++ 6 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 src/Account.jsx diff --git a/index.html b/index.html index 1a8f579..4672da1 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - + diff --git a/package.json b/package.json index 32702c2..4735835 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amerc-site", - "version": "0.34.0-share", + "version": "0.35.0-account", "private": true, "type": "module", "scripts": { diff --git a/src/Account.jsx b/src/Account.jsx new file mode 100644 index 0000000..84fecff --- /dev/null +++ b/src/Account.jsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import { api } from './api.js'; +import { KeysCard } from './AgentPlatform.jsx'; +import { toast } from './toast.js'; + +function avatarStyle(name) { let h = 0; for (const ch of String(name)) h = (h * 31 + ch.charCodeAt(0)) >>> 0; const a = h % 360, b = (a + 70 + h % 90) % 360; return { background: `linear-gradient(135deg, hsl(${a} 68% 46%), hsl(${b} 64% 32%))` }; } + +export default function Account({ auth, setRoute }) { + const [cur, setCur] = useState(''); + const [nw, setNw] = useState(''); + const [err, setErr] = useState(''); + if (!auth.ready) return

Loading…

; + if (!auth.user) return

Account

Log in to manage your account.

; + const u = auth.user; + const changePw = async (e) => { + e.preventDefault(); setErr(''); + try { await api('/auth/password', { method: 'POST', body: { currentPassword: cur, newPassword: nw } }); setCur(''); setNw(''); toast('Password updated ✓'); } + catch (e2) { setErr(e2.message); } + }; + return ( +
+

Account

+
+
+ {u.handle.slice(0, 2).toUpperCase()} +

{u.handle}

+

{u.email}

+ {u.role} +
+ + + +
+
+
+

Change password

+
+ setCur(e.target.value)} required /> + setNw(e.target.value)} required minLength={6} /> + {err &&

{err}

} + +
+
+
+ +
+ ); +} diff --git a/src/AgentPlatform.jsx b/src/AgentPlatform.jsx index 82aa77c..dd1c6a7 100644 --- a/src/AgentPlatform.jsx +++ b/src/AgentPlatform.jsx @@ -165,7 +165,7 @@ export function UsePanel({ instance, onClose }) { } // ---- My Booth: publish classes + instances, manage ------------------------ -function KeysCard() { +export function KeysCard() { const [keys, setKeys] = useState([]); const [name, setName] = useState(''); const [created, setCreated] = useState(null); diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx index 05a9946..29c5db8 100644 --- a/src/Scene2D.jsx +++ b/src/Scene2D.jsx @@ -3,10 +3,11 @@ import { Menu, X } from 'lucide-react'; import { useAuth, AuthPanel, AdminConsole } from './Auth.jsx'; import { AgentBrowse, BoothDashboard } from './AgentPlatform.jsx'; import Mansion from './Mansion.jsx'; +import Account from './Account.jsx'; import TavernGame from './TavernGame.jsx'; import { api } from './api.js'; -const RELEASE = '0.34.0-share'; +const RELEASE = '0.35.0-account'; const NAV = [ { id: 'home', label: 'Tavern' }, @@ -14,7 +15,7 @@ const NAV = [ { id: 'mansion', label: 'Mansion' }, { id: 'booth', label: 'My Booth' }, ]; -const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'admin', 'signin']; +const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'account', 'admin', 'signin']; function useHashRoute() { const normalize = () => { @@ -50,7 +51,10 @@ function PixelTopbar({ route, setRoute, auth }) { {isAdmin && PM} {isAdmin && } {loggedIn - ? + ? <> + + + : } ); @@ -192,6 +196,7 @@ function Scene({ route, setRoute, auth }) { if (route === 'agents') return ; if (route === 'mansion') return ; if (route === 'booth') return ; + if (route === 'account') return ; if (route === 'signin') return
GATEHOUSE

Open Booth

One amerc account across tavern, docs, PM and git.

setRoute('booth')} /> diff --git a/src/styles.css b/src/styles.css index 3486b83..0e8f241 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1159,3 +1159,18 @@ button { .ap-inst-status.pulse { box-shadow: 0 0 0 0 rgba(54,240,176,0.7); animation: ap-pulse 1.8s infinite; } .ap-mine-class { display: flex; flex-direction: column; gap: 6px; } .ap-mine-row .ap-avatar-sm { flex: none; } + +/* account / settings (0.35) */ +.acct-grid { display: grid; grid-template-columns: 280px 1fr; gap: 16px; align-items: start; margin-bottom: 6px; } +.acct-card { padding: 20px; border-radius: 14px; background: #130d20; border: 1px solid #271c3a; } +.acct-profile { text-align: center; } +.acct-avatar { width: 64px; height: 64px; border-radius: 16px; font-size: 22px; font-weight: 700; color: #fff; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; font-family: Georgia, serif; } +.acct-profile h3 { margin: 0; color: #fff; font-size: 18px; } +.acct-email { color: #9a86b8; font-size: 13px; margin: 4px 0 10px; word-break: break-all; } +.acct-role { display: inline-block; font-size: 11px; padding: 3px 11px; border-radius: 12px; text-transform: uppercase; letter-spacing: 1px; } +.acct-role-admin { background: rgba(242,184,95,0.15); color: #f2b85f; border: 1px solid rgba(242,184,95,0.4); } +.acct-role-member { background: rgba(70,200,224,0.12); color: #46c8e0; border: 1px solid rgba(70,200,224,0.3); } +.acct-links { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; } +.acct-form { display: flex; flex-direction: column; gap: 10px; max-width: 360px; } +.acct-form input { padding: 10px; border-radius: 8px; background: #0a0f1a; border: 1px solid #2e2440; color: #eaf2ff; font-family: inherit; } +@media (max-width: 680px) { .acct-grid { grid-template-columns: 1fr; } }