Files
ACORE/templates/partials/documents-acl.html
T
lokses 5800f79eec documents: иерархия отделов и ACL на папки/файлы с people-picker.
В /documents/ добавлено дерево департаментов, права view/edit через DocumentAccessGrant и выдача доступа сотрудникам/отделам/группам.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 17:21:01 +03:00

123 lines
7.2 KiB
HTML

{% load i18n %}
<div class="docs-acl" style="border:1px solid var(--color-border); border-radius:0.5rem; padding:0.75rem; margin-top:0.75rem; background:var(--color-muted);">
<div style="display:flex; align-items:center; gap:0.5rem; margin-bottom:0.5rem;">
<div style="font-weight:600; font-size:0.8125rem; flex:1;">
{% if is_folder %}{% trans "Права на папку" %}{% else %}{% trans "Права на файл" %}{% endif %}
<span style="font-weight:400; color:var(--color-muted-foreground);">· {{ path|default:"«корінь»" }}</span>
</div>
<button type="button" class="nc-file-act" onclick="document.getElementById('docs-acl-panel').innerHTML=''">{% trans "Закрити" %}</button>
</div>
{% if grants %}
<table style="width:100%; border-collapse:collapse; font-size:0.75rem; margin-bottom:0.75rem; background:var(--color-card); border-radius:0.375rem;">
<tbody>
{% for g in grants %}
<tr style="border-bottom:1px solid var(--color-border);">
<td style="padding:0.4rem 0.5rem;">
{% if g.employee %}{{ g.employee.get_full_name|default:g.employee }}
{% elif g.grant_department %}{% trans "Відділ" %}: {{ g.grant_department.name }}
{% elif g.group %}{% trans "Група" %}: {{ g.group.name }}{% endif %}
</td>
<td style="padding:0.4rem 0.5rem; white-space:nowrap;">
{% if g.level == 'edit' %}{% trans "редагування" %}{% else %}{% trans "перегляд" %}{% endif %}
</td>
<td style="padding:0.4rem 0.5rem; text-align:right;">
<form method="post" action="/documents/acl/revoke/"
hx-post="/documents/acl/revoke/" hx-target="#docs-acl-panel" hx-swap="innerHTML"
style="display:inline;">
{% csrf_token %}
<input type="hidden" name="grant_id" value="{{ g.pk }}">
<input type="hidden" name="section" value="{{ section.key }}">
<input type="hidden" name="path" value="{{ sub }}">
{% if section.department_id %}<input type="hidden" name="dept" value="{{ section.department_id }}">{% endif %}
{% if name %}<input type="hidden" name="name" value="{{ name }}">{% endif %}
<button type="submit" class="nc-file-act nc-file-act-danger">{% trans "Забрати" %}</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.5rem;">
{% trans "Явних прав ще немає (діють правила за замовчуванням)." %}
</div>
{% endif %}
<form method="post" action="/documents/acl/grant/"
hx-post="/documents/acl/grant/" hx-target="#docs-acl-panel" hx-swap="innerHTML"
class="docs-acl-form" style="display:flex; flex-direction:column; gap:0.65rem;">
{% csrf_token %}
<input type="hidden" name="section" value="{{ section.key }}">
<input type="hidden" name="path" value="{{ sub }}">
{% if section.department_id %}<input type="hidden" name="dept" value="{{ section.department_id }}">{% endif %}
{% if name %}<input type="hidden" name="name" value="{{ name }}">{% endif %}
<div style="display:flex; flex-wrap:wrap; gap:0.5rem; align-items:center;">
<span style="font-size:0.75rem; color:var(--color-muted-foreground);">{% trans "Кому" %}:</span>
<label style="font-size:0.75rem; display:inline-flex; align-items:center; gap:0.25rem; cursor:pointer;">
<input type="radio" name="subject_type" value="employee" checked
onchange="docsAclSubjectSwitch(this.form, this.value)"> {% trans "Співробітники" %}
</label>
<label style="font-size:0.75rem; display:inline-flex; align-items:center; gap:0.25rem; cursor:pointer;">
<input type="radio" name="subject_type" value="department"
onchange="docsAclSubjectSwitch(this.form, this.value)"> {% trans "Відділ" %}
</label>
<label style="font-size:0.75rem; display:inline-flex; align-items:center; gap:0.25rem; cursor:pointer;">
<input type="radio" name="subject_type" value="group"
onchange="docsAclSubjectSwitch(this.form, this.value)"> {% trans "Група" %}
</label>
</div>
<div data-acl-subj="employee">
{% include 'partials/people-picker-field.html' with label="Співробітники" field_name='employee_ids' options=employee_options selected_ids=empty_ids placeholder="+ Додати співробітника…" %}
</div>
<div data-acl-subj="department" style="display:none;">
<label style="display:block; font-size:0.8125rem; font-weight:500; margin-bottom:0.375rem;">{% trans "Відділ-отримувач" %}</label>
<div class="searchable-select" data-placeholder="{% trans 'Пошук відділу…' %}">
<select name="grant_department_id" hidden>
<option value="">—</option>
{% for d in department_options %}
<option value="{{ d.pk }}">{{ d.label }}</option>
{% endfor %}
</select>
</div>
</div>
<div data-acl-subj="group" style="display:none;">
<label style="display:block; font-size:0.8125rem; font-weight:500; margin-bottom:0.375rem;">{% trans "Група" %}</label>
<div class="searchable-select" data-placeholder="{% trans 'Пошук групи…' %}">
<select name="group_id" hidden>
<option value="">—</option>
{% for g in group_options %}
<option value="{{ g.pk }}">{{ g.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div style="display:flex; flex-wrap:wrap; gap:0.5rem; align-items:flex-end;">
<label style="font-size:0.75rem;">
{% trans "Рівень" %}
<select name="level" class="input" style="display:block; font-size:0.8125rem; margin-top:0.2rem; min-width:9rem;">
<option value="view">{% trans "Перегляд" %}</option>
<option value="edit">{% trans "Редагування" %}</option>
</select>
</label>
<button type="submit" class="btn btn-primary" style="font-size:0.8125rem;">{% trans "Видати" %}</button>
</div>
</form>
</div>
<script>
(function () {
if (typeof initPeoplePickers === 'function') initPeoplePickers(document.getElementById('docs-acl-panel'));
if (typeof initSearchableSelects === 'function') initSearchableSelects(document.getElementById('docs-acl-panel'));
})();
function docsAclSubjectSwitch(form, value) {
form.querySelectorAll('[data-acl-subj]').forEach(function (el) {
el.style.display = el.getAttribute('data-acl-subj') === value ? '' : 'none';
});
}
</script>