/* ============================================================ KODILA — Admin: Pravice ============================================================ */ const PERMISSION_MODULES = [ { key: 'sifranti', label: 'Šifranti' }, { key: 'dokumenti', label: 'Dokumenti' }, { key: 'admin', label: 'Admin panel' }, { key: 'aplikacije', label: 'Aplikacije' }, ]; function emptyPermissionMap(value) { const map = {}; PERMISSION_MODULES.forEach(m => { map[m.key] = value; }); return map; } function permissionCheckboxCol(mod) { return { field: mod.key, headerName: mod.label, width: 130, cellRenderer: 'agCheckboxCellRenderer', cellEditor: 'agCheckboxCellEditor', editable: true, sortable: false, filter: false, }; } const PRAVICE_COLS = [ { field: 'id', headerName: 'ID', width: 70, cellClass: 'cell-mono', pinned: 'left' }, { field: 'fullName', headerName: 'Ime in priimek', flex: 2, minWidth: 200, cellRenderer: userNameRenderer }, { field: 'email', headerName: 'E-pošta', minWidth: 190 }, { field: 'groupName', headerName: 'Skupina', minWidth: 150, valueFormatter: p => p.value || 'Brez skupine' }, ...PERMISSION_MODULES.map(permissionCheckboxCol), ]; /* --- Pravice po uporabnikih --- */ function PraviceUporabniki() { const toast = useToast(); const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [selected, setSelected] = useState([]); const load = async () => { setLoading(true); const { ok, data } = await api('/api/permissions'); if (ok) { setRows(data.users.map(u => ({ ...u, ...u.permissions }))); } else { toast('Pravic ni bilo mogoče naložiti.', 'alert'); } setLoading(false); }; useEffect(() => { load(); }, []); const applyResult = (data) => { setRows(rs => rs.map(r => r.id === data.user.id ? { ...r, groupId: data.user.groupId, groupName: data.user.groupName, ...data.user.permissions } : r)); }; const persist = async (userId, permissions) => { const { ok, data } = await api(`/api/permissions/users/${userId}`, { method: 'PUT', body: JSON.stringify({ permissions }) }); if (!ok) { toast('Napaka pri shranjevanju pravic.', 'alert'); return; } applyResult(data); }; const onCellValueChanged = (p) => { const field = p.colDef.field; if (!PERMISSION_MODULES.some(m => m.key === field)) return; const map = {}; PERMISSION_MODULES.forEach(m => { map[m.key] = m.key === field ? !!p.newValue : !!p.data[m.key]; }); persist(p.data.id, map); }; const bulkApply = async (grant) => { if (!selected.length) return; const map = emptyPermissionMap(grant); for (const row of selected) { await persist(row.id, map); } toast(grant ? 'Vse pravice dodeljene' : 'Vse pravice ponastavljene', grant ? 'check' : 'x'); }; return (
{loading ?
Nalaganje …
: ( d.id} selectable exportName="kodila-pravice" exportTitle="Pravice uporabnikov" searchPlaceholder="Išči po imenu, e-pošti…" onCellValueChanged={onCellValueChanged} onSelectionChange={setSelected} actions={ <> } /> )}
); } /* --- Vnosni obrazec skupine (drawer) --- */ function GroupForm({ initial, onSave, onClose }) { const isEdit = !!(initial && initial.id); const [name, setName] = useState(initial && initial.id ? initial.name : ''); const [perms, setPerms] = useState(initial && initial.permissions ? { ...initial.permissions } : emptyPermissionMap(false)); const [errors, setErrors] = useState({}); const [saving, setSaving] = useState(false); const togglePerm = (key) => setPerms(p => ({ ...p, [key]: !p[key] })); const save = async () => { setSaving(true); const ok = await onSave(isEdit ? initial.id : null, { name: name.trim(), permissions: perms }, isEdit, setErrors); setSaving(false); if (ok) onClose(); }; const fieldError = (field) => { const codes = errors[field]; if (!codes || !codes.length) return null; if (codes.includes('taken')) return 'Skupina s tem imenom že obstaja.'; if (codes.includes('required')) return 'Ime skupine je obvezno.'; return codes.join(', '); }; return (
{ if (e.target.classList.contains('overlay')) onClose(); }}>
e.stopPropagation()}>
{isEdit ? 'Uredi skupino' : 'Nova skupina'}
Uporabniška skupina in nabor pravic
Osnovni podatki
setName(e.target.value)} placeholder="npr. Prodaja" style={fieldError('name') ? { borderColor: 'var(--danger)' } : null} /> {fieldError('name') && {fieldError('name')}}
Pravice
{PERMISSION_MODULES.map(m => (
))}
* obvezna polja
); } /* --- Uporabniške skupine --- */ function PraviceSkupine() { const toast = useToast(); const [groups, setGroups] = useState([]); const [loading, setLoading] = useState(true); const [editing, setEditing] = useState(null); const load = async () => { setLoading(true); const { ok, data } = await api('/api/groups'); if (ok) setGroups(data.groups); else toast('Skupin ni bilo mogoče naložiti.', 'alert'); setLoading(false); }; useEffect(() => { load(); }, []); const save = async (id, payload, isEdit, setErrors) => { const { ok, data } = isEdit ? await api(`/api/groups/${id}`, { method: 'PUT', body: JSON.stringify(payload) }) : await api('/api/groups', { method: 'POST', body: JSON.stringify(payload) }); if (!ok) { setErrors(data.errors || {}); return false; } setGroups(gs => isEdit ? gs.map(g => g.id === data.group.id ? data.group : g) : [...gs, data.group]); toast(isEdit ? 'Skupina posodobljena' : 'Skupina ustvarjena: ' + data.group.name, 'check'); return true; }; const remove = async (group) => { if (!window.confirm(`Izbrišem skupino "${group.name}"? Uporabniki v tej skupini bodo prestavljeni na "Brez skupine".`)) return; const { ok, data } = await api(`/api/groups/${group.id}/delete`, { method: 'POST' }); if (!ok) { toast(data.error === 'system_group_locked' ? 'Sistemske skupine ni mogoče izbrisati.' : 'Brisanje ni uspelo.', 'alert'); return; } setGroups(gs => gs.filter(g => g.id !== group.id)); toast('Skupina izbrisana', 'trash'); }; return (
Predloge naborov pravic, ki jih lahko dodeliš uporabnikom v modulu Uporabniki.
{loading ? (
Nalaganje …
) : (
{groups.map(g => (
{g.name} {g.isSystem && sistemska } {g.userCount} uporabnikov
{PERMISSION_MODULES.filter(m => g.permissions[m.key]).map(m => m.label).join(', ') || 'Brez pravic'}
))} {groups.length === 0 &&
Ni ustvarjenih skupin.
}
)} {editing !== null && ( setEditing(null)} /> )}
); } const PRAVICE_TABS = [ { id: 'uporabniki', label: 'Pravice po uporabnikih' }, { id: 'skupine', label: 'Uporabniške skupine' }, ]; function Pravice({ currentUser }) { const [tab, setTab] = useState(PRAVICE_TABS[0].id); return (
{PRAVICE_TABS.map(t => ( ))}
{tab === 'uporabniki' && } {tab === 'skupine' && }
); } Object.assign(window, { Pravice });