149 lines
7.5 KiB
Cheetah
149 lines
7.5 KiB
Cheetah
{{define "edit_rbac_ru"}}
|
||
<style>
|
||
.col-toggle { cursor: pointer; user-select: none; display: inline-flex; align-items: center; gap: 4px; }
|
||
</style>
|
||
<div class="page-container">
|
||
<div class="page-card" style="max-width: 780px; margin: 0 auto;">
|
||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px;">
|
||
<div>
|
||
<h1>Редактировать права</h1>
|
||
<p class="intro">Доступ {{.GranteeName}} к {{.TargetDossier.Name}}</p>
|
||
</div>
|
||
<a href="/dossier/{{.TargetDossier.DossierID}}" class="btn btn-secondary btn-small">Назад</a>
|
||
</div>
|
||
|
||
{{if .Error}}<div class="error">{{.Error}}</div>{{end}}
|
||
{{if .Success}}<div class="success">{{.Success}}</div>{{end}}
|
||
|
||
<form action="/dossier/{{.TargetDossier.DossierID}}/rbac/{{.GranteeID}}" method="POST">
|
||
<input type="hidden" name="action" value="update">
|
||
<input type="hidden" name="role" id="roleHidden" value="{{.SelectedRole}}">
|
||
|
||
<div style="margin-bottom: 24px;">
|
||
<label style="font-weight: 600; margin-bottom: 8px; display: block;">Роль</label>
|
||
<select id="roleSelect" style="width: 100%;">
|
||
<option value="Custom"{{if eq .SelectedRole "Custom"}} selected{{end}}>Пользовательская</option>
|
||
{{range .Roles}}
|
||
<option value="{{.Name}}" data-grants='{{.GrantsJSON}}'{{if eq $.SelectedRole .Name}} selected{{end}}>{{.Name}} — {{.Description}}</option>
|
||
{{end}}
|
||
</select>
|
||
</div>
|
||
|
||
<table style="width: 100%; border-collapse: collapse; font-size: 0.9rem; table-layout: fixed;">
|
||
<colgroup>
|
||
<col style="width: auto;">
|
||
<col style="width: 72px;">
|
||
<col style="width: 72px;">
|
||
<col style="width: 72px;">
|
||
<col style="width: 72px;">
|
||
<col style="width: 72px;">
|
||
</colgroup>
|
||
<tr style="border-bottom: 2px solid var(--border);">
|
||
<th style="text-align: left; padding: 8px 12px;">Категория</th>
|
||
<th style="text-align: center; padding: 8px 4px;">Все</th>
|
||
<th style="text-align: center; padding: 8px 4px;"><label class="col-toggle" data-op="r"><input type="checkbox" class="col-cb" data-op="r"> Чтение</label></th>
|
||
<th style="text-align: center; padding: 8px 4px;"><label class="col-toggle" data-op="w"><input type="checkbox" class="col-cb" data-op="w"> Запись</label></th>
|
||
<th style="text-align: center; padding: 8px 4px;"><label class="col-toggle" data-op="d"><input type="checkbox" class="col-cb" data-op="d"> Удаление</label></th>
|
||
<th style="text-align: center; padding: 8px 4px;"><label class="col-toggle" data-op="m"><input type="checkbox" class="col-cb" data-op="m"> Управление</label></th>
|
||
</tr>
|
||
{{range .CategoriesRBAC}}
|
||
<tr style="border-bottom: 1px solid var(--border);" data-cat="{{.ID}}">
|
||
<td style="padding: 6px 12px; text-transform: capitalize;">{{.Name}}</td>
|
||
<td style="text-align: center; padding: 6px 4px;"><input type="checkbox" class="row-toggle"></td>
|
||
<td style="text-align: center; padding: 6px 4px;"><input type="checkbox" name="cat_{{.ID}}_r" value="1" class="perm-cb op-r" {{if .CanRead}}checked{{end}}></td>
|
||
<td style="text-align: center; padding: 6px 4px;"><input type="checkbox" name="cat_{{.ID}}_w" value="1" class="perm-cb op-w" {{if .CanWrite}}checked{{end}}></td>
|
||
<td style="text-align: center; padding: 6px 4px;"><input type="checkbox" name="cat_{{.ID}}_d" value="1" class="perm-cb op-d" {{if .CanDelete}}checked{{end}}></td>
|
||
<td style="text-align: center; padding: 6px 4px;"><input type="checkbox" name="cat_{{.ID}}_m" value="1" class="perm-cb op-m" {{if .CanManage}}checked{{end}}></td>
|
||
</tr>
|
||
{{end}}
|
||
</table>
|
||
|
||
<div style="display: flex; gap: 12px; margin-top: 32px;">
|
||
<a href="/dossier/{{.TargetDossier.DossierID}}" class="btn btn-secondary" style="flex: 1; text-align: center;">Отмена</a>
|
||
<button type="submit" class="btn btn-primary" style="flex: 1;">Сохранить изменения</button>
|
||
</div>
|
||
</form>
|
||
|
||
<div style="border-top: 1px solid var(--border); padding-top: 32px; margin-top: 32px;">
|
||
<form action="/dossier/{{.TargetDossier.DossierID}}/rbac/{{.GranteeID}}" method="POST" onsubmit="return confirm('Отозвать весь доступ для {{.GranteeName}}?');">
|
||
<input type="hidden" name="action" value="revoke">
|
||
<button type="submit" class="btn btn-danger btn-full">Отозвать весь доступ</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function() {
|
||
var roleSelect = document.getElementById('roleSelect');
|
||
var roleHidden = document.getElementById('roleHidden');
|
||
var allCbs = document.querySelectorAll('.perm-cb');
|
||
var catIDs = [];
|
||
document.querySelectorAll('tr[data-cat]').forEach(function(tr) { catIDs.push(parseInt(tr.dataset.cat)); });
|
||
|
||
roleSelect.addEventListener('change', function() {
|
||
roleHidden.value = this.value;
|
||
if (this.value === 'Custom') return;
|
||
var opt = this.options[this.selectedIndex];
|
||
var grantsJSON = opt.dataset.grants;
|
||
if (!grantsJSON) return;
|
||
var grants = JSON.parse(grantsJSON);
|
||
allCbs.forEach(function(cb) { cb.checked = false; });
|
||
var rootOps = '';
|
||
grants.forEach(function(g) { if (g.Category === 0) rootOps = g.Ops || ''; });
|
||
if (rootOps) catIDs.forEach(function(id) { setOps(id, rootOps); });
|
||
grants.forEach(function(g) { if (g.Category > 0) setOps(g.Category, g.Ops || ''); });
|
||
syncAll();
|
||
});
|
||
|
||
function setOps(catID, ops) {
|
||
var row = document.querySelector('tr[data-cat="' + catID + '"]');
|
||
if (!row) return;
|
||
var cbs = row.querySelectorAll('.perm-cb');
|
||
if (cbs[0]) cbs[0].checked = ops.includes('r');
|
||
if (cbs[1]) cbs[1].checked = ops.includes('w');
|
||
if (cbs[2]) cbs[2].checked = ops.includes('d');
|
||
if (cbs[3]) cbs[3].checked = ops.includes('m');
|
||
}
|
||
|
||
allCbs.forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
roleSelect.value = 'Custom'; roleHidden.value = 'Custom';
|
||
syncRowToggle(this.closest('tr')); syncColToggles();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('.row-toggle').forEach(function(toggle) {
|
||
toggle.addEventListener('change', function() {
|
||
var row = this.closest('tr');
|
||
row.querySelectorAll('.perm-cb').forEach(function(cb) { cb.checked = toggle.checked; });
|
||
roleSelect.value = 'Custom'; roleHidden.value = 'Custom';
|
||
syncColToggles();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('.col-cb').forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
document.querySelectorAll('.op-' + this.dataset.op).forEach(function(c) { c.checked = cb.checked; });
|
||
roleSelect.value = 'Custom'; roleHidden.value = 'Custom';
|
||
syncRowToggles();
|
||
});
|
||
});
|
||
|
||
function syncRowToggle(row) {
|
||
var cbs = row.querySelectorAll('.perm-cb');
|
||
var toggle = row.querySelector('.row-toggle');
|
||
if (toggle) toggle.checked = cbs.length > 0 && Array.from(cbs).every(function(cb) { return cb.checked; });
|
||
}
|
||
function syncRowToggles() { document.querySelectorAll('tr[data-cat]').forEach(syncRowToggle); }
|
||
function syncColToggles() {
|
||
document.querySelectorAll('.col-cb').forEach(function(cb) {
|
||
var colCbs = document.querySelectorAll('.op-' + cb.dataset.op);
|
||
cb.checked = colCbs.length > 0 && Array.from(colCbs).every(function(c) { return c.checked; });
|
||
});
|
||
}
|
||
function syncAll() { syncRowToggles(); syncColToggles(); }
|
||
syncAll();
|
||
})();
|
||
</script>
|
||
{{end}} |