Doing a relayout of permissions to make it a bit easier to navigate
This commit is contained in:
1 parent
1549971a13
commit
eba7896893
5 files changed
+137
-23
No files matched your search
@@ -207,6 +207,37 @@ input[type=password] {
|
|||||||
padding: 0 0.4rem;
|
padding: 0 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.perm-matrix {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perm-matrix th,
|
||||||
|
.perm-matrix td {
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perm-matrix th {
|
||||||
|
background: var(--surface-2);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.perm-matrix .perm-scope {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perm-matrix .perm-na {
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
.inline-edit {
|
.inline-edit {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -2,7 +2,32 @@
|
|||||||
<td>{{ r.id }}</td>
|
<td>{{ r.id }}</td>
|
||||||
<td>{{ r.name }}</td>
|
<td>{{ r.name }}</td>
|
||||||
<td>{{ r.description or "" }}</td>
|
<td>{{ r.description or "" }}</td>
|
||||||
<td>{% for perm in r.permissions %}<span class="tag">{{ perm.name }}</span>{% endfor %}</td>
|
<td>
|
||||||
|
{% set role_perm_names = r.permissions | map(attribute='name') | list %}
|
||||||
|
<table class="perm-matrix">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
{% for action in actions %}<th>{{ action }}</th>{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scope in scopes %}
|
||||||
|
<tr>
|
||||||
|
<td class="perm-scope">{{ scope }}</td>
|
||||||
|
{% for action in actions %}
|
||||||
|
{% set key = scope ~ ':' ~ action %}
|
||||||
|
{% if key in perm_map %}
|
||||||
|
<td class="perm-cell">{{ '✓' if key in role_perm_names else '' }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="perm-cell perm-na">—</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
<td class="row-actions">
|
<td class="row-actions">
|
||||||
<button class="btn btn-ghost"
|
<button class="btn btn-ghost"
|
||||||
hx-get="/admin/roles/{{ r.id }}/edit"
|
hx-get="/admin/roles/{{ r.id }}/edit"
|
||||||
|
|||||||
@@ -5,14 +5,36 @@
|
|||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
class="inline-edit">
|
class="inline-edit">
|
||||||
<span class="muted">#{{ r.id }} · {{ r.name }}</span>
|
<span class="muted">#{{ r.id }} · {{ r.name }}</span>
|
||||||
|
{% set role_perm_ids = r.permissions | map(attribute='id') | list %}
|
||||||
<fieldset class="roles-field">
|
<fieldset class="roles-field">
|
||||||
<legend>Permissions</legend>
|
<legend>Permissions</legend>
|
||||||
{% for perm in permissions %}
|
<table class="perm-matrix">
|
||||||
<label class="check">
|
<thead>
|
||||||
<input type="checkbox" name="permission_ids" value="{{ perm.id }}"
|
<tr>
|
||||||
{{ "checked" if perm in r.permissions }}> {{ perm.name }}
|
<th></th>
|
||||||
</label>
|
{% for action in actions %}<th>{{ action }}</th>{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scope in scopes %}
|
||||||
|
<tr>
|
||||||
|
<td class="perm-scope">{{ scope }}</td>
|
||||||
|
{% for action in actions %}
|
||||||
|
{% set key = scope ~ ':' ~ action %}
|
||||||
|
{% if key in perm_map %}
|
||||||
|
{% set p = perm_map[key] %}
|
||||||
|
<td class="perm-cell">
|
||||||
|
<input type="checkbox" name="permission_ids" value="{{ p.id }}"
|
||||||
|
{{ 'checked' if p.id in role_perm_ids }}>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="perm-cell perm-na">—</td>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
<button type="submit" class="btn btn-primary">Save</button>
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
|
|||||||
@@ -12,11 +12,34 @@
|
|||||||
<label>Description <input type="text" name="description"></label>
|
<label>Description <input type="text" name="description"></label>
|
||||||
<fieldset class="roles-field">
|
<fieldset class="roles-field">
|
||||||
<legend>Permissions</legend>
|
<legend>Permissions</legend>
|
||||||
{% for perm in permissions %}
|
{% if scopes %}
|
||||||
<label class="check"><input type="checkbox" name="permission_ids" value="{{ perm.id }}"> {{ perm.name }}</label>
|
<table class="perm-matrix">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
{% for action in actions %}<th>{{ action }}</th>{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scope in scopes %}
|
||||||
|
<tr>
|
||||||
|
<td class="perm-scope">{{ scope }}</td>
|
||||||
|
{% for action in actions %}
|
||||||
|
{% set key = scope ~ ':' ~ action %}
|
||||||
|
{% if key in perm_map %}
|
||||||
|
{% set p = perm_map[key] %}
|
||||||
|
<td class="perm-cell"><input type="checkbox" name="permission_ids" value="{{ p.id }}"></td>
|
||||||
|
{% else %}
|
||||||
|
<td class="perm-cell perm-na">—</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="muted">No permissions defined. Run the seed script.</span>
|
<span class="muted">No permissions defined. Run the seed script.</span>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<button type="submit" class="btn btn-primary">Add role</button>
|
<button type="submit" class="btn btn-primary">Add role</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+27
-14
@@ -9,17 +9,17 @@ from app.crud import audit as audit_crud
|
|||||||
from app.crud import imported_part as imported_part_crud
|
from app.crud import imported_part as imported_part_crud
|
||||||
from app.crud import imported_part_config as imported_part_config_crud
|
from app.crud import imported_part_config as imported_part_config_crud
|
||||||
from app.crud import iso_thread as iso_thread_crud
|
from app.crud import iso_thread as iso_thread_crud
|
||||||
from app.crud import sub_part_type as sub_part_type_crud
|
|
||||||
from app.services import odata as odata_service
|
|
||||||
from app.crud import manufacturer as manufacturer_crud
|
from app.crud import manufacturer as manufacturer_crud
|
||||||
from app.crud import part_type as part_type_crud
|
from app.crud import part_type as part_type_crud
|
||||||
from app.crud import role as role_crud
|
from app.crud import role as role_crud
|
||||||
from app.crud import scope_model as scope_model_crud
|
from app.crud import scope_model as scope_model_crud
|
||||||
|
from app.crud import sub_part_type as sub_part_type_crud
|
||||||
from app.crud import unc_thread as unc_thread_crud
|
from app.crud import unc_thread as unc_thread_crud
|
||||||
from app.crud import user as user_crud
|
from app.crud import user as user_crud
|
||||||
from app.deps import get_current_web_user
|
from app.deps import get_current_web_user
|
||||||
from app.models.unc_thread import UncSeries
|
from app.models.unc_thread import UncSeries
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
|
from app.services import odata as odata_service
|
||||||
from app.web.templates import templates
|
from app.web.templates import templates
|
||||||
|
|
||||||
router = APIRouter(prefix="/admin", include_in_schema=False)
|
router = APIRouter(prefix="/admin", include_in_schema=False)
|
||||||
@@ -39,6 +39,18 @@ def web_require(*perms: str):
|
|||||||
return checker
|
return checker
|
||||||
|
|
||||||
|
|
||||||
|
async def _role_perm_ctx(db: AsyncSession) -> dict:
|
||||||
|
all_perms = await role_crud.list_permissions(db)
|
||||||
|
scopes = sorted({p.name.split(":")[0] for p in all_perms})
|
||||||
|
perm_map = {p.name: p for p in all_perms}
|
||||||
|
return {
|
||||||
|
"permissions": all_perms,
|
||||||
|
"scopes": scopes,
|
||||||
|
"perm_map": perm_map,
|
||||||
|
"actions": ["read", "write", "delete"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_class=HTMLResponse)
|
@router.get("", response_class=HTMLResponse)
|
||||||
async def admin_home():
|
async def admin_home():
|
||||||
return RedirectResponse(url="/admin/users", status_code=status.HTTP_302_FOUND)
|
return RedirectResponse(url="/admin/users", status_code=status.HTTP_302_FOUND)
|
||||||
@@ -190,11 +202,7 @@ async def roles_page(
|
|||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/roles.html",
|
"admin/roles.html",
|
||||||
{
|
{"user": user, "roles": await role_crud.list_roles(db), **(await _role_perm_ctx(db))},
|
||||||
"user": user,
|
|
||||||
"roles": await role_crud.list_roles(db),
|
|
||||||
"permissions": await role_crud.list_permissions(db),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -222,10 +230,7 @@ async def create_role(
|
|||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/_roles_table.html",
|
"admin/_roles_table.html",
|
||||||
{
|
{"roles": await role_crud.list_roles(db), **(await _role_perm_ctx(db))},
|
||||||
"roles": await role_crud.list_roles(db),
|
|
||||||
"permissions": await role_crud.list_permissions(db),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -242,7 +247,7 @@ async def edit_role_row(
|
|||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/_role_row_edit.html",
|
"admin/_role_row_edit.html",
|
||||||
{"r": role, "permissions": await role_crud.list_permissions(db)},
|
{"r": role, **(await _role_perm_ctx(db))},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -256,7 +261,11 @@ async def role_row(
|
|||||||
role = await role_crud.get(db, role_id)
|
role = await role_crud.get(db, role_id)
|
||||||
if role is None:
|
if role is None:
|
||||||
raise HTTPException(status_code=404, detail="Role not found")
|
raise HTTPException(status_code=404, detail="Role not found")
|
||||||
return templates.TemplateResponse(request, "admin/_role_row.html", {"r": role})
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"admin/_role_row.html",
|
||||||
|
{"r": role, **(await _role_perm_ctx(db))},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/roles/{role_id}", response_class=HTMLResponse)
|
@router.post("/roles/{role_id}", response_class=HTMLResponse)
|
||||||
@@ -277,7 +286,11 @@ async def update_role(
|
|||||||
resource_id=role_id,
|
resource_id=role_id,
|
||||||
description=audit_crud.describe_update("Role", role_id, changes),
|
description=audit_crud.describe_update("Role", role_id, changes),
|
||||||
)
|
)
|
||||||
return templates.TemplateResponse(request, "admin/_role_row.html", {"r": role})
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"admin/_role_row.html",
|
||||||
|
{"r": role, **(await _role_perm_ctx(db))},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/roles/{role_id}")
|
@router.delete("/roles/{role_id}")
|
||||||
|
|||||||
Reference in new issue
Block a user