1
0
forked from baron/baron-sso

Merge branch 'dev' into feature/tenant-user-list-ui-improvement

This commit is contained in:
2026-05-14 11:23:57 +09:00
37 changed files with 1080 additions and 765 deletions

View File

@@ -27,6 +27,10 @@ import {
TableHeader,
TableRow,
} from "../../components/ui/table";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import type { AuditLog } from "../../lib/adminApi";
import { fetchAuditLogs } from "../../lib/adminApi";
import { t } from "../../lib/i18n";
@@ -254,8 +258,8 @@ function AuditLogsPage() {
))
)}
</div>
<div className="flex-1 rounded-md border overflow-hidden flex flex-col">
<div className="flex-1 overflow-auto relative custom-scrollbar">
<div className={commonTableShellClass}>
<div className={commonTableViewportClass}>
<Table className="table-fixed">
<TableHeader className="sticky top-0 z-10 bg-secondary shadow-sm">
<TableRow>

View File

@@ -29,10 +29,19 @@ import {
import * as React from "react";
import { Link, useNavigate } from "react-router-dom";
import {
type SortConfig,
type SortResolverMap,
SortableTableHead,
sortableTableHeadBaseClassName,
sortableTableHeaderClassName,
} from "../../../../../common/core/components/sort";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../../common/ui/table";
import {
sortItems,
toggleSort,
type SortConfig,
type SortResolverMap,
} from "../../../../../common/core/utils";
import { RoleGuard } from "../../../components/auth/RoleGuard";
import { Badge } from "../../../components/ui/badge";
@@ -257,7 +266,10 @@ function TenantListPage() {
const [selectedIds, setSelectedIds] = React.useState<string[]>([]);
const [search, setSearch] = React.useState("");
const [sortConfig, setSortConfig] =
React.useState<SortConfig<TenantSortKey> | null>(null);
React.useState<SortConfig<TenantSortKey> | null>({
key: "createdAt",
direction: "desc",
});
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
const [importMessage, setImportMessage] = React.useState("");
const [previewRows, setPreviewRows] = React.useState<
@@ -444,9 +456,8 @@ function TenantListPage() {
}
if (
profile &&
profileRole === "tenant_admin" &&
(profile.manageableTenants?.length ?? 0) <= 1
(profile?.manageableTenants?.length ?? 0) <= 1
) {
return null;
}
@@ -487,17 +498,6 @@ function TenantListPage() {
setSortConfig((current) => toggleSort(current, key));
};
const getSortIcon = (key: TenantSortKey) => {
if (!sortConfig || sortConfig.key !== key) {
return <ArrowUpDown size={14} className="ml-1 opacity-50" />;
}
return sortConfig.direction === "asc" ? (
<ArrowUp size={14} className="ml-1" />
) : (
<ArrowDown size={14} className="ml-1" />
);
};
const deletableTenants = React.useMemo(
() => allTenants.filter((tenant) => !isSeedTenant(tenant)),
[allTenants],

View File

@@ -24,6 +24,15 @@ import {
sortItems,
toggleSort,
} from "../../../../common/core/utils";
import {
SortableTableHead,
sortableTableHeadBaseClassName,
sortableTableHeaderClassName,
} from "../../../../common/core/components/sort";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { Button } from "../../components/ui/button";
import {
Card,
@@ -294,17 +303,6 @@ function UserListPage() {
setSortConfig((current) => toggleSort(current, key));
};
const getSortIcon = (key: UserSortKey) => {
if (!sortConfig || sortConfig.key !== key) {
return <ArrowUpDown size={14} className="ml-1 opacity-50" />;
}
return sortConfig.direction === "asc" ? (
<ArrowUp size={14} className="ml-1" />
) : (
<ArrowDown size={14} className="ml-1" />
);
};
const total = query.data?.total ?? 0;
const totalPages = Math.ceil(total / limit);
const canPromoteSuperAdmin = isSuperAdminRole(profile?.role);
@@ -603,12 +601,14 @@ function UserListPage() {
</div>
)}
<div className="flex-1 rounded-md border overflow-hidden flex flex-col">
<div className="flex-1 overflow-auto relative custom-scrollbar">
<div className={commonTableShellClass}>
<div className={commonTableViewportClass}>
<Table>
<TableHeader className="sticky top-0 z-10 bg-secondary shadow-sm">
<TableHeader className={sortableTableHeaderClassName}>
<TableRow>
<TableHead className="w-12">
<TableHead
className={`${sortableTableHeadBaseClassName} w-12`}
>
<input
type="checkbox"
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary cursor-pointer"
@@ -689,34 +689,30 @@ function UserListPage() {
{userSchema.map(
(field) =>
visibleColumns[field.key] !== false && (
<TableHead
<SortableTableHead
key={field.key}
className="whitespace-nowrap uppercase cursor-pointer hover:bg-muted/50 transition-colors"
onClick={() => requestSort(field.key)}
>
<div className="flex items-center">
{field.label}
{getSortIcon(field.key)}
</div>
</TableHead>
className="whitespace-nowrap"
label={field.label}
onSort={requestSort}
sortConfig={sortConfig}
sortKey={field.key}
/>
),
)}
<TableHead
className="whitespace-nowrap cursor-pointer hover:bg-muted/50 transition-colors"
onClick={() => requestSort("createdAt")}
>
<div className="flex items-center">
{t("ui.admin.users.list.table.created", "CREATED")}
{getSortIcon("createdAt")}
</div>
</TableHead>
<SortableTableHead
className="whitespace-nowrap"
label={t("ui.admin.users.list.table.created", "CREATED")}
onSort={requestSort}
sortConfig={sortConfig}
sortKey="createdAt"
/>
</TableRow>
</TableHeader>
<TableBody>
{query.isLoading && (
<TableRow>
<TableCell
colSpan={6 + userSchema.length}
colSpan={7 + userSchema.length}
className="h-24 text-center"
>
{t("msg.common.loading", "로딩 중...")}
@@ -726,7 +722,7 @@ function UserListPage() {
{!query.isLoading && items.length === 0 && (
<TableRow>
<TableCell
colSpan={6 + userSchema.length}
colSpan={7 + userSchema.length}
className="h-24 text-center"
>
{t(