From 1613bd52755450aae972c6c758e7417a3bde3fb4 Mon Sep 17 00:00:00 2001 From: Hyko Date: Wed, 22 Apr 2026 15:08:46 -0400 Subject: [PATCH] feat(admin): add dynamic role color support for user badges --- src/features/admin/pages/UsersPage.client.js | 24 +++++++++++++++++--- src/shared/components/Badge.js | 15 +++++++++--- src/shared/components/Select.js | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/features/admin/pages/UsersPage.client.js b/src/features/admin/pages/UsersPage.client.js index 59c9254..cdb08cb 100644 --- a/src/features/admin/pages/UsersPage.client.js +++ b/src/features/admin/pages/UsersPage.client.js @@ -1,8 +1,8 @@ 'use client'; -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { Card, Table, StatusBadge, Button } from '@zen/core/shared/components'; +import { Card, Table, Badge, StatusBadge, Button } from '@zen/core/shared/components'; import { PencilEdit01Icon } from '@zen/core/shared/icons'; import { useToast } from '@zen/core/toast'; @@ -11,6 +11,7 @@ const UsersPageClient = () => { const toast = useToast(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); + const [roleColorMap, setRoleColorMap] = useState({}); // Pagination state const [pagination, setPagination] = useState({ @@ -55,7 +56,11 @@ const UsersPageClient = () => { key: 'role', label: 'RĂ´le', sortable: true, - render: (user) => , + render: (user) => ( + + {user.role} + + ), skeleton: { height: 'h-6', width: '80px', className: 'rounded-full' } }, { @@ -131,6 +136,19 @@ const UsersPageClient = () => { } }; + useEffect(() => { + fetch('/zen/api/roles', { credentials: 'include' }) + .then(r => r.json()) + .then(data => { + const map = {}; + for (const role of data.roles || []) { + if (role.color) map[role.name.toLowerCase()] = role.color; + } + setRoleColorMap(map); + }) + .catch(() => {}); + }, []); + // Effect to fetch users when sort or pagination change useEffect(() => { fetchUsers(); diff --git a/src/shared/components/Badge.js b/src/shared/components/Badge.js index 682dc58..0f5de41 100644 --- a/src/shared/components/Badge.js +++ b/src/shared/components/Badge.js @@ -6,11 +6,12 @@ const Badge = ({ children, variant = 'default', size = 'sm', + color = null, className = '', ...props }) => { const baseClassName = 'inline-flex items-center font-medium border font-ibm-plex-mono'; - + const variants = { default: 'bg-neutral-100 text-neutral-700 border-neutral-200 dark:bg-neutral-500/10 dark:text-neutral-400 dark:border-neutral-500/20', primary: 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-500/10 dark:text-blue-400 dark:border-blue-500/20', @@ -22,16 +23,24 @@ const Badge = ({ pink: 'bg-pink-100 text-pink-700 border-pink-200 dark:bg-pink-500/10 dark:text-pink-400 dark:border-pink-500/20', orange: 'bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-500/10 dark:text-orange-400 dark:border-orange-500/20' }; - + const sizes = { sm: 'px-[8px] py-[2px] rounded-lg text-[11px]', md: 'px-[8px] py-[2px] rounded-lg text-[11px]', lg: 'px-3 py-1 rounded-lg text-xs' }; + const variantClass = color ? '' : (variants[variant] || variants.default); + const colorStyle = color ? { + backgroundColor: `${color}1a`, + color: color, + borderColor: `${color}33`, + } : {}; + return ( {children} diff --git a/src/shared/components/Select.js b/src/shared/components/Select.js index f0d12ab..c644eac 100644 --- a/src/shared/components/Select.js +++ b/src/shared/components/Select.js @@ -15,7 +15,7 @@ const Select = ({ description, ...props }) => { - const baseSelectClassName = `w-full cursor-pointer px-[10px] py-[7px] rounded text-[13px] focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900/60 dark:border-neutral-700/50 dark:text-white dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20 ${ + const baseSelectClassName = `w-full cursor-pointer px-[10px] py-[7px] rounded-lg text-[13px] focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900/60 dark:border-neutral-700/50 dark:text-white dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20 ${ error ? 'border-red-500/50 dark:border-red-500/50' : '' } ${className}`;