v0.12.10 ui bug fixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rosterchirp-frontend",
|
||||
"version": "0.12.9",
|
||||
"version": "0.12.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -21,7 +21,9 @@ import Avatar from '../components/Avatar.jsx';
|
||||
|
||||
function UserCheckList({ allUsers, selectedIds, onChange, onIF, onIB }) {
|
||||
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 (
|
||||
<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} />
|
||||
|
||||
@@ -356,33 +356,47 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
|
||||
)}
|
||||
|
||||
{/* 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' && (
|
||||
<>
|
||||
<input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)}
|
||||
onFocus={onIF} onBlur={onIB}
|
||||
autoComplete="new-password" autoCorrect="off" spellCheck={false}
|
||||
style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
|
||||
<div style={{ background:'var(--surface)', borderRadius:'var(--radius)', boxShadow:'var(--shadow-sm)', overflow:'hidden' }}>
|
||||
{loading ? (
|
||||
<div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div>
|
||||
) : loadError ? (
|
||||
<div style={{ padding:32, textAlign:'center', color:'var(--error)' }}>
|
||||
<div style={{ marginBottom:12 }}>⚠ {loadError}</div>
|
||||
<button className="btn btn-secondary btn-sm" onClick={load}>Retry</button>
|
||||
</div>
|
||||
) : filtered.length === 0 ? (
|
||||
<div style={{ padding:32, textAlign:'center', color:'var(--text-tertiary)', fontSize:14 }}>
|
||||
{search ? 'No users match your search.' : 'No users yet.'}
|
||||
</div>
|
||||
) : (
|
||||
filtered.map(u => <UserRow key={u.id} u={u} onUpdated={load} />)
|
||||
)}
|
||||
{/* 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)}
|
||||
onFocus={onIF} onBlur={onIB}
|
||||
autoComplete="new-password" autoCorrect="off" spellCheck={false}
|
||||
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' }}>
|
||||
{loading ? (
|
||||
<div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div>
|
||||
) : loadError ? (
|
||||
<div style={{ padding:32, textAlign:'center', color:'var(--error)' }}>
|
||||
<div style={{ marginBottom:12 }}>⚠ {loadError}</div>
|
||||
<button className="btn btn-secondary btn-sm" onClick={load}>Retry</button>
|
||||
</div>
|
||||
) : filtered.length === 0 ? (
|
||||
<div style={{ padding:32, textAlign:'center', color:'var(--text-tertiary)', fontSize:14 }}>
|
||||
{search ? 'No users match your search.' : 'No users yet.'}
|
||||
</div>
|
||||
) : (
|
||||
filtered.map(u => <UserRow key={u.id} u={u} onUpdated={load} />)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{tab === 'create' && <CreateUserForm userPass={userPass} onCreated={() => { load(); setTab('users'); }} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
|
||||
{tab === 'bulk' && <BulkImportForm userPass={userPass} onCreated={load} />}
|
||||
{tab === 'create' && (
|
||||
<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>
|
||||
|
||||
{/* Mobile footer — fixed, hidden when any input is focused (keyboard open) */}
|
||||
|
||||
Reference in New Issue
Block a user