v0.12.10 ui bug fixes

This commit is contained in:
2026-03-23 22:34:42 -04:00
parent 477b25dfa0
commit bcd9f4a060
5 changed files with 42 additions and 26 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "rosterchirp-backend", "name": "rosterchirp-backend",
"version": "0.12.9", "version": "0.12.10",
"description": "RosterChirp backend server", "description": "RosterChirp backend server",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {

View File

@@ -13,7 +13,7 @@
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
set -euo pipefail set -euo pipefail
VERSION="${1:-0.12.9}" VERSION="${1:-0.12.10}"
ACTION="${2:-}" ACTION="${2:-}"
REGISTRY="${REGISTRY:-}" REGISTRY="${REGISTRY:-}"
IMAGE_NAME="rosterchirp" IMAGE_NAME="rosterchirp"

View File

@@ -1,6 +1,6 @@
{ {
"name": "rosterchirp-frontend", "name": "rosterchirp-frontend",
"version": "0.12.9", "version": "0.12.10",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -21,7 +21,9 @@ import Avatar from '../components/Avatar.jsx';
function UserCheckList({ allUsers, selectedIds, onChange, onIF, onIB }) { function UserCheckList({ allUsers, selectedIds, onChange, onIF, onIB }) {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const filtered = allUsers.filter(u => (u.display_name||u.name).toLowerCase().includes(search.toLowerCase())); const filtered = allUsers
.filter(u => (u.display_name||u.name).toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => (a.display_name||a.name).localeCompare(b.display_name||b.name));
return ( return (
<div> <div>
<input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> <input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} />

View File

@@ -356,13 +356,18 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
)} )}
{/* Content */} {/* Content */}
<div style={{ flex:1, overflowY:'auto', padding:16, paddingBottom: isMobile ? 72 : 16, overscrollBehavior: 'contain' }}> <div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden', minHeight:0, background:'var(--background)' }}>
{tab === 'users' && ( {tab === 'users' && (
<> <>
{/* Search — always visible, outside scroll area */}
<div style={{ padding:'16px 16px 8px', flexShrink:0 }}>
<input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)} <input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)}
onFocus={onIF} onBlur={onIB} onFocus={onIF} onBlur={onIB}
autoComplete="new-password" autoCorrect="off" spellCheck={false} autoComplete="new-password" autoCorrect="off" spellCheck={false}
style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} /> style={{ width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
</div>
{/* User list — bounded scroll */}
<div style={{ flex:1, overflowY:'auto', padding:'0 16px', paddingBottom: isMobile ? 72 : 16, overscrollBehavior:'contain' }}>
<div style={{ background:'var(--surface)', borderRadius:'var(--radius)', boxShadow:'var(--shadow-sm)', overflow:'hidden' }}> <div style={{ background:'var(--surface)', borderRadius:'var(--radius)', boxShadow:'var(--shadow-sm)', overflow:'hidden' }}>
{loading ? ( {loading ? (
<div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div> <div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div>
@@ -379,10 +384,19 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
filtered.map(u => <UserRow key={u.id} u={u} onUpdated={load} />) filtered.map(u => <UserRow key={u.id} u={u} onUpdated={load} />)
)} )}
</div> </div>
</div>
</> </>
)} )}
{tab === 'create' && <CreateUserForm userPass={userPass} onCreated={() => { load(); setTab('users'); }} isMobile={isMobile} onIF={onIF} onIB={onIB} />} {tab === 'create' && (
{tab === 'bulk' && <BulkImportForm userPass={userPass} onCreated={load} />} <div style={{ flex:1, overflowY:'auto', padding:16, paddingBottom: isMobile ? 72 : 16, overscrollBehavior:'contain' }}>
<CreateUserForm userPass={userPass} onCreated={() => { load(); setTab('users'); }} isMobile={isMobile} onIF={onIF} onIB={onIB} />
</div>
)}
{tab === 'bulk' && (
<div style={{ flex:1, overflowY:'auto', padding:16, paddingBottom: isMobile ? 72 : 16, overscrollBehavior:'contain' }}>
<BulkImportForm userPass={userPass} onCreated={load} />
</div>
)}
</div> </div>
{/* Mobile footer — fixed, hidden when any input is focused (keyboard open) */} {/* Mobile footer — fixed, hidden when any input is focused (keyboard open) */}