forked from baron/baron-sso
안내 메세지 및 실제 이름 표시
This commit is contained in:
@@ -173,90 +173,106 @@ function ClientConsentsPage() {
|
|||||||
Loading consents...
|
Loading consents...
|
||||||
</CardContent>
|
</CardContent>
|
||||||
)}
|
)}
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
{subject.length === 0 && !isLoading && !error ? (
|
||||||
<TableRow>
|
<div className="flex flex-col items-center justify-center py-16 text-center text-muted-foreground">
|
||||||
<TableHead>User</TableHead>
|
<Search className="mb-4 h-12 w-12 opacity-20" />
|
||||||
<TableHead>Status</TableHead>
|
<h3 className="mb-1 text-lg font-semibold text-foreground">사용자 검색 필요</h3>
|
||||||
<TableHead>Granted Scopes</TableHead>
|
<p className="max-w-sm text-sm">
|
||||||
<TableHead>Last Authenticated</TableHead>
|
보안상의 이유로 전체 목록은 제공되지 않습니다.<br/>
|
||||||
<TableHead className="text-right">Action</TableHead>
|
사용자 ID, 이메일, 또는 이름으로 검색하여 동의 내역을 확인하세요.
|
||||||
</TableRow>
|
</p>
|
||||||
</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>
|
|
||||||
</div>
|
</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>
|
</Card>
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-3">
|
<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">
|
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
||||||
Active Grants
|
Active Grants
|
||||||
</p>
|
</p>
|
||||||
<CardTitle className="text-2xl font-black">1,250</CardTitle>
|
<CardTitle className="text-2xl font-black">{rows.length}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="glass-panel">
|
<Card className="glass-panel">
|
||||||
@@ -273,7 +289,9 @@ function ClientConsentsPage() {
|
|||||||
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
||||||
Total Scopes Issued
|
Total Scopes Issued
|
||||||
</p>
|
</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>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="glass-panel">
|
<Card className="glass-panel">
|
||||||
@@ -281,7 +299,14 @@ function ClientConsentsPage() {
|
|||||||
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
||||||
Avg. Scopes per User
|
Avg. Scopes per User
|
||||||
</p>
|
</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>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export type ClientUpsertRequest = {
|
|||||||
|
|
||||||
export type ConsentSummary = {
|
export type ConsentSummary = {
|
||||||
subject: string;
|
subject: string;
|
||||||
|
userName?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientName?: string;
|
clientName?: string;
|
||||||
grantedScopes: string[];
|
grantedScopes: string[];
|
||||||
|
|||||||
Reference in New Issue
Block a user