inou/portal/templates/permissions.tmpl

114 lines
5.5 KiB
Cheetah

{{define "permissions"}}
<div class="page-container">
<div class="page-card" style="max-width: 640px; margin: 0 auto;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px;">
<div>
<h1>{{.T.permissions_title}}</h1>
<p class="intro">{{.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}}
<div style="margin-bottom: 32px;">
<h3>{{.T.current_access}}</h3>
<div id="grantees-list">
{{if .Grantees}}
{{range .Grantees}}
<div style="display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid var(--border);">
<div>
<span style="font-weight: 500;">{{.Name}}</span>
<span style="margin-left: 8px; font-size: 0.85rem; color: var(--text-muted);">{{.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}}/rbac/{{.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>
<div style="border-top: 1px solid var(--border); padding-top: 32px;">
<h3>{{.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"><input type="checkbox" name="op_r" value="1" checked disabled><span>{{.T.op_read}}</span></label>
<label class="checkbox-label"><input type="checkbox" name="op_w" value="1"><span>{{.T.op_write}}</span></label>
<label class="checkbox-label"><input type="checkbox" name="op_d" value="1"><span>{{.T.op_delete}}</span></label>
<label class="checkbox-label"><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>
<div style="border-top: 1px solid var(--border); padding-top: 32px; margin-top: 32px;">
<h3>{{.T.role_descriptions}}</h3>
<div style="display: grid; gap: 16px;">
{{range .Roles}}
<div style="padding: 12px 16px; background: var(--bg-secondary); border-radius: 8px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="font-weight: 500;">{{.Name}}</span>
<code style="font-size: 0.8rem;">{{.Ops}}</code>
</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-secondary); 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);">
<code>r</code> = {{.T.op_read_desc}} ·
<code>w</code> = {{.T.op_write_desc}} ·
<code>d</code> = {{.T.op_delete_desc}} ·
<code>m</code> = {{.T.op_manage_desc}}
</p>
</div>
</div>
</div>
</div>
<script>
function updateOpsFromRole(select) {
var customGroup = document.getElementById('custom-ops-group');
customGroup.style.display = select.value === 'custom' ? '' : 'none';
}
</script>
{{end}}