v0.10.7 UI rule changes
This commit is contained in:
@@ -64,6 +64,7 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
|
||||
const [selected, setSelected] = useState(null);
|
||||
const [savedMembers, setSavedMembers] = useState(new Set());
|
||||
const [members, setMembers] = useState(new Set());
|
||||
const [fullMembers, setFullMembers] = useState([]); // full member objects including deleted
|
||||
const [editName, setEditName] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
@@ -78,8 +79,12 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
|
||||
const { members: mems } = await api.getUserGroup(g.id);
|
||||
const ids = new Set(mems.map(m => m.id));
|
||||
setSelected(g); setEditName(g.name); setMembers(ids); setSavedMembers(ids);
|
||||
setFullMembers(mems);
|
||||
};
|
||||
const clearSelection = () => {
|
||||
setSelected(null); setEditName(''); setMembers(new Set()); setSavedMembers(new Set());
|
||||
setShowDelete(false); setFullMembers([]);
|
||||
};
|
||||
const clearSelection = () => { setSelected(null); setEditName(''); setMembers(new Set()); setSavedMembers(new Set()); setShowDelete(false); };
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!editName.trim()) return toast('Name required', 'error');
|
||||
@@ -90,7 +95,7 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
|
||||
toast('Group updated', 'success');
|
||||
const { members: fresh } = await api.getUserGroup(selected.id);
|
||||
const freshIds = new Set(fresh.map(m => m.id));
|
||||
setSavedMembers(freshIds); setMembers(freshIds);
|
||||
setSavedMembers(freshIds); setMembers(freshIds); setFullMembers(fresh);
|
||||
setSelected(prev => ({ ...prev, name: editName.trim() }));
|
||||
} else {
|
||||
await api.createUserGroup({ name: editName.trim(), memberIds: [...members] });
|
||||
@@ -111,6 +116,19 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
|
||||
|
||||
const canDelete = selected && savedMembers.size === 0;
|
||||
const isCreating = !selected;
|
||||
const deletedMembers = fullMembers.filter(m => m.status === 'deleted');
|
||||
|
||||
const forceRemoveMember = async (m) => {
|
||||
if (!confirm(`Force-remove deleted user "${m.name}" from this group?`)) return;
|
||||
try {
|
||||
await api.removeUserGroupMember(selected.id, m.id);
|
||||
toast(`${m.name} removed`, 'success');
|
||||
const { members: fresh } = await api.getUserGroup(selected.id);
|
||||
const freshIds = new Set(fresh.map(x => x.id));
|
||||
setSavedMembers(freshIds); setMembers(freshIds); setFullMembers(fresh);
|
||||
load(); onRefresh();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display:'flex', flexDirection: isMobile ? 'column' : 'row', gap:0, height:'100%', minHeight:0, overflow: isMobile ? 'auto' : 'hidden' }}>
|
||||
@@ -146,6 +164,25 @@ function AllGroupsTab({ allUsers, onRefresh, isMobile = false, onIF, onIB }) {
|
||||
<div style={{ marginTop:6 }}><UserCheckList allUsers={allUsers} selectedIds={members} onChange={setMembers} onIF={onIF} onIB={onIB} /></div>
|
||||
<p style={{ fontSize:12, color:'var(--text-tertiary)', marginTop:5 }}>{members.size} selected</p>
|
||||
</div>
|
||||
{deletedMembers.length > 0 && (
|
||||
<div>
|
||||
<label className="settings-section-label" style={{ color:'var(--error)' }}>
|
||||
Orphaned Members (deleted users)
|
||||
</label>
|
||||
<div style={{ marginTop:6, border:'1px solid var(--border)', borderRadius:'var(--radius)', overflow:'hidden' }}>
|
||||
{deletedMembers.map(m => (
|
||||
<div key={m.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 12px', borderBottom:'1px solid var(--border)' }}>
|
||||
<span style={{ flex:1, fontSize:13, color:'var(--text-tertiary)', textDecoration:'line-through' }}>{m.name}</span>
|
||||
<span style={{ fontSize:11, color:'var(--error)', marginRight:8 }}>Deleted</span>
|
||||
<button className="btn btn-danger btn-sm" onClick={() => forceRemoveMember(m)}>Remove</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p style={{ fontSize:11, color:'var(--text-tertiary)', marginTop:4 }}>
|
||||
These users were deleted but remain as group members. Remove them to allow this group to be deleted.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ display:'flex', gap:8, alignItems:'center', flexWrap:'wrap' }}>
|
||||
<button className="btn btn-primary btn-sm" onClick={handleSave} disabled={saving}>{saving?'Saving…':isCreating?'Create Group':'Save Changes'}</button>
|
||||
{!isCreating && <button className="btn btn-secondary btn-sm" onClick={clearSelection}>Cancel</button>}
|
||||
|
||||
Reference in New Issue
Block a user