151 lines
7.0 KiB
Cheetah
151 lines
7.0 KiB
Cheetah
{{define "permissions"}}
|
|
<div class="sg-container" style="justify-content: center;">
|
|
|
|
<div style="flex: 1; display: flex; align-items: flex-start; padding-top: 5vh; justify-content: center;">
|
|
<div class="data-card" style="padding: 48px; max-width: 640px; width: 100%;">
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px;">
|
|
<div>
|
|
<h1 style="font-size: 2rem; font-weight: 700; margin-bottom: 8px;">{{.T.permissions_title}}</h1>
|
|
<p style="color: var(--text-muted); font-weight: 300;">{{.T.permissions_subtitle}}</p>
|
|
</div>
|
|
<a href="/dossier/{{.TargetDossier.DossierID}}" class="btn btn-secondary btn-small">← {{.T.back}}</a>
|
|
</div>
|
|
|
|
{{if .Error}}
|
|
<div class="error">{{.Error}}</div>
|
|
{{end}}
|
|
|
|
{{if .Success}}
|
|
<div class="success">{{.Success}}</div>
|
|
{{end}}
|
|
|
|
<!-- Current Access List -->
|
|
<div style="margin-bottom: 32px;">
|
|
<h3 style="font-size: 1.1rem; font-weight: 600; margin-bottom: 16px;">{{.T.current_access}}</h3>
|
|
|
|
<div id="grantees-list">
|
|
{{if .Grantees}}
|
|
{{range .Grantees}}
|
|
<div class="data-row" style="display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid var(--border-light);">
|
|
<div>
|
|
<span style="font-weight: 500;">{{.Name}}</span>
|
|
<span class="badge" style="margin-left: 8px;">{{.Role}}</span>
|
|
<span style="color: var(--text-muted); font-size: 0.85rem; margin-left: 8px;">{{.Ops}}</span>
|
|
</div>
|
|
<div style="display: flex; gap: 8px;">
|
|
<a href="/dossier/{{$.TargetDossier.DossierID}}/access/{{.GranteeID}}" class="btn btn-secondary btn-small">Edit</a>
|
|
<form action="/dossier/{{$.TargetDossier.DossierID}}/permissions" method="POST" style="display: inline;">
|
|
<input type="hidden" name="action" value="revoke">
|
|
<input type="hidden" name="grantee_id" value="{{.GranteeID}}">
|
|
<button type="submit" class="btn btn-danger btn-small" onclick="return confirm('Revoke access for {{.Name}}?')">{{$.T.revoke}}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{else}}
|
|
<p style="color: var(--text-muted); padding: 16px;">{{.T.no_grantees}}</p>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grant Access Form -->
|
|
<div style="border-top: 1px solid var(--border-light); padding-top: 32px;">
|
|
<h3 style="font-size: 1.1rem; font-weight: 600; margin-bottom: 16px;">{{.T.grant_access}}</h3>
|
|
|
|
<form action="/dossier/{{.TargetDossier.DossierID}}/permissions" method="POST">
|
|
<input type="hidden" name="action" value="grant">
|
|
|
|
<div class="form-group">
|
|
<label>{{.T.person_email}}</label>
|
|
<input type="email" name="email" required placeholder="someone@example.com" autofocus>
|
|
<small style="color: var(--text-muted);">{{.T.person_email_hint}}</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>{{.T.person_name}}</label>
|
|
<input type="text" name="name" required placeholder="First Last">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>{{.T.role}}</label>
|
|
<select name="role" required onchange="updateOpsFromRole(this)">
|
|
<option value="">{{.T.select_role}}</option>
|
|
{{range .Roles}}
|
|
<option value="{{.Name}}" data-ops="{{.Ops}}">{{.Name}} — {{.Description}}</option>
|
|
{{end}}
|
|
<option value="custom" data-ops="r">{{.T.custom_role}}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group" id="custom-ops-group" style="display: none;">
|
|
<label>{{.T.permissions}}</label>
|
|
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
|
<input type="checkbox" name="op_r" value="1" checked disabled>
|
|
<span>{{.T.op_read}}</span>
|
|
</label>
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
|
<input type="checkbox" name="op_w" value="1">
|
|
<span>{{.T.op_write}}</span>
|
|
</label>
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
|
<input type="checkbox" name="op_d" value="1">
|
|
<span>{{.T.op_delete}}</span>
|
|
</label>
|
|
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
|
<input type="checkbox" name="op_m" value="1">
|
|
<span>{{.T.op_manage}}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display: flex; gap: 12px; margin-top: 24px;">
|
|
<a href="/dossier/{{.TargetDossier.DossierID}}" class="btn btn-secondary" style="flex: 1; text-align: center;">{{.T.cancel}}</a>
|
|
<button type="submit" class="btn btn-primary" style="flex: 1;">{{.T.grant}}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Role Descriptions -->
|
|
<div style="border-top: 1px solid var(--border-light); padding-top: 32px; margin-top: 32px;">
|
|
<h3 style="font-size: 1.1rem; font-weight: 600; margin-bottom: 16px;">{{.T.role_descriptions}}</h3>
|
|
<div style="display: grid; gap: 16px;">
|
|
{{range .Roles}}
|
|
<div style="padding: 12px 16px; background: var(--bg-muted); border-radius: 8px;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
<span style="font-weight: 500;">{{.Name}}</span>
|
|
<span class="badge mono" style="font-size: 0.8rem;">{{.Ops}}</span>
|
|
</div>
|
|
<p style="color: var(--text-muted); font-size: 0.9rem; margin-top: 4px;">{{.Description}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div style="margin-top: 24px; padding: 16px; background: var(--bg-muted); border-radius: 8px;">
|
|
<p style="font-size: 0.9rem; margin-bottom: 8px;"><strong>{{.T.ops_legend}}</strong></p>
|
|
<p style="font-size: 0.85rem; color: var(--text-muted);">
|
|
<span class="mono">r</span> = {{.T.op_read_desc}} ·
|
|
<span class="mono">w</span> = {{.T.op_write_desc}} ·
|
|
<span class="mono">d</span> = {{.T.op_delete_desc}} ·
|
|
<span class="mono">m</span> = {{.T.op_manage_desc}}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function updateOpsFromRole(select) {
|
|
const customGroup = document.getElementById('custom-ops-group');
|
|
const option = select.options[select.selectedIndex];
|
|
|
|
if (select.value === 'custom') {
|
|
customGroup.style.display = '';
|
|
} else {
|
|
customGroup.style.display = 'none';
|
|
}
|
|
}
|
|
</script>
|
|
{{end}}
|