1
0
forked from baron/baron-sso

chore: consolidate local integration changes

This commit is contained in:
2026-06-09 21:03:05 +09:00
parent aa2848c3b6
commit 1341f07ef9
158 changed files with 10995 additions and 1490 deletions

View File

@@ -13,6 +13,7 @@ import {
ChevronDown,
FileDown,
FileSpreadsheet,
Key,
LayoutDashboard,
Plus,
RefreshCw,
@@ -117,7 +118,7 @@ type UserSchemaField = {
type UserSortKey = string;
const USER_ROW_ESTIMATED_HEIGHT = 64;
const USER_ROW_OVERSCAN = 20;
const USER_ROW_OVERSCAN = 2;
const USER_TABLE_VIEWPORT_ESTIMATED_HEIGHT = 640;
const userFixedColumnWidths = [48, 160, 220, 160, 260, 170, 160, 220] as const;
const userMetadataColumnWidth = 160;
@@ -150,6 +151,52 @@ function assignableSystemRoleValue(role?: string | null) {
return isSuperAdminRole(role) ? "super_admin" : "user";
}
function collectAdditionalTenantLabels(user: UserSummary) {
const primaryKeys = new Set(
[user.tenant?.id, user.tenant?.slug, user.tenantSlug]
.filter((value): value is string => Boolean(value))
.map((value) => value.toLowerCase()),
);
const labels: string[] = [];
const seen = new Set<string>();
const addLabel = (
tenantId?: unknown,
tenantSlug?: unknown,
tenantName?: unknown,
) => {
const id = typeof tenantId === "string" ? tenantId.trim() : "";
const slug = typeof tenantSlug === "string" ? tenantSlug.trim() : "";
const name = typeof tenantName === "string" ? tenantName.trim() : "";
const key = (id || slug || name).toLowerCase();
if (!key || primaryKeys.has(key) || seen.has(key)) {
return;
}
seen.add(key);
labels.push(name || slug || id);
};
for (const tenant of user.joinedTenants ?? []) {
addLabel(tenant.id, tenant.slug, tenant.name);
}
const appointments = user.metadata?.additionalAppointments;
if (Array.isArray(appointments)) {
for (const appointment of appointments) {
if (!appointment || typeof appointment !== "object") {
continue;
}
const value = appointment as Record<string, unknown>;
addLabel(
value.tenantId,
value.tenantSlug ?? value.slug,
value.tenantName ?? value.name,
);
}
}
return labels;
}
function normalizeUserTableRect(rect: Rect, fallbackWidth: number): Rect {
return {
width: rect.width > 0 ? rect.width : fallbackWidth,
@@ -420,7 +467,7 @@ function UserListPage() {
name_email: (user) =>
`${user.name ?? ""} ${user.email ?? ""} ${user.phone ?? ""}`,
tenant_dept: (user) =>
`${user.tenant?.name ?? user.tenantSlug ?? ""} ${user.department ?? ""}`,
`${user.tenant?.name ?? user.tenantSlug ?? ""} ${collectAdditionalTenantLabels(user).join(" ")} ${user.department ?? ""}`,
},
),
[userSchema],
@@ -640,6 +687,15 @@ function UserListPage() {
<RefreshCw size={16} />
{t("ui.common.refresh", "새로고침")}
</Button>
<Button asChild variant="outline" size="sm" className="h-9 gap-2">
<Link to="/users/custom-claims">
<Key size={16} />
{t(
"ui.admin.users.global_custom_claims.title",
"전역 Claim 설정",
)}
</Link>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
@@ -963,6 +1019,8 @@ function UserListPage() {
virtualRows.map((virtualRow) => {
const user = items[virtualRow.index];
if (!user) return null;
const additionalTenantLabels =
collectAdditionalTenantLabels(user);
return (
<TableRow
@@ -1102,6 +1160,18 @@ function UserListPage() {
{user.department}
</span>
)}
{additionalTenantLabels.length > 0 && (
<div className="flex flex-wrap gap-1">
{additionalTenantLabels.map((label) => (
<span
key={label}
className="max-w-40 truncate rounded border bg-muted/40 px-1.5 py-0.5 text-xs text-muted-foreground"
>
{label}
</span>
))}
</div>
)}
</div>
</TableCell>
{/* Dynamic Metadata Cells */}