bug fixes

This commit is contained in:
2026-03-29 13:19:06 -04:00
parent 2c80da4be2
commit c0cd5664e7
4 changed files with 58 additions and 42 deletions

View File

@@ -663,11 +663,14 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
return (
<>
<div style={{width:'100%',maxWidth:1024,overflowX:'auto'}}>
<div style={{minWidth:500}} onKeyDown={e=>{if(e.key==='Enter'&&e.target.tagName!=='TEXTAREA') e.preventDefault();}}>
{/* Title */}
<div style={{marginBottom:20}}>
<input className="input" placeholder="Add title" value={title} onChange={e => setTitle(e.target.value)} autoComplete="off" autoCorrect="off" autoCapitalize="sentences" style={{fontSize:20,fontWeight:700,border:'none',borderBottom:'2px solid var(--border)',borderRadius:0,padding:'4px 0',background:'transparent',width:'100%'}}/>
</div>
{/* form wrapper suppresses Chrome Android's autofill chip bar; autoComplete="off"
on individual inputs is ignored by Chrome but respected on the form element */}
<form autoComplete="off" onSubmit={e => e.preventDefault()}>
<div style={{minWidth:500}} onKeyDown={e=>{if(e.key==='Enter'&&e.target.tagName!=='TEXTAREA') e.preventDefault();}}>
{/* Title */}
<div style={{marginBottom:20}}>
<input className="input" placeholder="Add title" value={title} onChange={e => setTitle(e.target.value)} autoComplete="off" autoCorrect="off" autoCapitalize="sentences" style={{fontSize:20,fontWeight:700,border:'none',borderBottom:'2px solid var(--border)',borderRadius:0,padding:'4px 0',background:'transparent',width:'100%'}}/>
</div>
{/* Event Type */}
<FormRow label="Event Type">
@@ -766,6 +769,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
{event&&(isToolManager||(userId&&event.created_by===userId))&&<button className="btn btn-sm" style={{marginLeft:'auto',background:'var(--error)',color:'white'}} onClick={()=>onDelete(event)}>Delete</button>}
</div>
</div>
</form>
</div>
{showScopeModal&&<RecurringChoiceModal title="Edit recurring event" onConfirm={doSave} onCancel={()=>setShowScopeModal(false)}/>}
</>

View File

@@ -290,12 +290,15 @@ export default function UserManagerModal({ onClose }) {
return (
<div className="modal-overlay" onClick={e => e.target === e.currentTarget && onClose()}>
<div className="modal" style={{ maxWidth: 600, width: '100%' }}>
<div className="flex items-center justify-between" style={{ marginBottom: 20 }}>
<h2 className="modal-title" style={{ margin: 0 }}>User Manager</h2>
<button className="btn-icon" onClick={onClose}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
{/* form wrapper suppresses Chrome Android's autofill chip bar; autoComplete="off"
on individual inputs is ignored by Chrome but respected on the form element */}
<form autoComplete="off" onSubmit={e => e.preventDefault()}>
<div className="flex items-center justify-between" style={{ marginBottom: 20 }}>
<h2 className="modal-title" style={{ margin: 0 }}>User Manager</h2>
<button className="btn-icon" onClick={onClose}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div className="flex gap-2" style={{ marginBottom: 20 }}>
<button className={`btn btn-sm ${tab === 'users' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('users')}>All Users ({users.length})</button>
@@ -421,6 +424,7 @@ export default function UserManagerModal({ onClose }) {
)}
</div>
)}
</form>
</div>
</div>
);

View File

@@ -741,12 +741,16 @@ export default function GroupManagerPage({ isMobile = false, onProfile, onHelp,
</div>
)}
{/* Content */}
<div style={{ flex:1, display:'flex', overflow: isMobile ? 'auto' : 'hidden', paddingBottom: isMobile ? 'calc(82px + env(safe-area-inset-bottom, 0px))' : 0 }}>
{tab==='all' && <AllGroupsTab allUsers={allUsers} onRefresh={onRefresh} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
{tab==='dm' && <DirectMessagesTab allUserGroups={allUserGroups} onRefresh={onRefresh} refreshKey={refreshKey} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
{tab==='u2u' && <U2URestrictionsTab allUserGroups={allUserGroups} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
</div>
{/* form wrapper suppresses Chrome Android's autofill chip bar; autoComplete="off"
on individual inputs is ignored by Chrome but respected on the form element */}
<form autoComplete="off" onSubmit={e => e.preventDefault()}>
{/* Content */}
<div style={{ flex:1, display:'flex', overflow: isMobile ? 'auto' : 'hidden', paddingBottom: isMobile ? 'calc(82px + env(safe-area-inset-bottom, 0px))' : 0 }}>
{tab==='all' && <AllGroupsTab allUsers={allUsers} onRefresh={onRefresh} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
{tab==='dm' && <DirectMessagesTab allUserGroups={allUserGroups} onRefresh={onRefresh} refreshKey={refreshKey} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
{tab==='u2u' && <U2URestrictionsTab allUserGroups={allUserGroups} isMobile={isMobile} onIF={onIF} onIB={onIB} />}
</div>
</form>
{/* Mobile footer — fixed, hidden when any input is focused (keyboard open) */}
{isMobile && !inputFocused && (

View File

@@ -621,32 +621,35 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
)}
{/* Content */}
<div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden', minHeight:0, background:'var(--background)' }}>
{/* form wrapper suppresses Chrome Android's autofill chip bar; autoComplete="off"
on individual inputs is ignored by Chrome but respected on the form element */}
<form autoComplete="off" onSubmit={e => e.preventDefault()}>
<div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden', minHeight:0, background:'var(--background)' }}>
{/* LIST VIEW */}
{view === 'list' && (
<>
<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="off" autoCorrect="off" spellCheck={false}
style={{ width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
</div>
<div style={{ flex:1, overflowY:'auto', padding:'0 16px', paddingBottom: isMobile ? 'calc(82px + env(safe-area-inset-bottom, 0px))' : 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} onEdit={goEdit} />)
{/* LIST VIEW */}
{view === 'list' && (
<>
<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="off" autoCorrect="off" spellCheck={false}
style={{ width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
</div>
<div style={{ flex:1, overflowY:'auto', padding:'0 16px', paddingBottom: isMobile ? 'calc(82px + env(safe-area-inset-bottom, 0px))' : 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} onEdit={goEdit} />)
)}
</div>
</div>
@@ -677,6 +680,7 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
</div>
)}
</div>
</form>
{/* Mobile footer — fixed, hidden when keyboard is up */}
{isMobile && !inputFocused && (