inou/portal/templates/permissions.tmpl

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}}/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>
<!-- 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}} &nbsp;·&nbsp;
<span class="mono">w</span> = {{.T.op_write_desc}} &nbsp;·&nbsp;
<span class="mono">d</span> = {{.T.op_delete_desc}} &nbsp;·&nbsp;
<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}}