bugs fixes due to reges curruption

This commit is contained in:
2026-03-20 23:19:45 -04:00
parent 6da08942a7
commit fc0c071d1d
18 changed files with 74 additions and 74 deletions

View File

@@ -455,7 +455,7 @@ export default function BrandingModal({ onClose }) {
className="input flex-1" className="input flex-1"
value={appName} value={appName}
maxLength={16} maxLength={16}
onChange={e = /> setAppName(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handleSaveName()} /> onChange={e => setAppName(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handleSaveName()} />
<button className="btn btn-primary btn-sm" onClick={handleSaveName} disabled={loading}>{loading ? '...' : 'Save'}</button> <button className="btn btn-primary btn-sm" onClick={handleSaveName} disabled={loading}>{loading ? '...' : 'Save'}</button>
</div> </div>
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 6 }}> <p style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 6 }}>

View File

@@ -134,7 +134,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
{editing ? ( {editing ? (
<div className="flex gap-2"> <div className="flex gap-2">
<input className="input flex-1" value={newName} onChange={e = /> setNewName(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handleRename()} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} /> <input className="input flex-1" value={newName} onChange={e => setNewName(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handleRename()} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} />
<button className="btn btn-primary btn-sm" onClick={handleRename}>Save</button> <button className="btn btn-primary btn-sm" onClick={handleRename}>Save</button>
<button className="btn btn-secondary btn-sm" onClick={() => setEditing(false)}>Cancel</button> <button className="btn btn-secondary btn-sm" onClick={() => setEditing(false)}>Cancel</button>
</div> </div>
@@ -165,7 +165,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
<input <input
className="input flex-1" className="input flex-1"
value={customName} value={customName}
onChange={e = /> setCustomName(e.target.value)} autoComplete="new-password" placeholder={group.owner_name_original || group.name} onChange={e => setCustomName(e.target.value)} autoComplete="new-password" placeholder={group.owner_name_original || group.name}
onKeyDown={e => e.key === 'Enter' && handleCustomName()} /> onKeyDown={e => e.key === 'Enter' && handleCustomName()} />
{customName.trim() !== savedCustomName ? ( {customName.trim() !== savedCustomName ? (
<button className="btn btn-primary btn-sm" onClick={handleCustomName} disabled={savingCustom}> <button className="btn btn-primary btn-sm" onClick={handleCustomName} disabled={savingCustom}>
@@ -217,7 +217,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
</div> </div>
{canManage && ( {canManage && (
<div style={{ marginTop: 12 }}> <div style={{ marginTop: 12 }}>
<input className="input" placeholder="Search to add member..." autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={addSearch} onChange={e = /> setAddSearch(e.target.value)} /> <input className="input" placeholder="Search to add member..." autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={addSearch} onChange={e => setAddSearch(e.target.value)} />
{addResults.length > 0 && addSearch && ( {addResults.length > 0 && addSearch && (
<div style={{ border: '1px solid var(--border)', borderRadius: 'var(--radius)', marginTop: 4, maxHeight: 150, overflowY: 'auto', background: 'var(--surface)' }}> <div style={{ border: '1px solid var(--border)', borderRadius: 'var(--radius)', marginTop: 4, maxHeight: 150, overflowY: 'auto', background: 'var(--surface)' }}>
{addResults.filter(u => !members.find(m => m.id === u.id)).map(u => ( {addResults.filter(u => !members.find(m => m.id === u.id)).map(u => (

View File

@@ -12,7 +12,7 @@ function UserCheckList({ allUsers, selectedIds, onChange }) {
return ( return (
<div> <div>
<input className="input" placeholder="Search users…" value={search} <input className="input" placeholder="Search users…" value={search}
onChange={e = /> setSearch(e.target.value)} style={{ marginBottom: 8 }} onChange={e => setSearch(e.target.value)} style={{ marginBottom: 8 }}
autoComplete="new-password" /> autoComplete="new-password" />
<div style={{ maxHeight: 200, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 'var(--radius)' }}> <div style={{ maxHeight: 200, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 'var(--radius)' }}>
{filtered.map(u => ( {filtered.map(u => (
@@ -155,7 +155,7 @@ function AllGroupsTab({ allUsers, onRefresh }) {
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
<div> <div>
<label className="settings-section-label">Group Name</label> <label className="settings-section-label">Group Name</label>
<input className="input" value={editName} onChange={e = /> setEditName(e.target.value)} <input className="input" value={editName} onChange={e => setEditName(e.target.value)}
placeholder="e.g. Coaches" style={{ marginTop: 6 }} placeholder="e.g. Coaches" style={{ marginTop: 6 }}
autoComplete="new-password" /> autoComplete="new-password" />
{isCreating && <p style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 5 }}>A matching Direct Message group will be created automatically.</p>} {isCreating && <p style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 5 }}>A matching Direct Message group will be created automatically.</p>}
@@ -296,7 +296,7 @@ function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey }) {
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
<div> <div>
<label className="settings-section-label">DM Name</label> <label className="settings-section-label">DM Name</label>
<input className="input" value={dmName} onChange={e = /> setDmName(e.target.value)} <input className="input" value={dmName} onChange={e => setDmName(e.target.value)}
placeholder="e.g. Coaches + Players" style={{ marginTop: 6 }} placeholder="e.g. Coaches + Players" style={{ marginTop: 6 }}
autoComplete="new-password" /> autoComplete="new-password" />
</div> </div>

View File

@@ -55,7 +55,7 @@ function FieldGroup({ label, children }) {
function Field({ label, value, onChange, placeholder, type = 'text', hint, required }) { function Field({ label, value, onChange, placeholder, type = 'text', hint, required }) {
return ( return (
<FieldGroup label={label}> <FieldGroup label={label}>
<input type={type} value={value} onChange={e = /> onChange(e.target.value)} autoComplete="new-password" placeholder={placeholder} required={required} <input type={type} value={value} onChange={e => onChange(e.target.value)} autoComplete="new-password" placeholder={placeholder} required={required}
autoComplete="new-password" autoCorrect="off" spellCheck={false} autoComplete="new-password" autoCorrect="off" spellCheck={false}
className="input" style={{ fontSize: 13 }} /> className="input" style={{ fontSize: 13 }} />
{hint && <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{hint}</span>} {hint && <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{hint}</span>}
@@ -229,7 +229,7 @@ function DeleteModal({ api, tenant, onClose, onDone }) {
Type <code style={{ background:'var(--background)', padding:'2px 6px', borderRadius:4 }}>{expected}</code> to confirm: Type <code style={{ background:'var(--background)', padding:'2px 6px', borderRadius:4 }}>{expected}</code> to confirm:
</p> </p>
{error && <div style={{ color:'var(--error)', fontSize:13, marginBottom:10 }}>{error}</div>} {error && <div style={{ color:'var(--error)', fontSize:13, marginBottom:10 }}>{error}</div>}
<input className="input" value={confirm} onChange={e = /> setConfirm(e.target.value)} autoComplete="new-password" placeholder={expected} style={{ marginBottom:16 }} autoComplete="new-password" /> <input className="input" value={confirm} onChange={e => setConfirm(e.target.value)} autoComplete="new-password" placeholder={expected} style={{ marginBottom:16 }} autoComplete="new-password" />
<div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}> <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
<button className="btn btn-secondary" onClick={onClose}>Cancel</button> <button className="btn btn-secondary" onClick={onClose}>Cancel</button>
<button className="btn btn-danger" onClick={handle} disabled={confirm !== expected || deleting}> <button className="btn btn-danger" onClick={handle} disabled={confirm !== expected || deleting}>
@@ -322,7 +322,7 @@ function KeyEntry({ onSubmit }) {
<h2 style={{ fontSize:18, fontWeight:700, margin:'0 0 4px' }}>Control Panel</h2> <h2 style={{ fontSize:18, fontWeight:700, margin:'0 0 4px' }}>Control Panel</h2>
<p style={{ color:'var(--text-secondary)', fontSize:13, margin:'0 0 20px' }}>Enter your host admin key to continue.</p> <p style={{ color:'var(--text-secondary)', fontSize:13, margin:'0 0 20px' }}>Enter your host admin key to continue.</p>
{error && <div style={{ padding:'8px 12px', background:'#fce8e6', color:'var(--error)', borderRadius:6, fontSize:13, marginBottom:14 }}>{error}</div>} {error && <div style={{ padding:'8px 12px', background:'#fce8e6', color:'var(--error)', borderRadius:6, fontSize:13, marginBottom:14 }}>{error}</div>}
<input type="password" className="input" value={key} onChange={e = /> setKey(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handle()} placeholder="Host admin key" autoFocus <input type="password" className="input" value={key} onChange={e => setKey(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handle()} placeholder="Host admin key" autoFocus
style={{ marginBottom:12, textAlign:'center' }} /> style={{ marginBottom:12, textAlign:'center' }} />
<button className="btn btn-primary" onClick={handle} disabled={checking} style={{ width:'100%', justifyContent:'center' }}> <button className="btn btn-primary" onClick={handle} disabled={checking} style={{ width:'100%', justifyContent:'center' }}>
{checking ? 'Checking…' : 'Unlock'} {checking ? 'Checking…' : 'Unlock'}
@@ -429,7 +429,7 @@ export default function HostPanel({ onProfile, onHelp, onAbout }) {
{/* Toolbar */} {/* Toolbar */}
<div style={{ padding:'0 24px 12px', flexShrink:0, display:'flex', gap:8, alignItems:'center', flexWrap:'wrap' }}> <div style={{ padding:'0 24px 12px', flexShrink:0, display:'flex', gap:8, alignItems:'center', flexWrap:'wrap' }}>
<input value={search} onChange={e = /> setSearch(e.target.value)} autoComplete="new-password" placeholder="Search tenants" <input value={search} onChange={e => setSearch(e.target.value)} autoComplete="new-password" placeholder="Search tenants…"
className="input" style={{ flex:1, minWidth:160, fontSize:13 }} autoComplete="new-password" /> className="input" style={{ flex:1, minWidth:160, fontSize:13 }} autoComplete="new-password" />
<button className="btn btn-secondary btn-sm" onClick={load} disabled={loading}>{loading ? '…' : '↻ Refresh'}</button> <button className="btn btn-secondary btn-sm" onClick={load} disabled={loading}>{loading ? '…' : '↻ Refresh'}</button>
<button className="btn btn-secondary btn-sm" onClick={handleMigrateAll} disabled={migrating}>{migrating ? 'Migrating…' : '⬆ Migrate All'}</button> <button className="btn btn-secondary btn-sm" onClick={handleMigrateAll} disabled={migrating}>{migrating ? 'Migrating…' : '⬆ Migrate All'}</button>

View File

@@ -119,7 +119,7 @@ function RecurrenceSheet({ value, onChange, onClose }) {
<div style={{ marginBottom:16 }}> <div style={{ marginBottom:16 }}>
<div style={{ fontSize:12,color:'var(--text-tertiary)',marginBottom:8 }}>Repeats every</div> <div style={{ fontSize:12,color:'var(--text-tertiary)',marginBottom:8 }}>Repeats every</div>
<div style={{ display:'flex',gap:10 }}> <div style={{ display:'flex',gap:10 }}>
<input type="number" className="input" min={1} max={99} value={customRule.interval||1} onChange={e= />upd('interval',Math.max(1,parseInt(e.target.value)||1))} style={{ width:70,textAlign:'center',fontSize:16 }}/> <input type="number" className="input" min={1} max={99} value={customRule.interval||1} onChange={e => upd('interval',Math.max(1,parseInt(e.target.value)||1))} style={{ width:70,textAlign:'center',fontSize:16 }}/>
<select className="input" value={customRule.unit||'week'} onChange={e=>upd('unit',e.target.value)} style={{ flex:1,fontSize:14 }}> <select className="input" value={customRule.unit||'week'} onChange={e=>upd('unit',e.target.value)} style={{ flex:1,fontSize:14 }}>
{['day','week','month','year'].map(u=><option key={u} value={u}>{u}{(customRule.interval||1)>1?'s':''}</option>)} {['day','week','month','year'].map(u=><option key={u} value={u}>{u}{(customRule.interval||1)>1?'s':''}</option>)}
</select> </select>
@@ -146,8 +146,8 @@ function RecurrenceSheet({ value, onChange, onClose }) {
{(customRule.ends||'never')===val&&<div style={{ width:10,height:10,borderRadius:'50%',background:'var(--primary)' }}/>} {(customRule.ends||'never')===val&&<div style={{ width:10,height:10,borderRadius:'50%',background:'var(--primary)' }}/>}
</div> </div>
<span style={{ flex:1,fontSize:15 }}>{lbl}</span> <span style={{ flex:1,fontSize:15 }}>{lbl}</span>
{val==='on'&&(customRule.ends||'never')==='on'&&<input type="date" className="input" value={customRule.endDate||''} onChange={e= />upd('endDate',e.target.value)} style={{ width:150 }}/>} {val==='on'&&(customRule.ends||'never')==='on'&&<input type="date" className="input" value={customRule.endDate||''} onChange={e => upd('endDate',e.target.value)} style={{ width:150 }}/>}
{val==='after'&&(customRule.ends||'never')==='after'&&<><input type="number" className="input" min={1} max={999} value={customRule.endCount||13} onChange={e= />upd('endCount',parseInt(e.target.value)||1)} style={{ width:64,textAlign:'center' }}/><span style={{ fontSize:13,color:'var(--text-tertiary)' }}>occurrences</span></>} {val==='after'&&(customRule.ends||'never')==='after'&&<><input type="number" className="input" min={1} max={999} value={customRule.endCount||13} onChange={e => upd('endCount',parseInt(e.target.value)||1)} style={{ width:64,textAlign:'center' }}/><span style={{ fontSize:13,color:'var(--text-tertiary)' }}>occurrences</span></>}
</div> </div>
))} ))}
</div> </div>
@@ -282,7 +282,7 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
<div style={{ flex:1,overflowY:'auto' }}> <div style={{ flex:1,overflowY:'auto' }}>
{/* Title */} {/* Title */}
<div style={{ padding:'16px 20px',borderBottom:'1px solid var(--border)' }}> <div style={{ padding:'16px 20px',borderBottom:'1px solid var(--border)' }}>
<input value={title} onChange={e = /> setTitle(e.target.value)} autoComplete="new-password" placeholder="Add title" autoComplete="new-password" autoCorrect="off" autoCapitalize="sentences" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:22,fontWeight:700,color:'var(--text-primary)',outline:'none' }}/> <input value={title} onChange={e => setTitle(e.target.value)} autoComplete="new-password" placeholder="Add title" autoComplete="new-password" autoCorrect="off" autoCapitalize="sentences" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:22,fontWeight:700,color:'var(--text-primary)',outline:'none' }}/>
</div> </div>
{/* Event Type */} {/* Event Type */}
@@ -364,7 +364,7 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
{/* Location */} {/* Location */}
<MobileRow icon={<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>}> <MobileRow icon={<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>}>
<input value={location} onChange={e = /> setLocation(e.target.value)} autoComplete="new-password" placeholder="Add location" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:15,color:'var(--text-primary)',outline:'none' }}/> <input value={location} onChange={e => setLocation(e.target.value)} autoComplete="new-password" placeholder="Add location" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:15,color:'var(--text-primary)',outline:'none' }}/>
</MobileRow> </MobileRow>
{/* Description */} {/* Description */}
@@ -397,7 +397,7 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
<input <input
autoFocus autoFocus
value={newTypeName} value={newTypeName}
onChange={e = /> setNewTypeName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&createEventType()} onChange={e => setNewTypeName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&createEventType()}
placeholder="Type name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} placeholder="Type name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false}
style={{ width:'100%',padding:'12px 14px',border:'1px solid var(--border)',borderRadius:'var(--radius)',fontSize:16,marginBottom:12,boxSizing:'border-box',background:'var(--background)',color:'var(--text-primary)' }} /> style={{ width:'100%',padding:'12px 14px',border:'1px solid var(--border)',borderRadius:'var(--radius)',fontSize:16,marginBottom:12,boxSizing:'border-box',background:'var(--background)',color:'var(--text-primary)' }} />
<div style={{ display:'flex',alignItems:'center',gap:12,marginBottom:16 }}> <div style={{ display:'flex',alignItems:'center',gap:12,marginBottom:16 }}>

View File

@@ -69,7 +69,7 @@ function MembersScreen({ group, allUsers, onBack }) {
<div style={{ position:'relative' }}> <div style={{ position:'relative' }}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--text-tertiary)" strokeWidth="2" style={{position:'absolute',left:10,top:'50%',transform:'translateY(-50%)',pointerEvents:'none'}}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--text-tertiary)" strokeWidth="2" style={{position:'absolute',left:10,top:'50%',transform:'translateY(-50%)',pointerEvents:'none'}}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input <input
value={search} onChange={e = /> setSearch(e.target.value)} autoComplete="new-password" placeholder="Search users" value={search} onChange={e => setSearch(e.target.value)} autoComplete="new-password" placeholder="Search users…"
autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false}
style={{width:'100%',padding:'8px 10px 8px 32px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:14,boxSizing:'border-box'}} /> style={{width:'100%',padding:'8px 10px 8px 32px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:14,boxSizing:'border-box'}} />
</div> </div>
@@ -142,7 +142,7 @@ function MultiGroupDmsScreen({ userGroups, onBack }) {
right={<button onClick={()=>setCreating(v=>!v)} style={{ background:'none',border:'none',cursor:'pointer',color:'var(--primary)',fontSize:24,lineHeight:1,padding:0 }}>+</button>} /> right={<button onClick={()=>setCreating(v=>!v)} style={{ background:'none',border:'none',cursor:'pointer',color:'var(--primary)',fontSize:24,lineHeight:1,padding:0 }}>+</button>} />
{creating && ( {creating && (
<div style={{ padding:16,background:'var(--surface)',borderBottom:'1px solid var(--border)' }}> <div style={{ padding:16,background:'var(--surface)',borderBottom:'1px solid var(--border)' }}>
<input autoFocus value={newName} onChange={e = /> setNewName(e.target.value)} autoComplete="new-password" placeholder="DM name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ width:'100%',padding:'9px 12px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:15,marginBottom:10,boxSizing:'border-box' }}/> <input autoFocus value={newName} onChange={e => setNewName(e.target.value)} autoComplete="new-password" placeholder="DM name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ width:'100%',padding:'9px 12px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:15,marginBottom:10,boxSizing:'border-box' }}/>
<div style={{ fontSize:12,color:'var(--text-tertiary)',marginBottom:6 }}>Select groups (min 2):</div> <div style={{ fontSize:12,color:'var(--text-tertiary)',marginBottom:6 }}>Select groups (min 2):</div>
{userGroups.map(g=>( {userGroups.map(g=>(
<label key={g.id} style={{ display:'flex',alignItems:'center',gap:10,padding:'8px 0',borderBottom:'1px solid var(--border)',cursor:'pointer' }}> <label key={g.id} style={{ display:'flex',alignItems:'center',gap:10,padding:'8px 0',borderBottom:'1px solid var(--border)',cursor:'pointer' }}>
@@ -236,7 +236,7 @@ export default function MobileGroupManager({ onClose }) {
<> <>
{creating && ( {creating && (
<div style={{ padding:'12px 16px',background:'var(--surface)',borderBottom:'1px solid var(--border)',display:'flex',gap:10 }}> <div style={{ padding:'12px 16px',background:'var(--surface)',borderBottom:'1px solid var(--border)',display:'flex',gap:10 }}>
<input autoFocus value={newName} onChange={e = /> setNewName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&createGroup()} placeholder="Group name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ flex:1,padding:'8px 12px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:15 }}/> <input autoFocus value={newName} onChange={e => setNewName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&createGroup()} placeholder="Group name…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ flex:1,padding:'8px 12px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:15 }}/>
<button onClick={createGroup} disabled={saving||!newName.trim()} style={{ padding:'8px 16px',background:'var(--primary)',color:'white',border:'none',borderRadius:'var(--radius)',fontSize:14,fontWeight:600,cursor:'pointer' }}>{saving?'…':'Create'}</button> <button onClick={createGroup} disabled={saving||!newName.trim()} style={{ padding:'8px 16px',background:'var(--primary)',color:'white',border:'none',borderRadius:'var(--radius)',fontSize:14,fontWeight:600,cursor:'pointer' }}>{saving?'…':'Create'}</button>
<button onClick={()=>{setCreating(false);setNewName('');}} style={{ padding:'8px',background:'none',border:'none',cursor:'pointer',color:'var(--text-secondary)',fontSize:18 }}></button> <button onClick={()=>{setCreating(false);setNewName('');}} style={{ padding:'8px',background:'none',border:'none',cursor:'pointer',color:'var(--text-secondary)',fontSize:18 }}></button>
</div> </div>

View File

@@ -100,7 +100,7 @@ export default function NewChatModal({ onClose, onCreated }) {
<input <input
className="input" className="input"
value={name} value={name}
onChange={e = /> setName(e.target.value)} autoComplete="new-password" placeholder={namePlaceholder} onChange={e => setName(e.target.value)} autoComplete="new-password" placeholder={namePlaceholder}
autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} /> autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} />
</div> </div>
)} )}
@@ -120,7 +120,7 @@ export default function NewChatModal({ onClose, onCreated }) {
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>
{isDirect ? 'Direct Message with' : 'Add Members'} {isDirect ? 'Direct Message with' : 'Add Members'}
</label> </label>
<input className="input" placeholder="Search users..." autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={search} onChange={e = /> setSearch(e.target.value)} /> <input className="input" placeholder="Search users..." autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={search} onChange={e => setSearch(e.target.value)} />
</div> </div>
{selected.length > 0 && ( {selected.length > 0 && (

View File

@@ -107,7 +107,7 @@ export default function ProfileModal({ onClose }) {
<input <input
className="input flex-1" className="input flex-1"
value={displayName} value={displayName}
onChange={async e = /> { onChange={async e => {
const val = e.target.value; const val = e.target.value;
setDisplayName(val); setDisplayName(val);
setDisplayNameWarning(''); setDisplayNameWarning('');
@@ -167,15 +167,15 @@ export default function ProfileModal({ onClose }) {
<div className="flex-col gap-3"> <div className="flex-col gap-3">
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Current Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Current Password</label>
<input className="input" type="password" value={currentPw} onChange={e = /> setCurrentPw(e.target.value)} autoComplete="new-password" /> <input className="input" type="password" value={currentPw} onChange={e => setCurrentPw(e.target.value)} autoComplete="new-password" />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>New Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>New Password</label>
<input className="input" type="password" value={newPw} onChange={e = /> setNewPw(e.target.value)} autoComplete="new-password" /> <input className="input" type="password" value={newPw} onChange={e => setNewPw(e.target.value)} autoComplete="new-password" />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Confirm New Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Confirm New Password</label>
<input className="input" type="password" value={confirmPw} onChange={e = /> setConfirmPw(e.target.value)} autoComplete="new-password" /> <input className="input" type="password" value={confirmPw} onChange={e => setConfirmPw(e.target.value)} autoComplete="new-password" />
</div> </div>
<button className="btn btn-primary" onClick={handleChangePassword} disabled={loading || !currentPw || !newPw}> <button className="btn btn-primary" onClick={handleChangePassword} disabled={loading || !currentPw || !newPw}>
{loading ? 'Changing...' : 'Change Password'} {loading ? 'Changing...' : 'Change Password'}

View File

@@ -101,11 +101,11 @@ function EventTypePopup({ userGroups, onSave, onClose, editing = null }) {
<div style={{ position:'absolute', top:'100%', left:0, zIndex:200, background:'var(--surface)', border:'1px solid var(--border)', borderRadius:'var(--radius)', padding:16, width:280, boxShadow:'0 4px 20px rgba(0,0,0,0.15)' }}> <div style={{ position:'absolute', top:'100%', left:0, zIndex:200, background:'var(--surface)', border:'1px solid var(--border)', borderRadius:'var(--radius)', padding:16, width:280, boxShadow:'0 4px 20px rgba(0,0,0,0.15)' }}>
<div style={{ marginBottom:10 }}> <div style={{ marginBottom:10 }}>
<label className="settings-section-label">Type Name</label> <label className="settings-section-label">Type Name</label>
<input className="input" value={name} onChange={e = /> setName(e.target.value)} autoComplete="new-password" style={{ marginTop:4 }} autoFocus /> <input className="input" value={name} onChange={e => setName(e.target.value)} autoComplete="new-password" style={{ marginTop:4 }} autoFocus />
</div> </div>
<div style={{ marginBottom:10 }}> <div style={{ marginBottom:10 }}>
<label className="settings-section-label">Colour</label> <label className="settings-section-label">Colour</label>
<input type="color" value={colour} onChange={e= />setColour(e.target.value)} style={{ marginTop:4, width:'100%', height:32, padding:2, borderRadius:4, border:'1px solid var(--border)' }} /> <input type="color" value={colour} onChange={e => setColour(e.target.value)} style={{ marginTop:4, width:'100%', height:32, padding:2, borderRadius:4, border:'1px solid var(--border)' }} />
</div> </div>
<div style={{ marginBottom:10 }}> <div style={{ marginBottom:10 }}>
<label className="settings-section-label">Default Group</label> <label className="settings-section-label">Default Group</label>
@@ -195,16 +195,16 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
return ( return (
<div style={{ display:'flex', flexDirection:'column', gap:0 }}> <div style={{ display:'flex', flexDirection:'column', gap:0 }}>
{/* Title */} {/* Title */}
<input className="input" placeholder="Add title" value={title} onChange={e = /> setTitle(e.target.value)} autoComplete="new-password" style={{ fontSize:18, fontWeight:600, marginBottom:16, border:'none', borderBottom:'2px solid var(--border)', borderRadius:0, padding:'4px 0' }} /> <input className="input" placeholder="Add title" value={title} onChange={e => setTitle(e.target.value)} autoComplete="new-password" style={{ fontSize:18, fontWeight:600, marginBottom:16, border:'none', borderBottom:'2px solid var(--border)', borderRadius:0, padding:'4px 0' }} />
{/* Date/Time */} {/* Date/Time */}
<Row label=""> <Row label="">
<div style={{ display:'flex', flexWrap:'wrap', gap:8, alignItems:'center' }}> <div style={{ display:'flex', flexWrap:'wrap', gap:8, alignItems:'center' }}>
<input type="date" className="input" value={startDate} onChange={e= />setStartDate(e.target.value)} style={{ width:150 }} /> <input type="date" className="input" value={startDate} onChange={e => setStartDate(e.target.value)} style={{ width:150 }} />
{!allDay && <input type="time" className="input" value={startTime} onChange={e= />setStartTime(e.target.value)} style={{ width:120 }} />} {!allDay && <input type="time" className="input" value={startTime} onChange={e => setStartTime(e.target.value)} style={{ width:120 }} />}
<span style={{ color:'var(--text-tertiary)', fontSize:13 }}>to</span> <span style={{ color:'var(--text-tertiary)', fontSize:13 }}>to</span>
{!allDay && <input type="time" className="input" value={endTime} onChange={e= />setEndTime(e.target.value)} style={{ width:120 }} />} {!allDay && <input type="time" className="input" value={endTime} onChange={e => setEndTime(e.target.value)} style={{ width:120 }} />}
<input type="date" className="input" value={endDate} onChange={e= />setEndDate(e.target.value)} style={{ width:150 }} /> <input type="date" className="input" value={endDate} onChange={e => setEndDate(e.target.value)} style={{ width:150 }} />
</div> </div>
<label style={{ display:'flex', alignItems:'center', gap:8, marginTop:8, fontSize:13, cursor:'pointer' }}> <label style={{ display:'flex', alignItems:'center', gap:8, marginTop:8, fontSize:13, cursor:'pointer' }}>
<input type="checkbox" checked={allDay} onChange={e=>setAllDay(e.target.checked)} /> All day <input type="checkbox" checked={allDay} onChange={e=>setAllDay(e.target.checked)} /> All day
@@ -264,7 +264,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
{/* Location */} {/* Location */}
<Row label="Location"> <Row label="Location">
<input className="input" placeholder="Add location" value={location} onChange={e = /> setLocation(e.target.value)} autoComplete="new-password" /> <input className="input" placeholder="Add location" value={location} onChange={e => setLocation(e.target.value)} autoComplete="new-password" />
</Row> </Row>
{/* Description */} {/* Description */}

View File

@@ -162,7 +162,7 @@ function MobileScheduleFilter({ selected, onMonthChange, view, eventTypes, filte
<div style={{padding:'8px 12px 12px',borderTop:'1px solid var(--border)'}}> <div style={{padding:'8px 12px 12px',borderTop:'1px solid var(--border)'}}>
<div style={{position:'relative',marginBottom:8}}> <div style={{position:'relative',marginBottom:8}}>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--text-tertiary)" strokeWidth="2" style={{position:'absolute',left:9,top:'50%',transform:'translateY(-50%)',pointerEvents:'none'}}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--text-tertiary)" strokeWidth="2" style={{position:'absolute',left:9,top:'50%',transform:'translateY(-50%)',pointerEvents:'none'}}><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input value={filterKeyword} onChange={e = /> onFilterKeyword(e.target.value)} autoComplete="new-password" onFocus={onInputFocus} onBlur={onInputBlur} <input value={filterKeyword} onChange={e => onFilterKeyword(e.target.value)} autoComplete="new-password" onFocus={onInputFocus} onBlur={onInputBlur}
placeholder="Search events…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} placeholder="Search events…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false}
style={{width:'100%',padding:'7px 8px 7px 28px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:13,boxSizing:'border-box'}}/> style={{width:'100%',padding:'7px 8px 7px 28px',border:'1px solid var(--border)',borderRadius:'var(--radius)',background:'var(--background)',color:'var(--text-primary)',fontSize:13,boxSizing:'border-box'}}/>
</div> </div>
@@ -201,8 +201,8 @@ function EventTypePopup({ userGroups, onSave, onClose, editing=null }) {
}; };
return ( return (
<div style={{position:'absolute',top:'100%',left:0,zIndex:300,background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'var(--radius)',padding:16,width:270,boxShadow:'0 4px 20px rgba(0,0,0,0.2)'}}> <div style={{position:'absolute',top:'100%',left:0,zIndex:300,background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'var(--radius)',padding:16,width:270,boxShadow:'0 4px 20px rgba(0,0,0,0.2)'}}>
<div style={{marginBottom:8}}><label className="settings-section-label">Name</label><input className="input" value={name} onChange={e = /> setName(e.target.value)} autoComplete="new-password" style={{marginTop:4}} autoFocus/></div> <div style={{marginBottom:8}}><label className="settings-section-label">Name</label><input className="input" value={name} onChange={e => setName(e.target.value)} autoComplete="new-password" style={{marginTop:4}} autoFocus/></div>
<div style={{marginBottom:8}}><label className="settings-section-label">Colour</label><input type="color" value={colour} onChange={e= />setColour(e.target.value)} style={{marginTop:4,width:'100%',height:32,padding:2,borderRadius:4,border:'1px solid var(--border)'}}/></div> <div style={{marginBottom:8}}><label className="settings-section-label">Colour</label><input type="color" value={colour} onChange={e => setColour(e.target.value)} style={{marginTop:4,width:'100%',height:32,padding:2,borderRadius:4,border:'1px solid var(--border)'}}/></div>
<div style={{marginBottom:8}}><label className="settings-section-label">Default Group</label><select className="input" value={groupId} onChange={e=>setGroupId(e.target.value)} style={{marginTop:4}}><option value="">None</option>{userGroups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select></div> <div style={{marginBottom:8}}><label className="settings-section-label">Default Group</label><select className="input" value={groupId} onChange={e=>setGroupId(e.target.value)} style={{marginTop:4}}><option value="">None</option>{userGroups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select></div>
<div style={{marginBottom:12}}> <div style={{marginBottom:12}}>
<label style={{display:'flex',alignItems:'center',gap:8,fontSize:13,cursor:'pointer'}}><input type="checkbox" checked={useDur} onChange={e=>setUseDur(e.target.checked)}/> Set default duration</label> <label style={{display:'flex',alignItems:'center',gap:8,fontSize:13,cursor:'pointer'}}><input type="checkbox" checked={useDur} onChange={e=>setUseDur(e.target.checked)}/> Set default duration</label>
@@ -265,7 +265,7 @@ function CustomRecurrenceFields({ rule, onChange }) {
<div style={{border:'1px solid var(--border)',borderRadius:'var(--radius)',padding:12,display:'flex',flexDirection:'column',gap:10}}> <div style={{border:'1px solid var(--border)',borderRadius:'var(--radius)',padding:12,display:'flex',flexDirection:'column',gap:10}}>
<div style={{display:'flex',alignItems:'center',gap:8,fontSize:13}}> <div style={{display:'flex',alignItems:'center',gap:8,fontSize:13}}>
<span style={{color:'var(--text-tertiary)'}}>Every</span> <span style={{color:'var(--text-tertiary)'}}>Every</span>
<input type="number" className="input" min={1} max={99} value={rule.interval||1} onChange={e= />upd('interval',Math.max(1,parseInt(e.target.value)||1))} style={{width:60,textAlign:'center'}}/> <input type="number" className="input" min={1} max={99} value={rule.interval||1} onChange={e => upd('interval',Math.max(1,parseInt(e.target.value)||1))} style={{width:60,textAlign:'center'}}/>
<select className="input" value={rule.unit||'week'} onChange={e=>upd('unit',e.target.value)} style={{flex:1}}> <select className="input" value={rule.unit||'week'} onChange={e=>upd('unit',e.target.value)} style={{flex:1}}>
{['day','week','month','year'].map(u=><option key={u} value={u}>{u}{(rule.interval||1)>1?'s':''}</option>)} {['day','week','month','year'].map(u=><option key={u} value={u}>{u}{(rule.interval||1)>1?'s':''}</option>)}
</select> </select>
@@ -287,8 +287,8 @@ function CustomRecurrenceFields({ rule, onChange }) {
<label key={val} style={{display:'flex',alignItems:'center',gap:10,marginBottom:6,fontSize:13,cursor:'pointer'}}> <label key={val} style={{display:'flex',alignItems:'center',gap:10,marginBottom:6,fontSize:13,cursor:'pointer'}}>
<input type="radio" name="recur_ends" checked={(rule.ends||'never')===val} onChange={()=>upd('ends',val)}/> <input type="radio" name="recur_ends" checked={(rule.ends||'never')===val} onChange={()=>upd('ends',val)}/>
{lbl} {lbl}
{val==='on' && (rule.ends||'never')==='on' && <input type="date" className="input" value={rule.endDate||''} onChange={e= />upd('endDate',e.target.value)} style={{marginLeft:8,flex:1}}/>} {val==='on' && (rule.ends||'never')==='on' && <input type="date" className="input" value={rule.endDate||''} onChange={e => upd('endDate',e.target.value)} style={{marginLeft:8,flex:1}}/>}
{val==='after' && (rule.ends||'never')==='after' && <><input type="number" className="input" min={1} max={999} value={rule.endCount||13} onChange={e= />upd('endCount',parseInt(e.target.value)||1)} style={{width:64,textAlign:'center',marginLeft:8}}/><span style={{color:'var(--text-tertiary)'}}>occurrences</span></>} {val==='after' && (rule.ends||'never')==='after' && <><input type="number" className="input" min={1} max={999} value={rule.endCount||13} onChange={e => upd('endCount',parseInt(e.target.value)||1)} style={{width:64,textAlign:'center',marginLeft:8}}/><span style={{color:'var(--text-tertiary)'}}>occurrences</span></>}
</label> </label>
))} ))}
</div> </div>
@@ -412,7 +412,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
<div style={{minWidth:500}} onKeyDown={e=>{if(e.key==='Enter'&&e.target.tagName!=='TEXTAREA') e.preventDefault();}}> <div style={{minWidth:500}} onKeyDown={e=>{if(e.key==='Enter'&&e.target.tagName!=='TEXTAREA') e.preventDefault();}}>
{/* Title */} {/* Title */}
<div style={{marginBottom:20}}> <div style={{marginBottom:20}}>
<input className="input" placeholder="Add title" value={title} onChange={e = /> setTitle(e.target.value)} autoComplete="new-password" style={{fontSize:20,fontWeight:700,border:'none',borderBottom:'2px solid var(--border)',borderRadius:0,padding:'4px 0',background:'transparent',width:'100%'}}/> <input className="input" placeholder="Add title" value={title} onChange={e => setTitle(e.target.value)} autoComplete="new-password" style={{fontSize:20,fontWeight:700,border:'none',borderBottom:'2px solid var(--border)',borderRadius:0,padding:'4px 0',background:'transparent',width:'100%'}}/>
</div> </div>
{/* Event Type */} {/* Event Type */}
@@ -431,7 +431,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
<FormRow label="Date & Time"> <FormRow label="Date & Time">
<div style={{display:'flex',flexDirection:'column',gap:8}}> <div style={{display:'flex',flexDirection:'column',gap:8}}>
<div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'nowrap'}}> <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'nowrap'}}>
<input type="date" className="input" value={sd} onChange={e= />setSd(e.target.value)} style={{width:150,flexShrink:0}}/> <input type="date" className="input" value={sd} onChange={e => setSd(e.target.value)} style={{width:150,flexShrink:0}}/>
{!allDay&&( {!allDay&&(
<> <>
<select className="input" value={st} onChange={e=>setSt(e.target.value)} style={{width:120,flexShrink:0}}> <select className="input" value={st} onChange={e=>setSt(e.target.value)} style={{width:120,flexShrink:0}}>
@@ -441,7 +441,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
<select className="input" value={et} onChange={e=>{setEt(e.target.value);userSetEndTime.current=true;}} style={{width:120,flexShrink:0}}> <select className="input" value={et} onChange={e=>{setEt(e.target.value);userSetEndTime.current=true;}} style={{width:120,flexShrink:0}}>
{TIME_SLOTS.map(s=><option key={s.value} value={s.value}>{s.label}</option>)} {TIME_SLOTS.map(s=><option key={s.value} value={s.value}>{s.label}</option>)}
</select> </select>
<input type="date" className="input" value={ed} onChange={e= />{setEd(e.target.value);userSetEndTime.current=true;}} style={{width:150,flexShrink:0}}/> <input type="date" className="input" value={ed} onChange={e => {setEd(e.target.value);userSetEndTime.current=true;}} style={{width:150,flexShrink:0}}/>
</> </>
)} )}
</div> </div>
@@ -499,7 +499,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
{/* Location */} {/* Location */}
<FormRow label="Location"> <FormRow label="Location">
<input className="input" placeholder="Add location" value={loc} onChange={e = /> setLoc(e.target.value)} autoComplete="new-password" /> <input className="input" placeholder="Add location" value={loc} onChange={e => setLoc(e.target.value)} autoComplete="new-password" />
</FormRow> </FormRow>
{/* Description */} {/* Description */}
@@ -672,7 +672,7 @@ function EventTypesPanel({ eventTypes, userGroups, onUpdated, isMobile=false })
<span style={{fontWeight:700,fontSize:16}}>{sheetMode==='create'?'New Event Type':'Edit Event Type'}</span> <span style={{fontWeight:700,fontSize:16}}>{sheetMode==='create'?'New Event Type':'Edit Event Type'}</span>
<button onClick={closeSheet} style={{background:'none',border:'none',cursor:'pointer',color:'var(--text-secondary)',fontSize:20,lineHeight:1}}></button> <button onClick={closeSheet} style={{background:'none',border:'none',cursor:'pointer',color:'var(--text-secondary)',fontSize:20,lineHeight:1}}></button>
</div> </div>
<input autoFocus value={sheetName} onChange={e = /> setSheetName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&saveSheet()} placeholder="Type name" <input autoFocus value={sheetName} onChange={e => setSheetName(e.target.value)} autoComplete="new-password" onKeyDown={e=>e.key==='Enter'&&saveSheet()} placeholder="Type name…"
style={{width:'100%',padding:'12px 14px',border:'1px solid var(--border)',borderRadius:'var(--radius)',fontSize:16,marginBottom:12,boxSizing:'border-box',background:'var(--background)',color:'var(--text-primary)'}}/> style={{width:'100%',padding:'12px 14px',border:'1px solid var(--border)',borderRadius:'var(--radius)',fontSize:16,marginBottom:12,boxSizing:'border-box',background:'var(--background)',color:'var(--text-primary)'}}/>
<div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}> <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}>
<label style={{fontSize:14,color:'var(--text-tertiary)',flexShrink:0}}>Colour</label> <label style={{fontSize:14,color:'var(--text-tertiary)',flexShrink:0}}>Colour</label>
@@ -1247,7 +1247,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
className="input" className="input"
placeholder={`Keyword… (space = OR, "phrase")`} placeholder={`Keyword… (space = OR, "phrase")`}
value={filterKeyword} value={filterKeyword}
onChange={e = /> setFilterKeyword(e.target.value)} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} onChange={e => setFilterKeyword(e.target.value)} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false}
style={{ marginBottom:8, fontSize:13 }} /> style={{ marginBottom:8, fontSize:13 }} />
<select <select
className="input" className="input"

View File

@@ -167,7 +167,7 @@ function RegistrationTab({ onFeaturesChanged }) {
<div className="settings-section-label">Registration Code</div> <div className="settings-section-label">Registration Code</div>
<div style={{ display: 'flex', gap: 8, marginTop: 6 }}> <div style={{ display: 'flex', gap: 8, marginTop: 6 }}>
<input className="input flex-1" placeholder="Enter registration code" value={regCode} <input className="input flex-1" placeholder="Enter registration code" value={regCode}
onChange={e = /> setRegCode(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleRegister()} autoComplete="new-password" /> onChange={e => setRegCode(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleRegister()} autoComplete="new-password" />
<button className="btn btn-primary btn-sm" onClick={handleRegister} disabled={regLoading}> <button className="btn btn-primary btn-sm" onClick={handleRegister} disabled={regLoading}>
{regLoading ? '…' : 'Register'} {regLoading ? '…' : 'Register'}
</button> </button>

View File

@@ -100,7 +100,7 @@ export default function SupportModal({ onClose }) {
className="input" className="input"
placeholder="Jane Smith" placeholder="Jane Smith"
value={name} value={name}
onChange={e = /> setName(e.target.value)} autoComplete="new-password" maxLength={100} /> onChange={e => setName(e.target.value)} autoComplete="new-password" maxLength={100} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
@@ -110,7 +110,7 @@ export default function SupportModal({ onClose }) {
type="email" type="email"
placeholder="jane@example.com" placeholder="jane@example.com"
value={email} value={email}
onChange={e = /> setEmail(e.target.value)} autoComplete="new-password" maxLength={200} /> onChange={e => setEmail(e.target.value)} autoComplete="new-password" maxLength={200} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
@@ -148,7 +148,7 @@ export default function SupportModal({ onClose }) {
type="number" type="number"
placeholder="Answer" placeholder="Answer"
value={captchaAnswer} value={captchaAnswer}
onChange={e = /> setCaptchaAnswer(e.target.value)} onChange={e => setCaptchaAnswer(e.target.value)}
style={{ width: 90 }} style={{ width: 90 }}
min={0} min={0}
max={999} /> max={999} />

View File

@@ -125,7 +125,7 @@ function UserRow({ u, onUpdated }) {
className="input" className="input"
style={{ flex: 1, fontSize: 13, padding: '5px 8px' }} style={{ flex: 1, fontSize: 13, padding: '5px 8px' }}
value={nameVal} value={nameVal}
onChange={e = /> setNameVal(e.target.value)} autoComplete="new-password" onKeyDown={e => { if (e.key === 'Enter') handleSaveName(); if (e.key === 'Escape') { setEditName(false); setNameVal(u.name); } }} /> onChange={e => setNameVal(e.target.value)} autoComplete="new-password" onKeyDown={e => { if (e.key === 'Enter') handleSaveName(); if (e.key === 'Escape') { setEditName(false); setNameVal(u.name); } }} />
<button className="btn btn-primary btn-sm" onClick={handleSaveName}>Save</button> <button className="btn btn-primary btn-sm" onClick={handleSaveName}>Save</button>
<button className="btn btn-secondary btn-sm" onClick={() => { setEditName(false); setNameVal(u.name); }}></button> <button className="btn btn-secondary btn-sm" onClick={() => { setEditName(false); setNameVal(u.name); }}></button>
</div> </div>
@@ -164,7 +164,7 @@ function UserRow({ u, onUpdated }) {
type="text" type="text"
placeholder="New password (min 6)" placeholder="New password (min 6)"
value={resetPw} value={resetPw}
onChange={e = /> setResetPw(e.target.value)} autoComplete="new-password" onKeyDown={e => { if (e.key === 'Enter') handleResetPw(); if (e.key === 'Escape') { setShowReset(false); setResetPw(''); } }} /> onChange={e => setResetPw(e.target.value)} autoComplete="new-password" onKeyDown={e => { if (e.key === 'Enter') handleResetPw(); if (e.key === 'Escape') { setShowReset(false); setResetPw(''); } }} />
<button className="btn btn-primary btn-sm" onClick={handleResetPw}>Set</button> <button className="btn btn-primary btn-sm" onClick={handleResetPw}>Set</button>
<button className="btn btn-secondary btn-sm" onClick={() => { setShowReset(false); setResetPw(''); }}></button> <button className="btn btn-secondary btn-sm" onClick={() => { setShowReset(false); setResetPw(''); }}></button>
</div> </div>
@@ -306,7 +306,7 @@ export default function UserManagerModal({ onClose }) {
{/* Users list — accordion */} {/* Users list — accordion */}
{tab === 'users' && ( {tab === 'users' && (
<> <>
<input className="input" style={{ marginBottom: 12 }} placeholder="Search users…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={search} onChange={e = /> setSearch(e.target.value)} /> <input className="input" style={{ marginBottom: 12 }} placeholder="Search users…" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={search} onChange={e => setSearch(e.target.value)} />
{loading ? ( {loading ? (
<div className="flex justify-center" style={{ padding: 40 }}><div className="spinner" /></div> <div className="flex justify-center" style={{ padding: 40 }}><div className="spinner" /></div>
) : loadError ? ( ) : loadError ? (
@@ -330,17 +330,17 @@ export default function UserManagerModal({ onClose }) {
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Full Name <span style={{ fontWeight: 400, color: 'var(--text-tertiary)' }}>(First Last)</span></label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Full Name <span style={{ fontWeight: 400, color: 'var(--text-tertiary)' }}>(First Last)</span></label>
<input className="input" placeholder="Jane Smith" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} value={form.name} onChange={e = /> setForm(p => ({ ...p, name: e.target.value }))} /> <input className="input" placeholder="Jane Smith" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} value={form.name} onChange={e => setForm(p => ({ ...p, name: e.target.value }))} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Email</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Email</label>
<input className="input" type="email" placeholder="jane@example.com" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={form.email} onChange={e = /> setForm(p => ({ ...p, email: e.target.value }))} /> <input className="input" type="email" placeholder="jane@example.com" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={form.email} onChange={e => setForm(p => ({ ...p, email: e.target.value }))} />
</div> </div>
</div> </div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 12 }}> <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 12 }}>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Temp Password <span style={{ fontWeight: 400, color: 'var(--text-tertiary)' }}>(blank = {userPass || 'USER_PASS'})</span></label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Temp Password <span style={{ fontWeight: 400, color: 'var(--text-tertiary)' }}>(blank = {userPass || 'USER_PASS'})</span></label>
<input className="input" type="text" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={form.password} onChange={e = /> setForm(p => ({ ...p, password: e.target.value }))} /> <input className="input" type="text" autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={form.password} onChange={e => setForm(p => ({ ...p, password: e.target.value }))} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Role</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Role</label>

View File

@@ -40,15 +40,15 @@ export default function ChangePassword() {
<form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}> <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Current Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Current Password</label>
<input className="input" type="password" value={current} onChange={e = /> setCurrent(e.target.value)} autoComplete="new-password" required /> <input className="input" type="password" value={current} onChange={e => setCurrent(e.target.value)} autoComplete="new-password" required />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>New Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>New Password</label>
<input className="input" type="password" value={next} onChange={e = /> setNext(e.target.value)} autoComplete="new-password" required /> <input className="input" type="password" value={next} onChange={e => setNext(e.target.value)} autoComplete="new-password" required />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Confirm New Password</label> <label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>Confirm New Password</label>
<input className="input" type="password" value={confirm} onChange={e = /> setConfirm(e.target.value)} autoComplete="new-password" required /> <input className="input" type="password" value={confirm} onChange={e => setConfirm(e.target.value)} autoComplete="new-password" required />
</div> </div>
<button className="btn btn-primary" type="submit" disabled={loading}> <button className="btn btn-primary" type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Set New Password'} {loading ? 'Saving...' : 'Set New Password'}

View File

@@ -24,7 +24,7 @@ function UserCheckList({ allUsers, selectedIds, onChange, onIF, onIB }) {
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()));
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} />
<div style={{ maxHeight:220, overflowY:'auto', border:'1px solid var(--border)', borderRadius:'var(--radius)' }}> <div style={{ maxHeight:220, overflowY:'auto', border:'1px solid var(--border)', borderRadius:'var(--radius)' }}>
{filtered.map(u => ( {filtered.map(u => (
<label key={u.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 12px', borderBottom:'1px solid var(--border)', cursor:'pointer' }}> <label key={u.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 12px', borderBottom:'1px solid var(--border)', cursor:'pointer' }}>
@@ -138,7 +138,7 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
<div style={{ display:'flex', flexDirection:'column', gap:18, maxWidth: isMobile ? '100%' : 520 }}> <div style={{ display:'flex', flexDirection:'column', gap:18, maxWidth: isMobile ? '100%' : 520 }}>
<div> <div>
<label className="settings-section-label">Group Name</label> <label className="settings-section-label">Group Name</label>
<input className="input" value={editName} onChange={e = /> setEditName(e.target.value)} autoComplete="new-password" placeholder="e.g. Coaches" style={{ marginTop:6 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> <input className="input" value={editName} onChange={e => setEditName(e.target.value)} autoComplete="new-password" placeholder="e.g. Coaches" style={{ marginTop:6 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
{isCreating && <p style={{ fontSize:12, color:'var(--text-tertiary)', marginTop:5 }}>A matching Direct Message group will be created automatically.</p>} {isCreating && <p style={{ fontSize:12, color:'var(--text-tertiary)', marginTop:5 }}>A matching Direct Message group will be created automatically.</p>}
</div> </div>
<div> <div>
@@ -246,7 +246,7 @@ function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey, isMobile = fa
<div style={{ display:'flex', flexDirection:'column', gap:18, maxWidth: isMobile ? '100%' : 520 }}> <div style={{ display:'flex', flexDirection:'column', gap:18, maxWidth: isMobile ? '100%' : 520 }}>
<div> <div>
<label className="settings-section-label">DM Name</label> <label className="settings-section-label">DM Name</label>
<input className="input" value={dmName} onChange={e = /> setDmName(e.target.value)} autoComplete="new-password" placeholder="e.g. Coaches + Players" style={{ marginTop:6 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> <input className="input" value={dmName} onChange={e => setDmName(e.target.value)} autoComplete="new-password" placeholder="e.g. Coaches + Players" style={{ marginTop:6 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
</div> </div>
<div> <div>
<label className="settings-section-label">Member Groups</label> <label className="settings-section-label">Member Groups</label>
@@ -432,7 +432,7 @@ function U2URestrictionsTab({ allUserGroups, isMobile = false, onIF, onIB }) {
Allowed Groups <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>({otherGroups.length - blockedIds.size} of {otherGroups.length} allowed)</span> Allowed Groups <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>({otherGroups.length - blockedIds.size} of {otherGroups.length} allowed)</span>
</label> </label>
<input className="input" placeholder="Search groups…" value={search} <input className="input" placeholder="Search groups…" value={search}
onChange={e = /> setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }} onChange={e => setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }}
autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
</div> </div>

View File

@@ -92,7 +92,7 @@ function Input({ label, value, onChange, placeholder, type = 'text', required, h
</label> </label>
)} )}
<input <input
type={type} value={value} onChange={e = /> onChange(e.target.value)} type={type} value={value} onChange={e => onChange(e.target.value)}
placeholder={placeholder} required={required} placeholder={placeholder} required={required}
autoComplete={autoComplete || 'new-password'} autoCorrect="off" spellCheck={false} autoComplete={autoComplete || 'new-password'} autoCorrect="off" spellCheck={false}
style={{ padding: '8px 10px', border: '1px solid #e0e0e0', borderRadius: 6, style={{ padding: '8px 10px', border: '1px solid #e0e0e0', borderRadius: 6,
@@ -411,7 +411,7 @@ function KeyEntry({ onSubmit }) {
{error && <div style={{ padding: '8px 12px', background: '#fce8e6', color: '#d93025', {error && <div style={{ padding: '8px 12px', background: '#fce8e6', color: '#d93025',
borderRadius: 6, fontSize: 13, marginBottom: 16 }}>{error}</div>} borderRadius: 6, fontSize: 13, marginBottom: 16 }}>{error}</div>}
<input <input
type="password" value={key} onChange={e = /> setKey(e.target.value)} type="password" value={key} onChange={e => setKey(e.target.value)}
onKeyDown={e => e.key === 'Enter' && handle()} onKeyDown={e => e.key === 'Enter' && handle()}
placeholder="Host admin key" autoFocus placeholder="Host admin key" autoFocus
style={{ width: '100%', padding: '10px 12px', border: '1px solid #e0e0e0', borderRadius: 6, style={{ width: '100%', padding: '10px 12px', border: '1px solid #e0e0e0', borderRadius: 6,
@@ -533,7 +533,7 @@ export default function HostAdmin() {
padding: '16px 20px', borderBottom: '1px solid #e0e0e0', gap: 12, flexWrap: 'wrap' }}> padding: '16px 20px', borderBottom: '1px solid #e0e0e0', gap: 12, flexWrap: 'wrap' }}>
<div style={{ fontWeight: 700, fontSize: 15 }}>Tenants</div> <div style={{ fontWeight: 700, fontSize: 15 }}>Tenants</div>
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flex: 1, justifyContent: 'flex-end', flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: 8, alignItems: 'center', flex: 1, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
<input value={search} onChange={e = /> setSearch(e.target.value)} <input value={search} onChange={e => setSearch(e.target.value)}
placeholder="Search tenants…" autoComplete="off" placeholder="Search tenants…" autoComplete="off"
style={{ padding: '7px 10px', border: '1px solid #e0e0e0', borderRadius: 6, style={{ padding: '7px 10px', border: '1px solid #e0e0e0', borderRadius: 6,
fontSize: 13, outline: 'none', width: 200 }} /> fontSize: 13, outline: 'none', width: 200 }} />

View File

@@ -92,11 +92,11 @@ export default function Login() {
<form onSubmit={handleSubmit} className="login-form"> <form onSubmit={handleSubmit} className="login-form">
<div className="field"> <div className="field">
<label>Email</label> <label>Email</label>
<input className="input" type="email" value={email} onChange={e = /> setEmail(e.target.value)} required autoFocus placeholder="your@email.com" /> <input className="input" type="email" value={email} onChange={e => setEmail(e.target.value)} required autoFocus placeholder="your@email.com" />
</div> </div>
<div className="field"> <div className="field">
<label>Password</label> <label>Password</label>
<input className="input" type="password" value={password} onChange={e = /> setPassword(e.target.value)} required placeholder="••••••••" /> <input className="input" type="password" value={password} onChange={e => setPassword(e.target.value)} required placeholder="••••••••" />
</div> </div>
<label className="remember-me"> <label className="remember-me">

View File

@@ -106,7 +106,7 @@ function UserRow({ u, onUpdated }) {
{editName ? ( {editName ? (
<div style={{ display:'flex', gap:6, alignItems:'center' }}> <div style={{ display:'flex', gap:6, alignItems:'center' }}>
<input className="input" style={{ flex:1, fontSize:13, padding:'5px 8px' }} <input className="input" style={{ flex:1, fontSize:13, padding:'5px 8px' }}
value={nameVal} onChange={e = /> setNameVal(e.target.value)} value={nameVal} onChange={e => setNameVal(e.target.value)}
onKeyDown={e => { if(e.key==='Enter') handleSaveName(); if(e.key==='Escape'){setEditName(false);setNameVal(u.name);} }} onKeyDown={e => { if(e.key==='Enter') handleSaveName(); if(e.key==='Escape'){setEditName(false);setNameVal(u.name);} }}
autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
<button className="btn btn-primary btn-sm" onClick={handleSaveName}>Save</button> <button className="btn btn-primary btn-sm" onClick={handleSaveName}>Save</button>
@@ -132,7 +132,7 @@ function UserRow({ u, onUpdated }) {
<div style={{ display:'flex', gap:6, alignItems:'center' }}> <div style={{ display:'flex', gap:6, alignItems:'center' }}>
<input className="input" style={{ flex:1, fontSize:13, padding:'5px 8px' }} <input className="input" style={{ flex:1, fontSize:13, padding:'5px 8px' }}
type="text" placeholder="New password (min 6)" value={resetPw} type="text" placeholder="New password (min 6)" value={resetPw}
onChange={e = /> setResetPw(e.target.value)} onChange={e => setResetPw(e.target.value)}
onKeyDown={e => { if(e.key==='Enter') handleResetPw(); if(e.key==='Escape'){setShowReset(false);setResetPw('');} }} onKeyDown={e => { if(e.key==='Enter') handleResetPw(); if(e.key==='Escape'){setShowReset(false);setResetPw('');} }}
autoComplete="new-password" onFocus={onIF} onBlur={onIB} /> autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
<button className="btn btn-primary btn-sm" onClick={handleResetPw}>Set</button> <button className="btn btn-primary btn-sm" onClick={handleResetPw}>Set</button>
@@ -178,15 +178,15 @@ function CreateUserForm({ userPass, onCreated, isMobile, onIF, onIB }) {
<div style={{ display:'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap:12, marginBottom:12 }}> <div style={{ display:'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap:12, marginBottom:12 }}>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Full Name <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>(First Last)</span></label> <label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Full Name <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>(First Last)</span></label>
<input className="input" placeholder="Jane Smith" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" value={form.name} onChange={e = /> set('name')(e.target.value)} onFocus={onIF} onBlur={onIB} /> <input className="input" placeholder="Jane Smith" autoComplete="new-password" autoCorrect="off" autoCapitalize="words" value={form.name} onChange={e => set('name')(e.target.value)} onFocus={onIF} onBlur={onIB} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Email</label> <label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Email</label>
<input className="input" type="email" placeholder="jane@example.com" autoComplete="new-password" value={form.email} onChange={e = /> set('email')(e.target.value)} onFocus={onIF} onBlur={onIB} /> <input className="input" type="email" placeholder="jane@example.com" autoComplete="new-password" value={form.email} onChange={e => set('email')(e.target.value)} onFocus={onIF} onBlur={onIB} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Temp Password <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>(blank = {userPass})</span></label> <label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Temp Password <span style={{ fontWeight:400, color:'var(--text-tertiary)' }}>(blank = {userPass})</span></label>
<input className="input" type="text" autoComplete="new-password" value={form.password} onChange={e = /> set('password')(e.target.value)} onFocus={onIF} onBlur={onIB} /> <input className="input" type="text" autoComplete="new-password" value={form.password} onChange={e => set('password')(e.target.value)} onFocus={onIF} onBlur={onIB} />
</div> </div>
<div className="flex-col gap-1"> <div className="flex-col gap-1">
<label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Role</label> <label className="text-sm font-medium" style={{ color:'var(--text-secondary)' }}>Role</label>
@@ -353,7 +353,7 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
<div style={{ flex:1, overflowY:'auto', padding:16 }}> <div style={{ flex:1, overflowY:'auto', padding:16 }}>
{tab === 'users' && ( {tab === 'users' && (
<> <>
<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={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />