refactor(admin): split sso page.tsx into colocated components
Extract types, constants, helpers, and UI pieces (shared LoadingSkeleton/ EmptyState/StatusBadge/CopyButton, SSOConfigFormModal, DeleteConfirmModal, ConnectionTestPanel, SSOConfigCard, SSOUsersTable, SSOInfoSection) into _components/ and _types.ts to bring page.tsx from 1482 LOC to 339 LOC (under the 500 hard cap). Behavior preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
87
admin-compliance/app/sdk/sso/_components/SSOUsersTable.tsx
Normal file
87
admin-compliance/app/sdk/sso/_components/SSOUsersTable.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
'use client'
|
||||
|
||||
import { CheckCircle2, Users, XCircle } from 'lucide-react'
|
||||
import type { SSOUser } from '../_types'
|
||||
import { formatDate } from './helpers'
|
||||
import { EmptyState, LoadingSkeleton } from './shared'
|
||||
|
||||
export function SSOUsersTable({
|
||||
users,
|
||||
loading,
|
||||
}: {
|
||||
users: SSOUser[]
|
||||
loading: boolean
|
||||
}) {
|
||||
if (loading) {
|
||||
return <LoadingSkeleton rows={4} />
|
||||
}
|
||||
|
||||
if (users.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon={<Users className="w-8 h-8 text-slate-400" />}
|
||||
title="Keine SSO-Benutzer"
|
||||
description="Es wurden noch keine Benutzer ueber SSO provisioniert. Benutzer erscheinen hier nach dem ersten Login."
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-slate-200">
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">Name</th>
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">E-Mail</th>
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">Externe ID</th>
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">Gruppen</th>
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">Letzter Login</th>
|
||||
<th className="text-left py-3 px-4 text-sm font-medium text-slate-500">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map(user => (
|
||||
<tr key={user.id} className="border-b border-slate-100 hover:bg-slate-50">
|
||||
<td className="py-3 px-4">
|
||||
<div className="font-medium text-slate-900">{user.display_name}</div>
|
||||
</td>
|
||||
<td className="py-3 px-4 text-sm text-slate-600">{user.email}</td>
|
||||
<td className="py-3 px-4">
|
||||
<span className="text-xs text-slate-500 font-mono">{user.external_id}</span>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{user.groups.length > 0 ? (
|
||||
user.groups.map(group => (
|
||||
<span key={group} className="px-2 py-0.5 bg-slate-100 text-slate-600 rounded text-xs">
|
||||
{group}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span className="text-xs text-slate-400">-</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4 text-sm text-slate-500">
|
||||
{formatDate(user.last_login)}
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
{user.is_active ? (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded-full">
|
||||
<CheckCircle2 className="w-3 h-3" />
|
||||
Aktiv
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-slate-100 text-slate-500 rounded-full">
|
||||
<XCircle className="w-3 h-3" />
|
||||
Inaktiv
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user