forked from baron/baron-sso
feat: implement sticky header and inner scrolling for user list page
This commit is contained in:
@@ -254,8 +254,8 @@ function UserListPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-6 flex flex-col h-[calc(100vh-theme(spacing.32))]">
|
||||||
<header className="flex flex-wrap items-start justify-between gap-4">
|
<header className="flex flex-wrap items-start justify-between gap-4 flex-shrink-0">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center gap-2 text-sm text-[var(--color-muted)]">
|
<div className="flex items-center gap-2 text-sm text-[var(--color-muted)]">
|
||||||
<span>{t("ui.admin.users.list.breadcrumb.section", "Users")}</span>
|
<span>{t("ui.admin.users.list.breadcrumb.section", "Users")}</span>
|
||||||
@@ -353,8 +353,8 @@ function UserListPage() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<Card className="bg-[var(--color-panel)]">
|
<Card className="flex-1 flex flex-col min-h-0 bg-[var(--color-panel)] overflow-hidden">
|
||||||
<CardHeader className="flex flex-row items-center justify-between">
|
<CardHeader className="flex flex-row items-center justify-between flex-shrink-0">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
{t("ui.admin.users.list.registry.title", "User Registry")}
|
{t("ui.admin.users.list.registry.title", "User Registry")}
|
||||||
@@ -368,8 +368,8 @@ function UserListPage() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="flex-1 flex flex-col min-h-0 pt-0">
|
||||||
<div className="mb-6 flex flex-wrap items-center gap-4">
|
<div className="mb-6 flex flex-wrap items-center gap-4 flex-shrink-0">
|
||||||
<div className="relative flex-1 min-w-[240px] max-w-sm">
|
<div className="relative flex-1 min-w-[240px] max-w-sm">
|
||||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||||
<Input
|
<Input
|
||||||
@@ -412,14 +412,15 @@ function UserListPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(errorMsg || fallbackError) && (
|
{(errorMsg || fallbackError) && (
|
||||||
<div className="mb-4 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
<div className="mb-4 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive flex-shrink-0">
|
||||||
{errorMsg ?? fallbackError}
|
{errorMsg ?? fallbackError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="rounded-md border overflow-x-auto">
|
<div className="flex-1 rounded-md border overflow-hidden flex flex-col">
|
||||||
|
<div className="flex-1 overflow-auto relative custom-scrollbar">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader className="sticky top-0 bg-muted/90 backdrop-blur z-10 shadow-sm">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-12">
|
<TableHead className="w-12">
|
||||||
<input
|
<input
|
||||||
@@ -433,7 +434,10 @@ function UserListPage() {
|
|||||||
/>
|
/>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="min-w-[200px]">
|
<TableHead className="min-w-[200px]">
|
||||||
{t("ui.admin.users.list.table.name_email", "NAME / EMAIL")}
|
{t(
|
||||||
|
"ui.admin.users.list.table.name_email",
|
||||||
|
"NAME / EMAIL",
|
||||||
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
{t("ui.admin.users.list.table.role", "ROLE")}
|
{t("ui.admin.users.list.table.role", "ROLE")}
|
||||||
@@ -468,7 +472,7 @@ function UserListPage() {
|
|||||||
{query.isLoading && (
|
{query.isLoading && (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell
|
<TableCell
|
||||||
colSpan={6 + userSchema.length}
|
colSpan={7 + userSchema.length}
|
||||||
className="h-24 text-center"
|
className="h-24 text-center"
|
||||||
>
|
>
|
||||||
{t("msg.common.loading", "로딩 중...")}
|
{t("msg.common.loading", "로딩 중...")}
|
||||||
@@ -478,10 +482,13 @@ function UserListPage() {
|
|||||||
{!query.isLoading && items.length === 0 && (
|
{!query.isLoading && items.length === 0 && (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell
|
<TableCell
|
||||||
colSpan={6 + userSchema.length}
|
colSpan={7 + userSchema.length}
|
||||||
className="h-24 text-center"
|
className="h-24 text-center"
|
||||||
>
|
>
|
||||||
{t("msg.admin.users.list.empty", "검색 결과가 없습니다.")}
|
{t(
|
||||||
|
"msg.admin.users.list.empty",
|
||||||
|
"검색 결과가 없습니다.",
|
||||||
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
@@ -574,6 +581,7 @@ function UserListPage() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Bulk Action Bar */}
|
{/* Bulk Action Bar */}
|
||||||
{selectedUserIds.length > 0 && (
|
{selectedUserIds.length > 0 && (
|
||||||
@@ -639,7 +647,7 @@ function UserListPage() {
|
|||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{totalPages > 1 && (
|
{totalPages > 1 && (
|
||||||
<div className="mt-4 flex items-center justify-end gap-2">
|
<div className="mt-4 flex flex-shrink-0 items-center justify-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
Reference in New Issue
Block a user