show Keycloak username on the users table and user detail page

Backend already mirrors preferred_username from OIDC login and KC
sync; just surface it in the two frontend views.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 13:17:58 -07:00
parent 820ef56d0f
commit e316bb8038
3 changed files with 18 additions and 0 deletions

View File

@@ -32,6 +32,20 @@ export function getUsersColumns(options?: UsersColumnsOptions): ColumnDef<User,
<span className="text-muted-foreground">{row.getValue("email")}</span>
),
},
{
accessorKey: "username",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Username" />
),
cell: ({ row }) => {
const username = row.getValue("username") as string | null;
return username ? (
<span className="font-mono text-sm text-muted-foreground">{username}</span>
) : (
<span className="text-muted-foreground">--</span>
);
},
},
{
id: "projects",
header: "Projects",

View File

@@ -17,6 +17,7 @@ export interface User {
email: string;
enabled: boolean;
keycloak_id: string | null;
username: string | null; // Keycloak preferred_username
sponsor_project_key: string | null;
unsponsored_since: string | null;
projects: ProjectMembership[];

View File

@@ -246,6 +246,9 @@ export default function UserDetailPage() {
</div>
<p className="text-sm text-muted-foreground">{user.email}</p>
<div className="mt-1 flex items-center gap-3 text-xs text-muted-foreground">
{user.username && (
<span className="font-mono">{user.username}</span>
)}
<span>Member since {user.created_at ? format(parseISO(user.created_at), "MMM d, yyyy") : "—"}</span>
{kcStatus?.enabled && (kcStatus.frontend_url || kcStatus.url) && kcStatus.realm && user.keycloak_id && (
<a