chrome mobile autofill bug fix
This commit is contained in:
@@ -235,7 +235,7 @@ function CustomPicker({ initial, onSet, onBack }) {
|
||||
width: 110, background: 'var(--surface)',
|
||||
color: 'var(--text-primary)',
|
||||
}}
|
||||
placeholder="#000000" autoComplete="new-password" />
|
||||
placeholder="#000000" autoComplete="off" />
|
||||
<span style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>Chosen colour</span>
|
||||
</div>
|
||||
|
||||
@@ -455,7 +455,7 @@ export default function BrandingModal({ onClose }) {
|
||||
className="input flex-1"
|
||||
value={appName}
|
||||
maxLength={16}
|
||||
onChange={e => setAppName(e.target.value)} autoComplete="new-password" onKeyDown={e => e.key === 'Enter' && handleSaveName()} />
|
||||
onChange={e => setAppName(e.target.value)} autoComplete="off" onKeyDown={e => e.key === 'Enter' && handleSaveName()} />
|
||||
<button className="btn btn-primary btn-sm" onClick={handleSaveName} disabled={loading}>{loading ? '...' : 'Save'}</button>
|
||||
</div>
|
||||
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 6 }}>
|
||||
|
||||
@@ -134,7 +134,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
{editing ? (
|
||||
<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="off" onKeyDown={e => e.key === 'Enter' && handleRename()} autoCorrect="off" autoCapitalize="off" spellCheck={false} />
|
||||
<button className="btn btn-primary btn-sm" onClick={handleRename}>Save</button>
|
||||
<button className="btn btn-secondary btn-sm" onClick={() => setEditing(false)}>Cancel</button>
|
||||
</div>
|
||||
@@ -165,7 +165,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
|
||||
<input
|
||||
className="input flex-1"
|
||||
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="off" placeholder={group.owner_name_original || group.name}
|
||||
onKeyDown={e => e.key === 'Enter' && handleCustomName()} />
|
||||
{customName.trim() !== savedCustomName ? (
|
||||
<button className="btn btn-primary btn-sm" onClick={handleCustomName} disabled={savingCustom}>
|
||||
@@ -219,7 +219,7 @@ export default function GroupInfoModal({ group, onClose, onUpdated, onBack }) {
|
||||
</div>
|
||||
{canManage && (
|
||||
<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="off" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={addSearch} onChange={e => setAddSearch(e.target.value)} />
|
||||
{addResults.length > 0 && addSearch && (
|
||||
<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 => (
|
||||
|
||||
@@ -105,7 +105,7 @@ function TimeInputMobile({ value, onChange }) {
|
||||
onFocus={() => setOpen(true)}
|
||||
onBlur={e => setTimeout(() => commit(e.target.value), 150)}
|
||||
onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); commit(inputVal); } if (e.key === 'Escape') { setInputVal(fmt12(value)); setOpen(false); } }}
|
||||
autoComplete="new-password"
|
||||
autoComplete="off"
|
||||
style={{ fontSize: 15, color: 'var(--primary)', fontWeight: 600, background: 'transparent', border: 'none', outline: 'none', cursor: 'text', width: 90 }}
|
||||
/>
|
||||
{open && (
|
||||
@@ -255,7 +255,7 @@ function RecurrenceSheet({ value, onChange, onClose }) {
|
||||
<div style={{ marginBottom:16 }}>
|
||||
<div style={{ fontSize:12,color:'var(--text-tertiary)',marginBottom:8 }}>Repeats every</div>
|
||||
<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))} autoComplete="off" 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 }}>
|
||||
{['day','week','month','year'].map(u=><option key={u} value={u}>{u}{(customRule.interval||1)>1?'s':''}</option>)}
|
||||
</select>
|
||||
@@ -282,8 +282,8 @@ function RecurrenceSheet({ value, onChange, onClose }) {
|
||||
{(customRule.ends||'never')===val&&<div style={{ width:10,height:10,borderRadius:'50%',background:'var(--primary)' }}/>}
|
||||
</div>
|
||||
<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==='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==='on'&&(customRule.ends||'never')==='on'&&<input type="date" className="input" value={customRule.endDate||''} onChange={e => upd('endDate',e.target.value)} autoComplete="off" 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)} autoComplete="off" style={{ width:64,textAlign:'center' }}/><span style={{ fontSize:13,color:'var(--text-tertiary)' }}>occurrences</span></>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -478,7 +478,7 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
|
||||
<div style={{ flex:1,overflowY:'auto' }}>
|
||||
{/* Title */}
|
||||
<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="off" placeholder="Add title" autoCorrect="off" autoCapitalize="sentences" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:22,fontWeight:700,color:'var(--text-primary)',outline:'none' }}/>
|
||||
</div>
|
||||
|
||||
{/* Event Type */}
|
||||
@@ -567,12 +567,12 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
|
||||
|
||||
{/* 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>}>
|
||||
<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="off" placeholder="Add location" autoCorrect="off" autoCapitalize="off" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:15,color:'var(--text-primary)',outline:'none' }}/>
|
||||
</MobileRow>
|
||||
|
||||
{/* Description */}
|
||||
<MobileRow icon={<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="21" y1="10" x2="3" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="21" y1="18" x2="3" y2="18"/></svg>} border={false}>
|
||||
<textarea value={description} onChange={e=>setDescription(e.target.value)} placeholder="Add description" rows={3} autoComplete="new-password" autoCorrect="off" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:15,color:'var(--text-primary)',outline:'none',resize:'none' }}/>
|
||||
<textarea value={description} onChange={e=>setDescription(e.target.value)} placeholder="Add description" rows={3} autoComplete="off" autoCorrect="off" spellCheck={false} style={{ width:'100%',border:'none',background:'transparent',fontSize:15,color:'var(--text-primary)',outline:'none',resize:'none' }}/>
|
||||
</MobileRow>
|
||||
|
||||
{/* Delete */}
|
||||
@@ -601,8 +601,8 @@ export default function MobileEventForm({ event, eventTypes, userGroups, selecte
|
||||
<input
|
||||
autoFocus
|
||||
value={newTypeName}
|
||||
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}
|
||||
onChange={e => setNewTypeName(e.target.value)} autoComplete="off" onKeyDown={e=>e.key==='Enter'&&createEventType()}
|
||||
placeholder="Type name…" 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)' }} />
|
||||
<div style={{ display:'flex',alignItems:'center',gap:12,marginBottom:16 }}>
|
||||
<label style={{ fontSize:14,color:'var(--text-tertiary)',flexShrink:0 }}>Colour</label>
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function NewChatModal({ onClose, onCreated }) {
|
||||
className="input"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)} placeholder={namePlaceholder}
|
||||
autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false} />
|
||||
autoComplete="off" autoCorrect="off" autoCapitalize="words" spellCheck={false} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -120,7 +120,7 @@ export default function NewChatModal({ onClose, onCreated }) {
|
||||
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>
|
||||
{isDirect ? 'Direct Message with' : 'Add Members'}
|
||||
</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="off" autoCorrect="off" autoCapitalize="off" spellCheck={false} value={search} onChange={e => setSearch(e.target.value)} />
|
||||
</div>
|
||||
|
||||
{selected.length > 0 && (
|
||||
|
||||
@@ -168,7 +168,7 @@ export default function ProfileModal({ onClose }) {
|
||||
}
|
||||
}}
|
||||
placeholder={user?.name}
|
||||
autoComplete="new-password" autoCorrect="off" autoCapitalize="words" spellCheck={false}
|
||||
autoComplete="off" autoCorrect="off" autoCapitalize="words" spellCheck={false}
|
||||
style={{ borderColor: displayNameWarning ? '#e53935' : undefined }} />
|
||||
{displayName !== savedDisplayName ? null : savedDisplayName ? (
|
||||
<button
|
||||
@@ -186,7 +186,7 @@ export default function ProfileModal({ onClose }) {
|
||||
</div>
|
||||
<div className="flex-col gap-1">
|
||||
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>About Me</label>
|
||||
<textarea className="input" value={aboutMe} onChange={e => setAboutMe(e.target.value)} placeholder="Tell your team about yourself..." rows={3} autoComplete="new-password" autoCorrect="off" spellCheck={false} style={{ resize: 'vertical' }} />
|
||||
<textarea className="input" value={aboutMe} onChange={e => setAboutMe(e.target.value)} placeholder="Tell your team about yourself..." rows={3} autoComplete="off" autoCorrect="off" spellCheck={false} style={{ resize: 'vertical' }} />
|
||||
</div>
|
||||
{user?.role === 'admin' && (
|
||||
<label className="flex items-center gap-2 text-sm pointer" style={{ color: 'var(--text-secondary)', userSelect: 'none' }}>
|
||||
@@ -339,7 +339,7 @@ export default function ProfileModal({ onClose }) {
|
||||
<div className="flex-col gap-3">
|
||||
<div className="flex-col gap-1">
|
||||
<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="current-password" />
|
||||
</div>
|
||||
<div className="flex-col gap-1">
|
||||
<label className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>New Password</label>
|
||||
|
||||
@@ -195,7 +195,7 @@ function TimeInput({ value, onChange, style }) {
|
||||
}}
|
||||
onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); commit(inputVal); } if (e.key === 'Escape') { setInputVal(fmt12(value)); setOpen(false); } }}
|
||||
style={{ width: '100%', cursor: 'text' }}
|
||||
autoComplete="new-password"
|
||||
autoComplete="off"
|
||||
placeholder="9:00 AM"
|
||||
/>
|
||||
{open && (
|
||||
@@ -350,8 +350,8 @@ function MobileScheduleFilter({ selected, onMonthChange, view, eventTypes, filte
|
||||
<div style={{padding:'8px 12px 12px',borderTop:'1px solid var(--border)'}}>
|
||||
<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>
|
||||
<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}
|
||||
<input value={filterKeyword} onChange={e => onFilterKeyword(e.target.value)} autoComplete="off" onFocus={onInputFocus} onBlur={onInputBlur}
|
||||
placeholder="Search events…" 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'}}/>
|
||||
</div>
|
||||
<select value={filterTypeId} onChange={e=>onFilterTypeId(e.target.value)}
|
||||
@@ -389,7 +389,7 @@ function EventTypePopup({ userGroups, onSave, onClose, editing=null }) {
|
||||
};
|
||||
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={{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="off" autoCorrect="off" 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">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}}>
|
||||
@@ -453,7 +453,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={{display:'flex',alignItems:'center',gap:8,fontSize:13}}>
|
||||
<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))} autoComplete="off" style={{width:60,textAlign:'center'}}/>
|
||||
<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>)}
|
||||
</select>
|
||||
@@ -475,8 +475,8 @@ function CustomRecurrenceFields({ rule, onChange }) {
|
||||
<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)}/>
|
||||
{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==='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==='on' && (rule.ends||'never')==='on' && <input type="date" className="input" value={rule.endDate||''} onChange={e => upd('endDate',e.target.value)} autoComplete="off" 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)} autoComplete="off" style={{width:64,textAlign:'center',marginLeft:8}}/><span style={{color:'var(--text-tertiary)'}}>occurrences</span></>}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
@@ -658,7 +658,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();}}>
|
||||
{/* Title */}
|
||||
<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="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 */}
|
||||
@@ -677,7 +677,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
|
||||
<FormRow label="Date & Time">
|
||||
<div style={{display:'flex',flexDirection:'column',gap:8}}>
|
||||
<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)} autoComplete="off" style={{width:150,flexShrink:0}}/>
|
||||
{!allDay&&(
|
||||
<>
|
||||
<TimeInput value={st} onChange={setSt} style={{width:120,flexShrink:0}}/>
|
||||
@@ -686,7 +686,7 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
|
||||
setEt(newEt); userSetEndTime.current=true;
|
||||
if(sd===ed && newEt<=st){ const d=new Date(buildISO(sd,st)); d.setDate(d.getDate()+1); const p=n=>String(n).padStart(2,'0'); setEd(`${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`); }
|
||||
}} style={{width:120,flexShrink:0}}/>
|
||||
<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;}} autoComplete="off" style={{width:150,flexShrink:0}}/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -744,12 +744,12 @@ function EventForm({ event, userGroups, eventTypes, selectedDate, onSave, onCanc
|
||||
|
||||
{/* 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="off" autoCorrect="off" autoCapitalize="off" />
|
||||
</FormRow>
|
||||
|
||||
{/* Description */}
|
||||
<FormRow label="Description">
|
||||
<textarea className="input" placeholder="Add description" value={desc} onChange={e=>setDesc(e.target.value)} rows={3} style={{resize:'vertical'}}/>
|
||||
<textarea className="input" placeholder="Add description" value={desc} onChange={e=>setDesc(e.target.value)} rows={3} autoComplete="off" autoCorrect="off" style={{resize:'vertical'}}/>
|
||||
</FormRow>
|
||||
|
||||
<div style={{display:'flex',gap:8,marginTop:8}}>
|
||||
@@ -919,7 +919,7 @@ function EventTypesPanel({ eventTypes, userGroups, onUpdated, isMobile=false })
|
||||
<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>
|
||||
</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="off" autoCorrect="off" 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)'}}/>
|
||||
<div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}>
|
||||
<label style={{fontSize:14,color:'var(--text-tertiary)',flexShrink:0}}>Colour</label>
|
||||
@@ -1627,7 +1627,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
className="input"
|
||||
placeholder={`Keyword… (space = OR, "phrase")`}
|
||||
value={filterKeyword}
|
||||
onChange={e => { setFilterKeyword(e.target.value); if (!e.target.value) setFilterFromDate(null); }} autoComplete="new-password" autoCorrect="off" autoCapitalize="off" spellCheck={false}
|
||||
onChange={e => { setFilterKeyword(e.target.value); if (!e.target.value) setFilterFromDate(null); }} autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck={false}
|
||||
style={{ marginBottom:8, fontSize:13 }} />
|
||||
<select
|
||||
className="input"
|
||||
|
||||
@@ -153,7 +153,7 @@ function RegistrationTab({ onFeaturesChanged }) {
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<div className="settings-section-label">Serial Number</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
|
||||
<input className="input flex-1" value={serialNumber} readOnly style={{ fontFamily: 'monospace', letterSpacing: 1 }} autoComplete="new-password" />
|
||||
<input className="input flex-1" value={serialNumber} readOnly style={{ fontFamily: 'monospace', letterSpacing: 1 }} autoComplete="off" />
|
||||
<button className="btn btn-secondary btn-sm" onClick={handleCopySerial} style={{ flexShrink: 0 }}>
|
||||
{copied ? '✓ Copied' : (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
@@ -167,7 +167,7 @@ function RegistrationTab({ onFeaturesChanged }) {
|
||||
<div className="settings-section-label">Registration Code</div>
|
||||
<div style={{ display: 'flex', gap: 8, marginTop: 6 }}>
|
||||
<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="off" />
|
||||
<button className="btn btn-primary btn-sm" onClick={handleRegister} disabled={regLoading}>
|
||||
{regLoading ? '…' : 'Register'}
|
||||
</button>
|
||||
|
||||
@@ -376,7 +376,7 @@ function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey, isMobile = fa
|
||||
<div style={{ display:'flex', flexDirection:'column', gap:18, maxWidth: isMobile ? '100%' : 520 }}>
|
||||
<div>
|
||||
<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="off" placeholder="e.g. Coaches + Players" style={{ marginTop:6 }} onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="settings-section-label">Member Groups</label>
|
||||
@@ -604,8 +604,8 @@ 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>
|
||||
</label>
|
||||
<input className="input" placeholder="Search groups…" value={search}
|
||||
onChange={e => setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }}
|
||||
autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
|
||||
onChange={e => setSearch(e.target.value)} autoComplete="off" style={{ marginBottom:8 }}
|
||||
onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
|
||||
@@ -241,7 +241,7 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
value={email} onChange={e => setEmail(e.target.value)}
|
||||
disabled={isEdit}
|
||||
style={{ width:'100%', ...(isEdit ? { opacity:0.6, cursor:'not-allowed' } : {}) }}
|
||||
autoComplete="email" autoCorrect="off" autoCapitalize="off" spellCheck="false" onFocus={onIF} onBlur={onIB} />
|
||||
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false" onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
|
||||
{/* Row 2: First Name + Last Name */}
|
||||
@@ -250,13 +250,13 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
{lbl('First Name', true)}
|
||||
<input className="input" placeholder="Jane"
|
||||
value={firstName} onChange={e => setFirstName(e.target.value)}
|
||||
autoComplete="given-name" autoCapitalize="words" onFocus={onIF} onBlur={onIB} />
|
||||
autoComplete="off" autoCapitalize="words" onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
<div>
|
||||
{lbl('Last Name', true)}
|
||||
<input className="input" placeholder="Smith"
|
||||
value={lastName} onChange={e => setLastName(e.target.value)}
|
||||
autoComplete="family-name" autoCapitalize="words" onFocus={onIF} onBlur={onIB} />
|
||||
autoComplete="off" autoCapitalize="words" onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,7 +266,7 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
{lbl('Phone', false, '(optional)')}
|
||||
<input className="input" type="tel" placeholder="+1 555 000 0000"
|
||||
value={phone} onChange={e => setPhone(e.target.value)}
|
||||
autoComplete="tel" onFocus={onIF} onBlur={onIB} />
|
||||
autoComplete="off" onFocus={onIF} onBlur={onIB} />
|
||||
</div>
|
||||
<div>
|
||||
{lbl('App Role', true)}
|
||||
@@ -629,7 +629,7 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
|
||||
<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}
|
||||
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' }}>
|
||||
|
||||
Reference in New Issue
Block a user