v0.12.29 various bug fixes

This commit is contained in:
2026-03-26 09:46:35 -04:00
parent 92dbcf2780
commit 13e5e3a627
8 changed files with 132 additions and 34 deletions

View File

@@ -50,6 +50,16 @@ function parseCSV(text, ignoreFirstRow, allUserGroups) {
return { rows, invalid };
}
function fmtLastLogin(ts) {
if (!ts) return 'Never';
const d = new Date(ts); const today = new Date(); today.setHours(0,0,0,0);
const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1);
const dd = new Date(d); dd.setHours(0,0,0,0);
if (dd >= today) return 'Today';
if (dd >= yesterday) return 'Yesterday';
return dd.toISOString().slice(0, 10);
}
// ── User Row (accordion list item) ───────────────────────────────────────────
function UserRow({ u, onUpdated, onEdit }) {
const toast = useToast();
@@ -94,15 +104,23 @@ function UserRow({ u, onUpdated, onEdit }) {
</button>
{open && !u.is_default_admin && (
<div style={{ padding:'6px 12px 12px', display:'flex', alignItems:'center', gap:8 }}>
<button className="btn btn-primary btn-sm" onClick={() => { setOpen(false); onEdit(u); }}>Edit User</button>
<div style={{ marginLeft:'auto', display:'flex', gap:8 }}>
{u.status === 'active' ? (
<button className="btn btn-sm" style={{ background:'var(--warning)', color:'white' }} onClick={handleSuspend}>Suspend</button>
) : u.status === 'suspended' ? (
<button className="btn btn-sm" style={{ background:'var(--success)', color:'white' }} onClick={handleActivate}>Activate</button>
) : null}
<button className="btn btn-danger btn-sm" onClick={handleDelete}>Delete</button>
<div style={{ padding:'6px 12px 12px', display:'flex', flexDirection:'column', gap:8 }}>
<div style={{ display:'flex', alignItems:'center', gap:14, flexWrap:'wrap', fontSize:12, color:'var(--text-tertiary)', paddingBottom:6, borderBottom:'1px solid var(--border)' }}>
<span>Last Login: <strong style={{ color:'var(--text-secondary)' }}>{fmtLastLogin(u.last_online)}</strong></span>
{!!u.must_change_password && (
<span style={{ color:'var(--warning)', fontWeight:600 }}> Must change password</span>
)}
</div>
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
<button className="btn btn-primary btn-sm" onClick={() => { setOpen(false); onEdit(u); }}>Edit User</button>
<div style={{ marginLeft:'auto', display:'flex', gap:8 }}>
{u.status === 'active' ? (
<button className="btn btn-sm" style={{ background:'var(--warning)', color:'white' }} onClick={handleSuspend}>Suspend</button>
) : u.status === 'suspended' ? (
<button className="btn btn-sm" style={{ background:'var(--success)', color:'white' }} onClick={handleActivate}>Activate</button>
) : null}
<button className="btn btn-danger btn-sm" onClick={handleDelete}>Delete</button>
</div>
</div>
</div>
)}
@@ -139,16 +157,6 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
.catch(() => {});
}, [isEdit, user?.id]);
const fmtLastLogin = (ts) => {
if (!ts) return 'Never';
const d = new Date(ts); const today = new Date(); today.setHours(0,0,0,0);
const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1);
const dd = new Date(d); dd.setHours(0,0,0,0);
if (dd >= today) return 'Today';
if (dd >= yesterday) return 'Yesterday';
return dd.toISOString().slice(0, 10);
};
const handleSubmit = async () => {
if (!isEdit && (!email.trim() || !isValidEmail(email.trim())))
return toast('Valid email address required', 'error');
@@ -293,7 +301,7 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o
{allUserGroups?.length > 0 && (
<div style={{ marginBottom:12 }}>
{lbl('User Groups', false, '(optional)')}
<div style={{ border:'1px solid var(--border)', borderRadius:'var(--radius)', maxHeight:160, overflowY:'auto', marginTop:6 }}>
<div style={{ border:'1px solid var(--border)', borderRadius:'var(--radius)', maxHeight:120, overflowY:'auto', marginTop:6 }}>
{allUserGroups.map(g => (
<label key={g.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'7px 10px', borderBottom:'1px solid var(--border)', cursor:'pointer' }}>
<input type="checkbox"