v0.12.10 ui bug fixes
This commit is contained in:
@@ -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": {
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -356,33 +356,47 @@ 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' && (
|
||||||
<>
|
<>
|
||||||
<input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)}
|
{/* Search — always visible, outside scroll area */}
|
||||||
onFocus={onIF} onBlur={onIB}
|
<div style={{ padding:'16px 16px 8px', flexShrink:0 }}>
|
||||||
autoComplete="new-password" autoCorrect="off" spellCheck={false}
|
<input className="input" placeholder="Search users…" value={search} onChange={e => setSearch(e.target.value)}
|
||||||
style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
|
onFocus={onIF} onBlur={onIB}
|
||||||
<div style={{ background:'var(--surface)', borderRadius:'var(--radius)', boxShadow:'var(--shadow-sm)', overflow:'hidden' }}>
|
autoComplete="new-password" autoCorrect="off" spellCheck={false}
|
||||||
{loading ? (
|
style={{ width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
|
||||||
<div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div>
|
</div>
|
||||||
) : loadError ? (
|
{/* User list — bounded scroll */}
|
||||||
<div style={{ padding:32, textAlign:'center', color:'var(--error)' }}>
|
<div style={{ flex:1, overflowY:'auto', padding:'0 16px', paddingBottom: isMobile ? 72 : 16, overscrollBehavior:'contain' }}>
|
||||||
<div style={{ marginBottom:12 }}>⚠ {loadError}</div>
|
<div style={{ background:'var(--surface)', borderRadius:'var(--radius)', boxShadow:'var(--shadow-sm)', overflow:'hidden' }}>
|
||||||
<button className="btn btn-secondary btn-sm" onClick={load}>Retry</button>
|
{loading ? (
|
||||||
</div>
|
<div style={{ padding:48, textAlign:'center' }}><div className="spinner" /></div>
|
||||||
) : filtered.length === 0 ? (
|
) : loadError ? (
|
||||||
<div style={{ padding:32, textAlign:'center', color:'var(--text-tertiary)', fontSize:14 }}>
|
<div style={{ padding:32, textAlign:'center', color:'var(--error)' }}>
|
||||||
{search ? 'No users match your search.' : 'No users yet.'}
|
<div style={{ marginBottom:12 }}>⚠ {loadError}</div>
|
||||||
</div>
|
<button className="btn btn-secondary btn-sm" onClick={load}>Retry</button>
|
||||||
) : (
|
</div>
|
||||||
filtered.map(u => <UserRow key={u.id} u={u} onUpdated={load} />)
|
) : 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>
|
</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) */}
|
||||||
|
|||||||
Reference in New Issue
Block a user