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) {
|
} 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'] ?? '';
|
$myCompanyId = $_SESSION['company_id'] ?? '';
|
||||||
$companyRoles[$myCompanyId] = 'user';
|
$requestedRole = $input['company_roles'][$myCompanyId] ?? 'user';
|
||||||
|
$companyRoles[$myCompanyId] = in_array($requestedRole, ['admin', 'user']) ? $requestedRole : 'user';
|
||||||
}
|
}
|
||||||
$newUser = [
|
$newUser = [
|
||||||
'id' => generateId(),
|
'id' => generateId(),
|
||||||
@@ -1982,9 +1983,12 @@ switch ($action) {
|
|||||||
if (isset($input['company_roles']) && is_array($input['company_roles'])) {
|
if (isset($input['company_roles']) && is_array($input['company_roles'])) {
|
||||||
$companyRoles = $u['company_roles'] ?? [];
|
$companyRoles = $u['company_roles'] ?? [];
|
||||||
foreach ($input['company_roles'] as $cid => $crole) {
|
foreach ($input['company_roles'] as $cid => $crole) {
|
||||||
if (in_array($cid, $u['companies'] ?? []) && in_array($crole, ['admin', 'user'])) {
|
if (!in_array($cid, $u['companies'] ?? []) || !in_array($crole, ['admin', 'user'])) continue;
|
||||||
$companyRoles[$cid] = $crole;
|
// 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;
|
$u['company_roles'] = $companyRoles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2133,6 +2133,13 @@
|
|||||||
<label>Yritykset ja roolit</label>
|
<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 id="user-company-checkboxes" style="display:flex;flex-direction:column;gap:0.5rem;margin-top:0.25rem;"></div>
|
||||||
</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>
|
||||||
<div id="user-signatures-section" style="display:none;margin-top:1rem;border-top:1px solid #e5e7eb;padding-top:1rem;">
|
<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>
|
<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 {
|
try {
|
||||||
const users = await apiCall('users');
|
const users = await apiCall('users');
|
||||||
const utbody = document.getElementById('users-tbody');
|
const utbody = document.getElementById('users-tbody');
|
||||||
utbody.innerHTML = users.map(u => `<tr>
|
const isSA = currentUser?.role === 'superadmin';
|
||||||
<td><strong>${esc(u.username)}</strong></td>
|
|
||||||
<td>${esc(u.nimi)}</td>
|
if (isSA) {
|
||||||
<td>${esc(u.email || '')}</td>
|
// Superadmin: ryhmittele yrityskohtaisesti
|
||||||
<td>${u.role === 'superadmin' ? '<span class="role-badge role-superadmin">Pääkäyttäjä</span>' :
|
const companyMap = {}; // companyId => { name, users }
|
||||||
(u.company_roles || {})[currentCompany?.id] === 'admin'
|
const noCompany = [];
|
||||||
? '<span class="role-badge role-admin">Admin</span>'
|
users.forEach(u => {
|
||||||
: '<span class="role-badge role-user">Käyttäjä</span>'}</td>
|
const comps = u.companies || [];
|
||||||
<td>${esc(u.luotu)}</td>
|
if (comps.length === 0) {
|
||||||
<td class="actions-cell">
|
noCompany.push(u);
|
||||||
<button onclick="editUser('${u.id}')" title="Muokkaa">✎</button>
|
} else {
|
||||||
${u.id !== '${currentUser.id}' ? `<button onclick="deleteUser('${u.id}','${esc(u.username)}')" title="Poista">🗑</button>` : ''}
|
comps.forEach(cid => {
|
||||||
</td>
|
if (!companyMap[cid]) {
|
||||||
</tr>`).join('');
|
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); }
|
} 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 = [];
|
let usersCache = [];
|
||||||
document.getElementById('btn-add-user').addEventListener('click', () => openUserForm());
|
document.getElementById('btn-add-user').addEventListener('click', () => openUserForm());
|
||||||
document.getElementById('user-modal-close').addEventListener('click', () => userModal.style.display = 'none');
|
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
|
// Piilota superadmin-kenttä ellei ole superadmin
|
||||||
const roleGroup = document.getElementById('user-role-group');
|
const roleGroup = document.getElementById('user-role-group');
|
||||||
if (roleGroup) roleGroup.style.display = currentUser?.role === 'superadmin' ? '' : 'none';
|
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');
|
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
|
// Yrityscheckboxit + yrityskohtaiset roolit
|
||||||
const allComps = availableCompanies.length > 0 ? availableCompanies : [];
|
const allComps = availableCompanies.length > 0 ? availableCompanies : [];
|
||||||
const userComps = user ? (user.companies || []) : [];
|
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);
|
const companies = [...document.querySelectorAll('.user-company-cb:checked')].map(cb => cb.value);
|
||||||
// Kerää yrityskohtaiset roolit
|
// Kerää yrityskohtaiset roolit
|
||||||
const company_roles = {};
|
const company_roles = {};
|
||||||
document.querySelectorAll('.user-company-role').forEach(sel => {
|
if (currentUser?.role === 'superadmin') {
|
||||||
const cid = sel.dataset.companyId;
|
// Superadmin: kerää kaikista yritys-dropdowneista
|
||||||
if (companies.includes(cid)) {
|
document.querySelectorAll('.user-company-role').forEach(sel => {
|
||||||
company_roles[cid] = sel.value;
|
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
|
// Kerää allekirjoitukset
|
||||||
const signatures = {};
|
const signatures = {};
|
||||||
document.querySelectorAll('.sig-textarea').forEach(ta => {
|
document.querySelectorAll('.sig-textarea').forEach(ta => {
|
||||||
|
|||||||
Reference in New Issue
Block a user