Group users by company + allow admins to set user/admin role
- Superadmin sees users grouped by company with header rows - Admins can now set user or admin role when creating/editing users - Admin role change restricted to own company only - Prevents admin from modifying superadmin roles Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
14
api.php
14
api.php
@@ -1919,9 +1919,10 @@ switch ($action) {
|
||||
}
|
||||
}
|
||||
} elseif (!$isSA) {
|
||||
// Admin luo käyttäjiä vain omaan yritykseensä -> oletusrooli user
|
||||
// Admin luo käyttäjiä omaan yritykseensä — voi valita admin tai user
|
||||
$myCompanyId = $_SESSION['company_id'] ?? '';
|
||||
$companyRoles[$myCompanyId] = 'user';
|
||||
$requestedRole = $input['company_roles'][$myCompanyId] ?? 'user';
|
||||
$companyRoles[$myCompanyId] = in_array($requestedRole, ['admin', 'user']) ? $requestedRole : 'user';
|
||||
}
|
||||
$newUser = [
|
||||
'id' => generateId(),
|
||||
@@ -1982,9 +1983,12 @@ switch ($action) {
|
||||
if (isset($input['company_roles']) && is_array($input['company_roles'])) {
|
||||
$companyRoles = $u['company_roles'] ?? [];
|
||||
foreach ($input['company_roles'] as $cid => $crole) {
|
||||
if (in_array($cid, $u['companies'] ?? []) && in_array($crole, ['admin', 'user'])) {
|
||||
$companyRoles[$cid] = $crole;
|
||||
}
|
||||
if (!in_array($cid, $u['companies'] ?? []) || !in_array($crole, ['admin', 'user'])) continue;
|
||||
// Admin voi muuttaa vain oman yrityksensä rooleja
|
||||
if (!$isSA && $cid !== $myCompanyId) continue;
|
||||
// Admin ei voi muuttaa superadminin roolia
|
||||
if (!$isSA && ($u['role'] === 'superadmin')) continue;
|
||||
$companyRoles[$cid] = $crole;
|
||||
}
|
||||
$u['company_roles'] = $companyRoles;
|
||||
}
|
||||
|
||||
@@ -2133,6 +2133,13 @@
|
||||
<label>Yritykset ja roolit</label>
|
||||
<div id="user-company-checkboxes" style="display:flex;flex-direction:column;gap:0.5rem;margin-top:0.25rem;"></div>
|
||||
</div>
|
||||
<div class="form-group" id="admin-company-role-section" style="display:none;">
|
||||
<label>Rooli yrityksessä</label>
|
||||
<select id="admin-company-role-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
||||
<option value="user">Käyttäjä</option>
|
||||
<option value="admin">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="user-signatures-section" style="display:none;margin-top:1rem;border-top:1px solid #e5e7eb;padding-top:1rem;">
|
||||
<h3 style="color:#0f3460;font-size:1rem;margin-bottom:0.75rem;">Sähköpostiallekirjoitukset</h3>
|
||||
|
||||
107
script.js
107
script.js
@@ -1160,23 +1160,64 @@ async function loadUsers() {
|
||||
try {
|
||||
const users = await apiCall('users');
|
||||
const utbody = document.getElementById('users-tbody');
|
||||
utbody.innerHTML = users.map(u => `<tr>
|
||||
<td><strong>${esc(u.username)}</strong></td>
|
||||
<td>${esc(u.nimi)}</td>
|
||||
<td>${esc(u.email || '')}</td>
|
||||
<td>${u.role === 'superadmin' ? '<span class="role-badge role-superadmin">Pääkäyttäjä</span>' :
|
||||
(u.company_roles || {})[currentCompany?.id] === 'admin'
|
||||
? '<span class="role-badge role-admin">Admin</span>'
|
||||
: '<span class="role-badge role-user">Käyttäjä</span>'}</td>
|
||||
<td>${esc(u.luotu)}</td>
|
||||
<td class="actions-cell">
|
||||
<button onclick="editUser('${u.id}')" title="Muokkaa">✎</button>
|
||||
${u.id !== '${currentUser.id}' ? `<button onclick="deleteUser('${u.id}','${esc(u.username)}')" title="Poista">🗑</button>` : ''}
|
||||
</td>
|
||||
</tr>`).join('');
|
||||
const isSA = currentUser?.role === 'superadmin';
|
||||
|
||||
if (isSA) {
|
||||
// Superadmin: ryhmittele yrityskohtaisesti
|
||||
const companyMap = {}; // companyId => { name, users }
|
||||
const noCompany = [];
|
||||
users.forEach(u => {
|
||||
const comps = u.companies || [];
|
||||
if (comps.length === 0) {
|
||||
noCompany.push(u);
|
||||
} else {
|
||||
comps.forEach(cid => {
|
||||
if (!companyMap[cid]) {
|
||||
const comp = availableCompanies.find(c => c.id === cid);
|
||||
companyMap[cid] = { name: comp?.nimi || cid, users: [] };
|
||||
}
|
||||
companyMap[cid].users.push(u);
|
||||
});
|
||||
}
|
||||
});
|
||||
// Järjestä yritykset nimen mukaan
|
||||
const sortedCompanies = Object.entries(companyMap).sort((a, b) => a[1].name.localeCompare(b[1].name));
|
||||
let html = '';
|
||||
for (const [cid, group] of sortedCompanies) {
|
||||
html += `<tr><td colspan="6" style="background:#e8ecf1;font-weight:700;color:#0f3460;padding:10px 12px;font-size:0.95rem;border-top:2px solid #c8d0da;">${esc(group.name)}</td></tr>`;
|
||||
html += group.users.map(u => renderUserRow(u, cid)).join('');
|
||||
}
|
||||
if (noCompany.length) {
|
||||
html += `<tr><td colspan="6" style="background:#f5f0e8;font-weight:700;color:#888;padding:10px 12px;font-size:0.95rem;border-top:2px solid #e0d8c8;">Ei yritystä</td></tr>`;
|
||||
html += noCompany.map(u => renderUserRow(u, null)).join('');
|
||||
}
|
||||
utbody.innerHTML = html;
|
||||
} else {
|
||||
// Admin: flat-lista omasta yrityksestä
|
||||
utbody.innerHTML = users.map(u => renderUserRow(u, currentCompany?.id)).join('');
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderUserRow(u, companyId) {
|
||||
const role = u.role === 'superadmin'
|
||||
? '<span class="role-badge role-superadmin">Pääkäyttäjä</span>'
|
||||
: (u.company_roles || {})[companyId] === 'admin'
|
||||
? '<span class="role-badge role-admin">Admin</span>'
|
||||
: '<span class="role-badge role-user">Käyttäjä</span>';
|
||||
return `<tr>
|
||||
<td><strong>${esc(u.username)}</strong></td>
|
||||
<td>${esc(u.nimi)}</td>
|
||||
<td>${esc(u.email || '')}</td>
|
||||
<td>${role}</td>
|
||||
<td>${esc(u.luotu)}</td>
|
||||
<td class="actions-cell">
|
||||
<button onclick="editUser('${u.id}')" title="Muokkaa">✎</button>
|
||||
${u.id !== currentUser?.id ? `<button onclick="deleteUser('${u.id}','${esc(u.username)}')" title="Poista">🗑</button>` : ''}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
let usersCache = [];
|
||||
document.getElementById('btn-add-user').addEventListener('click', () => openUserForm());
|
||||
document.getElementById('user-modal-close').addEventListener('click', () => userModal.style.display = 'none');
|
||||
@@ -1196,9 +1237,24 @@ function openUserForm(user = null) {
|
||||
// Piilota superadmin-kenttä ellei ole superadmin
|
||||
const roleGroup = document.getElementById('user-role-group');
|
||||
if (roleGroup) roleGroup.style.display = currentUser?.role === 'superadmin' ? '' : 'none';
|
||||
// Piilota yrityscheckboxit adminilta (näkee vain oman yrityksen)
|
||||
// Piilota yrityscheckboxit adminilta (näkee vain oman yrityksen), mutta näytä yrityskohtainen rooli
|
||||
const isSuperAdmin = currentUser?.role === 'superadmin';
|
||||
const compSection = document.getElementById('user-company-checkboxes')?.closest('.form-group');
|
||||
if (compSection) compSection.style.display = currentUser?.role === 'superadmin' ? '' : 'none';
|
||||
if (compSection) compSection.style.display = isSuperAdmin ? '' : 'none';
|
||||
// Admin-näkymä: yrityskohtainen rooli omalle yritykselle
|
||||
const adminRoleSection = document.getElementById('admin-company-role-section');
|
||||
if (adminRoleSection) {
|
||||
if (!isSuperAdmin && currentUser?.company_role === 'admin') {
|
||||
adminRoleSection.style.display = '';
|
||||
const adminRoleSelect = document.getElementById('admin-company-role-select');
|
||||
if (adminRoleSelect) {
|
||||
const currentRole = (user?.company_roles || {})[currentCompany?.id] || 'user';
|
||||
adminRoleSelect.value = currentRole;
|
||||
}
|
||||
} else {
|
||||
adminRoleSection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
// Yrityscheckboxit + yrityskohtaiset roolit
|
||||
const allComps = availableCompanies.length > 0 ? availableCompanies : [];
|
||||
const userComps = user ? (user.companies || []) : [];
|
||||
@@ -1282,12 +1338,21 @@ document.getElementById('user-form').addEventListener('submit', async (e) => {
|
||||
const companies = [...document.querySelectorAll('.user-company-cb:checked')].map(cb => cb.value);
|
||||
// Kerää yrityskohtaiset roolit
|
||||
const company_roles = {};
|
||||
document.querySelectorAll('.user-company-role').forEach(sel => {
|
||||
const cid = sel.dataset.companyId;
|
||||
if (companies.includes(cid)) {
|
||||
company_roles[cid] = sel.value;
|
||||
if (currentUser?.role === 'superadmin') {
|
||||
// Superadmin: kerää kaikista yritys-dropdowneista
|
||||
document.querySelectorAll('.user-company-role').forEach(sel => {
|
||||
const cid = sel.dataset.companyId;
|
||||
if (companies.includes(cid)) {
|
||||
company_roles[cid] = sel.value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Admin: käytä admin-company-role-select omalle yritykselle
|
||||
const adminRoleSelect = document.getElementById('admin-company-role-select');
|
||||
if (adminRoleSelect && currentCompany?.id) {
|
||||
company_roles[currentCompany.id] = adminRoleSelect.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Kerää allekirjoitukset
|
||||
const signatures = {};
|
||||
document.querySelectorAll('.sig-textarea').forEach(ta => {
|
||||
|
||||
Reference in New Issue
Block a user