v0.12.24 minor user manager buf fixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rosterchirp-backend",
|
||||
"version": "0.12.23",
|
||||
"version": "0.12.24",
|
||||
"description": "RosterChirp backend server",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
3
backend/src/models/migrations/010_dob_guardian.sql
Normal file
3
backend/src/models/migrations/010_dob_guardian.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- Migration 010: Date of birth and guardian fields
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS date_of_birth DATE;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS guardian_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL;
|
||||
2
build.sh
2
build.sh
@@ -13,7 +13,7 @@
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-0.12.23}"
|
||||
VERSION="${1:-0.12.24}"
|
||||
ACTION="${2:-}"
|
||||
REGISTRY="${REGISTRY:-}"
|
||||
IMAGE_NAME="rosterchirp"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rosterchirp-frontend",
|
||||
"version": "0.12.23",
|
||||
"version": "0.12.24",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -120,7 +120,8 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
const [email, setEmail] = useState(user?.email || '');
|
||||
const [phone, setPhone] = useState(user?.phone || '');
|
||||
const [role, setRole] = useState(user?.role || 'member');
|
||||
const [isMinor, setIsMinor] = useState(!!user?.is_minor);
|
||||
const [dob, setDob] = useState(user?.date_of_birth || '');
|
||||
const [guardianId, setGuardianId] = useState(user?.guardian_user_id || '');
|
||||
const [password, setPassword] = useState('');
|
||||
const [pwEnabled, setPwEnabled] = useState(!isEdit);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -155,8 +156,6 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
if (!lastName.trim()) return toast('Last name is required', 'error');
|
||||
if (!isValidPhone(phone)) return toast('Invalid phone number', 'error');
|
||||
if (!['member', 'admin', 'manager'].includes(role)) return toast('Role is required', 'error');
|
||||
if (!isEdit && (!password || password.length < 6))
|
||||
return toast('Password must be at least 6 characters', 'error');
|
||||
if (isEdit && pwEnabled && (!password || password.length < 6))
|
||||
return toast('New password must be at least 6 characters', 'error');
|
||||
|
||||
@@ -167,7 +166,6 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
firstName: firstName.trim(),
|
||||
lastName: lastName.trim(),
|
||||
phone: phone.trim(),
|
||||
isMinor,
|
||||
role,
|
||||
...(pwEnabled && password ? { password } : {}),
|
||||
});
|
||||
@@ -185,9 +183,8 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
lastName: lastName.trim(),
|
||||
email: email.trim(),
|
||||
phone: phone.trim(),
|
||||
isMinor,
|
||||
role,
|
||||
password,
|
||||
...(password ? { password } : {}),
|
||||
});
|
||||
// Add to selected groups
|
||||
for (const gId of selectedGroupIds) {
|
||||
@@ -273,13 +270,23 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 4: Is minor */}
|
||||
<div style={{ marginBottom:12 }}>
|
||||
<label style={{ display:'flex', alignItems:'center', gap:8, cursor:'pointer', fontSize:14, color:'var(--text-primary)', userSelect:'none' }}>
|
||||
<input type="checkbox" checked={isMinor} onChange={e => setIsMinor(e.target.checked)}
|
||||
style={{ accentColor:'var(--primary)', width:15, height:15 }} />
|
||||
User is a minor
|
||||
</label>
|
||||
{/* Row 4: DOB + Guardian */}
|
||||
<div style={{ display:'grid', gridTemplateColumns:colGrid, gap:12, marginBottom:12 }}>
|
||||
<div>
|
||||
{lbl('Date of Birth', false, '(optional)')}
|
||||
<input className="input" type="text" placeholder="YYYY-MM-DD"
|
||||
value={dob} onChange={e => setDob(e.target.value)}
|
||||
disabled
|
||||
style={{ opacity:0.5, cursor:'not-allowed' }} />
|
||||
</div>
|
||||
<div>
|
||||
{lbl('Guardian', false, '(optional)')}
|
||||
<select className="input" value={guardianId} onChange={e => setGuardianId(e.target.value)}
|
||||
disabled
|
||||
style={{ opacity:0.5, cursor:'not-allowed' }}>
|
||||
<option value="">— Select guardian —</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 4b: User Groups */}
|
||||
@@ -303,9 +310,9 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
{/* Row 5: Password */}
|
||||
<div style={{ marginBottom:16 }}>
|
||||
{lbl('Password',
|
||||
(!isEdit) || (isEdit && pwEnabled),
|
||||
isEdit && pwEnabled,
|
||||
isEdit && !pwEnabled ? '(not changing — click Reset Password to set a new one)' :
|
||||
!isEdit ? `(blank = ${userPass})` : null
|
||||
!isEdit ? `(optional — blank uses system default)` : null
|
||||
)}
|
||||
<div style={{ opacity: pwEnabled ? 1 : 0.55 }}>
|
||||
<PasswordInput
|
||||
@@ -335,9 +342,6 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
|
||||
Cancel Reset
|
||||
</button>
|
||||
)}
|
||||
<button className="btn btn-secondary" onClick={onCancel} style={{ marginLeft:'auto' }}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Row 7 (edit only): Last login + must change password */}
|
||||
@@ -393,7 +397,7 @@ function BulkImportForm({ userPass, allUserGroups, onCreated }) {
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
|
||||
const codeStyle = { fontSize:12, color:'var(--text-secondary)', display:'block', background:'var(--surface)', padding:'6px 8px', borderRadius:4, border:'1px solid var(--border)', whiteSpace:'pre', fontFamily:'monospace', marginBottom:4 };
|
||||
const codeStyle = { fontSize:12, color:'var(--text-secondary)', display:'block', background:'var(--surface)', padding:'6px 8px', borderRadius:4, border:'1px solid var(--border)', whiteSpace:'pre-wrap', overflowWrap:'anywhere', fontFamily:'monospace', marginBottom:4 };
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth:580, display:'flex', flexDirection:'column', gap:16 }}>
|
||||
@@ -444,7 +448,7 @@ function BulkImportForm({ userPass, allUserGroups, onCreated }) {
|
||||
</ul>
|
||||
</div>
|
||||
<p style={{ color:'var(--text-tertiary)', marginTop:2 }}>
|
||||
Optional field defaults: password = <strong>{userPass}</strong>, role = member, usergroup = (none), minor = (none)
|
||||
Optional field defaults: password = <strong>{userPass}</strong>, role = member, usergroup = (none)
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user