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:
@@ -34,7 +34,8 @@
|
||||
v-for="role in user.roles"
|
||||
:key="role.roleid"
|
||||
class="badge"
|
||||
:class="role.rolename === 'admin' ? 'badge-primary' : 'badge-secondary'"
|
||||
:class="roleBadgeClass(role)"
|
||||
:style="roleBadgeStyle(role)"
|
||||
>
|
||||
{{ role.rolename }}
|
||||
</span>
|
||||
@@ -83,7 +84,7 @@
|
||||
<tbody>
|
||||
<tr v-for="role in roles" :key="role.roleid">
|
||||
<td>
|
||||
<span class="badge" :class="role.rolename === 'admin' ? 'badge-primary' : 'badge-secondary'">
|
||||
<span class="badge" :class="roleBadgeClass(role)" :style="roleBadgeStyle(role)">
|
||||
{{ role.rolename }}
|
||||
</span>
|
||||
</td>
|
||||
@@ -214,6 +215,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Badge color</label>
|
||||
<ColorSwatchPicker v-model="roleForm.color" />
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="!editingRole?.isadmin">
|
||||
<label>Permissions</label>
|
||||
<p class="text-muted" style="font-size: 0.85rem; margin: 0 0 0.5rem 0">
|
||||
@@ -289,6 +295,9 @@ import { ref, reactive, onMounted } from 'vue'
|
||||
import { usersApi } from '../../api'
|
||||
import { useAuthStore } from '../../stores/auth'
|
||||
import { apiError } from '../../utils/apiError'
|
||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||
|
||||
const DEFAULT_ROLE_COLOR = '#0d6efd'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const currentUserId = authStore.user?.userid
|
||||
@@ -323,9 +332,32 @@ const userForm = reactive({
|
||||
const roleForm = reactive({
|
||||
rolename: '',
|
||||
description: '',
|
||||
color: DEFAULT_ROLE_COLOR,
|
||||
permissions: []
|
||||
})
|
||||
|
||||
// Role badge: use the role's own color when set (contrast-aware text), else
|
||||
// fall back to the legacy admin=primary / other=gray classes.
|
||||
function isLightColor(color) {
|
||||
if (!color) return false
|
||||
const hex = color.replace('#', '')
|
||||
if (hex.length !== 6) return false
|
||||
const r = parseInt(hex.slice(0, 2), 16)
|
||||
const g = parseInt(hex.slice(2, 4), 16)
|
||||
const b = parseInt(hex.slice(4, 6), 16)
|
||||
return (r * 299 + g * 587 + b * 114) / 1000 > 155
|
||||
}
|
||||
|
||||
function roleBadgeClass(role) {
|
||||
if (role.color) return ''
|
||||
return role.rolename === 'admin' ? 'badge-primary' : 'badge-secondary'
|
||||
}
|
||||
|
||||
function roleBadgeStyle(role) {
|
||||
if (!role.color) return null
|
||||
return { background: role.color, color: isLightColor(role.color) ? '#000' : '#fff' }
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -459,6 +491,7 @@ function editRole(role) {
|
||||
editingRole.value = role
|
||||
roleForm.rolename = role.rolename
|
||||
roleForm.description = role.description || ''
|
||||
roleForm.color = role.color || DEFAULT_ROLE_COLOR
|
||||
roleForm.permissions = role.permissions ? [...role.permissions] : []
|
||||
}
|
||||
|
||||
@@ -467,6 +500,7 @@ function closeRoleModal() {
|
||||
editingRole.value = null
|
||||
roleForm.rolename = ''
|
||||
roleForm.description = ''
|
||||
roleForm.color = DEFAULT_ROLE_COLOR
|
||||
roleForm.permissions = []
|
||||
}
|
||||
|
||||
@@ -476,6 +510,7 @@ async function saveRole() {
|
||||
try {
|
||||
const data = {
|
||||
description: roleForm.description,
|
||||
color: roleForm.color,
|
||||
permissions: roleForm.permissions
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user