1
0
forked from baron/baron-sso

안내 메세지 및 실제 이름 표시

This commit is contained in:
2026-02-05 14:46:58 +09:00
parent ea66671d44
commit beebb3f7e6
2 changed files with 112 additions and 86 deletions

View File

@@ -173,90 +173,106 @@ function ClientConsentsPage() {
Loading consents...
</CardContent>
)}
<Table>
<TableHeader>
<TableRow>
<TableHead>User</TableHead>
<TableHead>Status</TableHead>
<TableHead>Granted Scopes</TableHead>
<TableHead>Last Authenticated</TableHead>
<TableHead className="text-right">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={`${row.subject}-${row.clientId}`}>
<TableCell>
<div className="flex items-center gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-xs font-bold text-primary">
{row.subject.slice(0, 2).toUpperCase()}
</div>
<div className="flex flex-col">
<span className="text-sm font-semibold">
{row.clientName || "Subject"}
</span>
<span className="text-xs text-muted-foreground">
{row.subject}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge variant="success">Active</Badge>
</TableCell>
<TableCell>
<div className="flex flex-wrap gap-1">
{row.grantedScopes.map((scope) => (
<Badge
key={scope}
variant="muted"
className="border bg-muted/40 text-foreground"
>
{scope}
</Badge>
))}
</div>
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{row.authenticatedAt || "-"}
</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
className="text-destructive"
onClick={() =>
revokeMutation.mutate({ subject: row.subject })
}
>
Revoke
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<CardContent className="flex items-center justify-between border-t border-border bg-muted/10 px-6 py-4 text-sm text-muted-foreground">
<p>
Showing <span className="font-semibold text-foreground">1</span> to{" "}
<span className="font-semibold text-foreground">4</span> of{" "}
<span className="font-semibold text-foreground">1,250</span> users
</p>
<div className="flex gap-2">
<Button variant="outline" size="icon" disabled>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button size="sm">1</Button>
<Button variant="ghost" size="sm">
2
</Button>
<Button variant="ghost" size="sm">
3
</Button>
<Button variant="outline" size="icon">
<ChevronRight className="h-4 w-4" />
</Button>
{subject.length === 0 && !isLoading && !error ? (
<div className="flex flex-col items-center justify-center py-16 text-center text-muted-foreground">
<Search className="mb-4 h-12 w-12 opacity-20" />
<h3 className="mb-1 text-lg font-semibold text-foreground"> </h3>
<p className="max-w-sm text-sm">
.<br/>
ID, , .
</p>
</div>
</CardContent>
) : (
<>
<Table>
<TableHeader>
<TableRow>
<TableHead>User</TableHead>
<TableHead>Status</TableHead>
<TableHead>Granted Scopes</TableHead>
<TableHead>Last Authenticated</TableHead>
<TableHead className="text-right">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.length === 0 && !isLoading ? (
<TableRow>
<TableCell colSpan={5} className="h-24 text-center">
.
</TableCell>
</TableRow>
) : (
rows.map((row) => (
<TableRow key={`${row.subject}-${row.clientId}`}>
<TableCell>
<div className="flex items-center gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-xs font-bold text-primary">
{(row.userName || row.subject).slice(0, 2).toUpperCase()}
</div>
<div className="flex flex-col">
<span className="text-sm font-semibold">
{row.userName || "Subject"}
</span>
<span className="text-xs text-muted-foreground">
{row.subject}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge variant="success">Active</Badge>
</TableCell>
<TableCell>
<div className="flex flex-wrap gap-1">
{row.grantedScopes.map((scope) => (
<Badge
key={scope}
variant="muted"
className="border bg-muted/40 text-foreground"
>
{scope}
</Badge>
))}
</div>
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{row.authenticatedAt || "-"}
</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
className="text-destructive"
onClick={() =>
revokeMutation.mutate({ subject: row.subject })
}
>
Revoke
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
<CardContent className="flex items-center justify-between border-t border-border bg-muted/10 px-6 py-4 text-sm text-muted-foreground">
<p>
Showing <span className="font-semibold text-foreground">{rows.length > 0 ? 1 : 0}</span> to{" "}
<span className="font-semibold text-foreground">{rows.length}</span> of{" "}
<span className="font-semibold text-foreground">{rows.length}</span> users
</p>
<div className="flex gap-2">
<Button variant="outline" size="icon" disabled>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button size="sm" disabled={rows.length === 0}>1</Button>
<Button variant="outline" size="icon" disabled>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</CardContent>
</>
)}
</Card>
<div className="grid gap-6 md:grid-cols-3">
@@ -265,7 +281,7 @@ function ClientConsentsPage() {
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Active Grants
</p>
<CardTitle className="text-2xl font-black">1,250</CardTitle>
<CardTitle className="text-2xl font-black">{rows.length}</CardTitle>
</CardHeader>
</Card>
<Card className="glass-panel">
@@ -273,7 +289,9 @@ function ClientConsentsPage() {
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Total Scopes Issued
</p>
<CardTitle className="text-2xl font-black">4,812</CardTitle>
<CardTitle className="text-2xl font-black">
{rows.reduce((acc, row) => acc + row.grantedScopes.length, 0)}
</CardTitle>
</CardHeader>
</Card>
<Card className="glass-panel">
@@ -281,7 +299,14 @@ function ClientConsentsPage() {
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Avg. Scopes per User
</p>
<CardTitle className="text-2xl font-black">3.8</CardTitle>
<CardTitle className="text-2xl font-black">
{rows.length > 0
? (
rows.reduce((acc, row) => acc + row.grantedScopes.length, 0) /
rows.length
).toFixed(1)
: "0.0"}
</CardTitle>
</CardHeader>
</Card>
</div>

View File

@@ -49,6 +49,7 @@ export type ClientUpsertRequest = {
export type ConsentSummary = {
subject: string;
userName?: string;
clientId: string;
clientName?: string;
grantedScopes: string[];