Add per-role badge colors

Role badges rendered gray for everything except admin, with no way to tell
roles apart. Add an optional color per role, matching how statuses and types
carry one: new roles.color column (migration 7d27_roles_color), color threaded
through the role API and the user serializer, and a ColorSwatchPicker in the
role editor. Badges use the role's color with contrast-aware text and fall
back to the old admin/gray classes when unset.
This commit is contained in:
cproudlock
2026-07-20 10:05:58 -04:00
parent 0b247ed96f
commit a7ff882e21
5 changed files with 119 additions and 4 deletions

View File

@@ -270,6 +270,7 @@ def list_roles():
'roleid': r.roleid,
'rolename': r.rolename,
'description': r.description,
'color': r.color,
'usercount': r.users.count(),
'permissions': [p.name for p in r.permissions],
'isadmin': r.rolename == 'admin'
@@ -290,7 +291,8 @@ def create_role():
role = Role(
rolename=data['rolename'],
description=data.get('description')
description=data.get('description'),
color=data.get('color')
)
# Assign permissions
@@ -308,6 +310,7 @@ def create_role():
'roleid': role.roleid,
'rolename': role.rolename,
'description': role.description,
'color': role.color,
'permissions': [p.name for p in role.permissions]
}, message='Role created', http_code=201)
@@ -333,6 +336,12 @@ def update_role(roleid: int):
changes['description'] = {'old': role.description, 'new': data['description']}
role.description = data['description']
# Color is cosmetic - editable on any role, including admin
if 'color' in data:
if data['color'] != role.color:
changes['color'] = {'old': role.color, 'new': data['color']}
role.color = data['color']
# Update permissions
if 'permissions' in data and role.rolename != 'admin':
old_perms = [p.name for p in role.permissions]
@@ -351,6 +360,7 @@ def update_role(roleid: int):
'roleid': role.roleid,
'rolename': role.rolename,
'description': role.description,
'color': role.color,
'permissions': [p.name for p in role.permissions]
}, message='Role updated')
@@ -393,7 +403,7 @@ def user_to_dict(user: User) -> dict:
'mustchangepassword': bool(user.mustchangepassword),
'lastlogindate': user.lastlogindate.isoformat() + 'Z' if user.lastlogindate else None,
'failedlogins': user.failedlogins,
'roles': [{'roleid': r.roleid, 'rolename': r.rolename} for r in user.roles],
'roles': [{'roleid': r.roleid, 'rolename': r.rolename, 'color': r.color} for r in user.roles],
'createddate': user.createddate.isoformat() + 'Z' if user.createddate else None,
'modifieddate': user.modifieddate.isoformat() + 'Z' if user.modifieddate else None
}