1
0
forked from baron/baron-sso

chore: formatting and linting cleanup via code-check

This commit is contained in:
2026-06-02 18:53:56 +09:00
parent bf64f82507
commit f76dd4e60d
10 changed files with 266 additions and 323 deletions

View File

@@ -4,7 +4,6 @@ import { Building2, Sparkles } from "lucide-react";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom"; import { useNavigate, useSearchParams } from "react-router-dom";
import { Button } from "../../../components/ui/button"; import { Button } from "../../../components/ui/button";
import { Checkbox } from "../../../components/ui/checkbox";
import { import {
Card, Card,
CardContent, CardContent,
@@ -12,6 +11,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "../../../components/ui/card"; } from "../../../components/ui/card";
import { Checkbox } from "../../../components/ui/checkbox";
import { Input } from "../../../components/ui/input"; import { Input } from "../../../components/ui/input";
import { Label } from "../../../components/ui/label"; import { Label } from "../../../components/ui/label";
import { Textarea } from "../../../components/ui/textarea"; import { Textarea } from "../../../components/ui/textarea";

View File

@@ -24,8 +24,6 @@ function TenantDetailPage() {
const profileRole = normalizeAdminRole(profile?.role); const profileRole = normalizeAdminRole(profile?.role);
const canAccessSchema = profileRole === "super_admin"; const canAccessSchema = profileRole === "super_admin";
Broadway
const showWorksmobileEntry = canShowWorksmobileEntry(tenantQuery.data);
const isPermissionsTab = location.pathname.includes("/permissions"); const isPermissionsTab = location.pathname.includes("/permissions");
const isOrganizationTab = location.pathname.includes("/organization"); const isOrganizationTab = location.pathname.includes("/organization");

View File

@@ -19,12 +19,13 @@ export type WorksmobileAccessProfile = {
}>; }>;
}; };
export function isWorksmobileExcludedConfig( export function isWorksmobileExcludedConfig(config?: Record<string, unknown>) {
config?: Record<string, unknown>,
) {
const rawValue = config?.worksmobileExcluded; const rawValue = config?.worksmobileExcluded;
return ( return (
rawValue === true || String(rawValue ?? "").trim().toLowerCase() === "true" rawValue === true ||
String(rawValue ?? "")
.trim()
.toLowerCase() === "true"
); );
} }

View File

@@ -80,14 +80,10 @@ describe("tenant org config", () => {
}); });
it("reads, writes, and removes the Worksmobile exclusion flag", () => { it("reads, writes, and removes the Worksmobile exclusion flag", () => {
expect( expect(readTenantOrgConfig({ worksmobileExcluded: true })).toMatchObject({
readTenantOrgConfig({ worksmobileExcluded: true }),
).toMatchObject({
worksmobileExcluded: true, worksmobileExcluded: true,
}); });
expect( expect(readTenantOrgConfig({ worksmobileExcluded: "true" })).toMatchObject({
readTenantOrgConfig({ worksmobileExcluded: "true" }),
).toMatchObject({
worksmobileExcluded: true, worksmobileExcluded: true,
}); });
expect( expect(

View File

@@ -986,14 +986,13 @@ function UserCreatePage() {
} }
> >
<option value=""></option> <option value=""></option>
{getTenantGradeOptions( {getTenantGradeOptions(appointment, tenants).map(
appointment, (grade) => (
tenants, <option key={grade} value={grade}>
).map((grade) => ( {grade}
<option key={grade} value={grade}> </option>
{grade} ),
</option> )}
))}
</select> </select>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">

View File

@@ -82,4 +82,3 @@ func SyncKetoRelations(db *gorm.DB, outbox repository.KetoOutboxRepository) erro
slog.Info("✅ Keto ReBAC synchronization items added to Outbox.") slog.Info("✅ Keto ReBAC synchronization items added to Outbox.")
return nil return nil
} }

View File

@@ -13,8 +13,10 @@ import (
"time" "time"
) )
const HanmacFamilyTenantSlug = "hanmac-family" const (
const worksmobileExcludedConfigKey = "worksmobileExcluded" HanmacFamilyTenantSlug = "hanmac-family"
worksmobileExcludedConfigKey = "worksmobileExcluded"
)
type WorksmobileSyncer interface { type WorksmobileSyncer interface {
EnqueueTenantUpsertIfInScope(ctx context.Context, tenant domain.Tenant) error EnqueueTenantUpsertIfInScope(ctx context.Context, tenant domain.Tenant) error

View File

@@ -1,64 +1,62 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
getHanmacFamilyTenantOrderRank, getHanmacFamilyTenantOrderRank,
orderHanmacFamilyChildren, orderHanmacFamilyChildren,
orderHanmacFamilyTenants, orderHanmacFamilyTenants,
} from "./hanmacFamilyOrder"; } from "./hanmacFamilyOrder";
function tenant(name: string, slug: string) { function tenant(name: string, slug: string) {
return { name, slug }; return { name, slug };
} }
describe("hanmac family organization order", () => { describe("hanmac family organization order", () => {
it("orders the top hanmac-family siblings by policy", () => { it("orders the top hanmac-family siblings by policy", () => {
const ordered = orderHanmacFamilyTenants([ const ordered = orderHanmacFamilyTenants([
tenant("한라산업개발", "halla"), tenant("한라산업개발", "halla"),
tenant("바론그룹", "baron-group"), tenant("바론그룹", "baron-group"),
tenant("한맥기술", "hanmac"), tenant("한맥기술", "hanmac"),
tenant("삼안", "saman"), tenant("삼안", "saman"),
tenant("총괄기획&기술개발센터", "gpdtdc"), tenant("총괄기획&기술개발센터", "gpdtdc"),
]); ]);
expect(ordered.map((item) => item.name)).toEqual([ expect(ordered.map((item) => item.name)).toEqual([
"총괄기획&기술개발센터", "총괄기획&기술개발센터",
"삼안", "삼안",
"한맥기술", "한맥기술",
"바론그룹", "바론그룹",
"한라산업개발", "한라산업개발",
]); ]);
}); });
it("keeps hanmac-family as the root before ordered descendants", () => { it("keeps hanmac-family as the root before ordered descendants", () => {
const family = tenant("한맥가족", "hanmac-family"); const family = tenant("한맥가족", "hanmac-family");
const children = orderHanmacFamilyChildren(family, [ const children = orderHanmacFamilyChildren(family, [
tenant("바론그룹", "baron-group"), tenant("바론그룹", "baron-group"),
tenant("총괄기획&기술개발센터", "gpdtdc"), tenant("총괄기획&기술개발센터", "gpdtdc"),
tenant("삼안", "saman"), tenant("삼안", "saman"),
tenant("한라산업개발", "halla"), tenant("한라산업개발", "halla"),
tenant("한맥기술", "hanmac"), tenant("한맥기술", "hanmac"),
]); ]);
expect([family, ...children].map((item) => item.name)).toEqual([ expect([family, ...children].map((item) => item.name)).toEqual([
"한맥가족", "한맥가족",
"총괄기획&기술개발센터", "총괄기획&기술개발센터",
"삼안", "삼안",
"한맥기술", "한맥기술",
"바론그룹", "바론그룹",
"한라산업개발", "한라산업개발",
]); ]);
}); });
it("does not rank generic technical centers as GPDTDC", () => { it("does not rank generic technical centers as GPDTDC", () => {
expect( expect(
getHanmacFamilyTenantOrderRank( getHanmacFamilyTenantOrderRank(tenant("기술개발센터", "rnd-center")),
tenant("기술개발센터", "rnd-center"), ).toBe(Number.MAX_SAFE_INTEGER);
), });
).toBe(Number.MAX_SAFE_INTEGER);
});
it("ranks Halla as the fifth hanmac-family company", () => { it("ranks Halla as the fifth hanmac-family company", () => {
expect( expect(
getHanmacFamilyTenantOrderRank(tenant("한라산업개발", "halla")), getHanmacFamilyTenantOrderRank(tenant("한라산업개발", "halla")),
).toBe(4); ).toBe(4);
}); });
}); });

View File

@@ -1,67 +1,67 @@
export type HanmacFamilyOrderTenant = { export type HanmacFamilyOrderTenant = {
name: string; name: string;
slug: string; slug: string;
}; };
export const HANMAC_FAMILY_ROOT_SLUG = "hanmac-family"; export const HANMAC_FAMILY_ROOT_SLUG = "hanmac-family";
export const HANMAC_FAMILY_TENANT_ORDER = [ export const HANMAC_FAMILY_TENANT_ORDER = [
"gpdtdc", "gpdtdc",
"saman", "saman",
"hanmac", "hanmac",
"baron-group", "baron-group",
"halla", "halla",
] as const; ] as const;
function normalizedTenantText(tenant: HanmacFamilyOrderTenant) { function normalizedTenantText(tenant: HanmacFamilyOrderTenant) {
return `${tenant.slug} ${tenant.name}`.trim().toLowerCase(); return `${tenant.slug} ${tenant.name}`.trim().toLowerCase();
} }
export function isHanmacFamilyRootTenant(tenant: HanmacFamilyOrderTenant) { export function isHanmacFamilyRootTenant(tenant: HanmacFamilyOrderTenant) {
return ( return (
tenant.slug.toLowerCase() === HANMAC_FAMILY_ROOT_SLUG || tenant.slug.toLowerCase() === HANMAC_FAMILY_ROOT_SLUG ||
tenant.name.includes("한맥가족") tenant.name.includes("한맥가족")
); );
} }
export function getHanmacFamilyTenantOrderRank( export function getHanmacFamilyTenantOrderRank(
tenant: HanmacFamilyOrderTenant, tenant: HanmacFamilyOrderTenant,
) { ) {
const text = normalizedTenantText(tenant); const text = normalizedTenantText(tenant);
if (text.includes("gpdtdc") || text.includes("총괄기획")) return 0; if (text.includes("gpdtdc") || text.includes("총괄기획")) return 0;
if (text.includes("saman") || text.includes("삼안")) return 1; if (text.includes("saman") || text.includes("삼안")) return 1;
if ( if (
(text.includes("hanmac") || text.includes("한맥기술")) && (text.includes("hanmac") || text.includes("한맥기술")) &&
!isHanmacFamilyRootTenant(tenant) !isHanmacFamilyRootTenant(tenant)
) { ) {
return 2; return 2;
} }
if (text.includes("baron-group") || text.includes("바론그룹")) return 3; if (text.includes("baron-group") || text.includes("바론그룹")) return 3;
if (text.includes("halla") || text.includes("한라산업개발")) return 4; if (text.includes("halla") || text.includes("한라산업개발")) return 4;
return Number.MAX_SAFE_INTEGER; return Number.MAX_SAFE_INTEGER;
} }
export function compareHanmacFamilyTenants<T extends HanmacFamilyOrderTenant>( export function compareHanmacFamilyTenants<T extends HanmacFamilyOrderTenant>(
a: T, a: T,
b: T, b: T,
) { ) {
const rankDiff = const rankDiff =
getHanmacFamilyTenantOrderRank(a) - getHanmacFamilyTenantOrderRank(b); getHanmacFamilyTenantOrderRank(a) - getHanmacFamilyTenantOrderRank(b);
if (rankDiff !== 0) return rankDiff; if (rankDiff !== 0) return rankDiff;
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
} }
export function orderHanmacFamilyTenants<T extends HanmacFamilyOrderTenant>( export function orderHanmacFamilyTenants<T extends HanmacFamilyOrderTenant>(
tenants: readonly T[], tenants: readonly T[],
) { ) {
return [...tenants].sort(compareHanmacFamilyTenants); return [...tenants].sort(compareHanmacFamilyTenants);
} }
export function orderHanmacFamilyChildren<T extends HanmacFamilyOrderTenant>( export function orderHanmacFamilyChildren<T extends HanmacFamilyOrderTenant>(
parent: HanmacFamilyOrderTenant, parent: HanmacFamilyOrderTenant,
children: readonly T[], children: readonly T[],
) { ) {
return isHanmacFamilyRootTenant(parent) return isHanmacFamilyRootTenant(parent)
? orderHanmacFamilyTenants(children) ? orderHanmacFamilyTenants(children)
: [...children]; : [...children];
} }

View File

@@ -3,237 +3,187 @@ import type { TenantSummary, UserSummary } from "../../lib/adminApi";
import { buildOrgPickerTree } from "./pickerTree"; import { buildOrgPickerTree } from "./pickerTree";
function tenant( function tenant(
id: string, id: string,
type: string, type: string,
name: string, name: string,
slug: string, slug: string,
parentId?: string, parentId?: string,
): TenantSummary { ): TenantSummary {
return { return {
id, id,
type, type,
name, name,
slug, slug,
description: "", description: "",
status: "active", status: "active",
parentId, parentId,
memberCount: 0, memberCount: 0,
createdAt: "2026-05-11T00:00:00.000Z", createdAt: "2026-05-11T00:00:00.000Z",
updatedAt: "2026-05-11T00:00:00.000Z", updatedAt: "2026-05-11T00:00:00.000Z",
}; };
} }
describe("buildOrgPickerTree", () => { describe("buildOrgPickerTree", () => {
it("uses the hanmac-family company-group as the default picker root", () => { it("uses the hanmac-family company-group as the default picker root", () => {
const tenants = [ const tenants = [
tenant( tenant("wrong-group", "COMPANY_GROUP", "Wrong Group", "wrong-group"),
"wrong-group", tenant(
"COMPANY_GROUP", "wrong-company",
"Wrong Group", "COMPANY",
"wrong-group", "Wrong Company",
), "wrong-company",
tenant( "wrong-group",
"wrong-company", ),
"COMPANY", tenant("hanmac-family-id", "COMPANY_GROUP", "한맥가족", "hanmac-family"),
"Wrong Company", tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
"wrong-company", ];
"wrong-group",
),
tenant(
"hanmac-family-id",
"COMPANY_GROUP",
"한맥가족",
"hanmac-family",
),
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
];
const tree = buildOrgPickerTree({ const tree = buildOrgPickerTree({
tenants, tenants,
users: [] satisfies UserSummary[], users: [] satisfies UserSummary[],
});
expect(tree.companyGroupId).toBe("hanmac-family-id");
expect(tree.roots).toHaveLength(1);
expect(tree.roots[0]?.id).toBe("hanmac-family-id");
expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([
"saman-id",
]);
}); });
it("orders hanmac-family children by the shared organization policy", () => { expect(tree.companyGroupId).toBe("hanmac-family-id");
const tenants = [ expect(tree.roots).toHaveLength(1);
tenant( expect(tree.roots[0]?.id).toBe("hanmac-family-id");
"hanmac-family-id", expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([
"COMPANY_GROUP", "saman-id",
"한맥가족", ]);
"hanmac-family", });
),
tenant(
"baron-group-id",
"COMPANY_GROUP",
"바론그룹",
"baron-group",
"hanmac-family-id",
),
tenant(
"hanmac-id",
"COMPANY",
"한맥기술",
"hanmac",
"hanmac-family-id",
),
tenant(
"halla-id",
"COMPANY",
"한라산업개발",
"halla",
"hanmac-family-id",
),
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
tenant(
"gpdtdc-id",
"ORGANIZATION",
"총괄기획&기술개발센터",
"gpdtdc",
"hanmac-family-id",
),
];
const tree = buildOrgPickerTree({ it("orders hanmac-family children by the shared organization policy", () => {
tenants, const tenants = [
users: [] satisfies UserSummary[], tenant("hanmac-family-id", "COMPANY_GROUP", "한맥가족", "hanmac-family"),
}); tenant(
"baron-group-id",
"COMPANY_GROUP",
"바론그룹",
"baron-group",
"hanmac-family-id",
),
tenant("hanmac-id", "COMPANY", "한맥기술", "hanmac", "hanmac-family-id"),
tenant(
"halla-id",
"COMPANY",
"한라산업개발",
"halla",
"hanmac-family-id",
),
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
tenant(
"gpdtdc-id",
"ORGANIZATION",
"총괄기획&기술개발센터",
"gpdtdc",
"hanmac-family-id",
),
];
expect(tree.roots[0]?.children.map((node) => node.name)).toEqual([ const tree = buildOrgPickerTree({
"총괄기획&기술개발센터", tenants,
"삼안", users: [] satisfies UserSummary[],
"한맥기술",
"바론그룹",
"한라산업개발",
]);
}); });
it("scopes descendant filtering by tenant slug", () => { expect(tree.roots[0]?.children.map((node) => node.name)).toEqual([
const tenants = [ "총괄기획&기술개발센터",
tenant( "삼안",
"hanmac-family-id", "한맥기술",
"COMPANY_GROUP", "바론그룹",
"한맥가족", "한라산업개발",
"hanmac-family", ]);
), });
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
tenant(
"planning-id",
"ORGANIZATION",
"기획팀",
"planning",
"saman-id",
),
tenant(
"hanmac-id",
"COMPANY",
"한맥기술",
"hanmac",
"hanmac-family-id",
),
];
const tree = buildOrgPickerTree({ it("scopes descendant filtering by tenant slug", () => {
tenants, const tenants = [
users: [] satisfies UserSummary[], tenant("hanmac-family-id", "COMPANY_GROUP", "한맥가족", "hanmac-family"),
tenantId: "saman", tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
}); tenant("planning-id", "ORGANIZATION", "기획팀", "planning", "saman-id"),
tenant("hanmac-id", "COMPANY", "한맥기술", "hanmac", "hanmac-family-id"),
];
expect(tree.roots).toHaveLength(1); const tree = buildOrgPickerTree({
expect(tree.roots[0]?.id).toBe("saman-id"); tenants,
expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([ users: [] satisfies UserSummary[],
"planning-id", tenantId: "saman",
]);
}); });
it("excludes internal and private tenants from picker choices by default", () => { expect(tree.roots).toHaveLength(1);
const tenants = [ expect(tree.roots[0]?.id).toBe("saman-id");
tenant( expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([
"hanmac-family-id", "planning-id",
"COMPANY_GROUP", ]);
"한맥가족", });
"hanmac-family",
),
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
{
...tenant(
"internal-id",
"ORGANIZATION",
"내부 조직",
"internal",
"saman-id",
),
config: { visibility: "internal" },
},
{
...tenant(
"secret-id",
"ORGANIZATION",
"비공개 조직",
"secret",
"saman-id",
),
config: { visibility: "private" },
},
tenant(
"secret-child-id",
"USER_GROUP",
"비공개 하위",
"secret-child",
"secret-id",
),
tenant("open-id", "ORGANIZATION", "공개 조직", "open", "saman-id"),
];
const tree = buildOrgPickerTree({ it("excludes internal and private tenants from picker choices by default", () => {
tenants, const tenants = [
users: [] satisfies UserSummary[], tenant("hanmac-family-id", "COMPANY_GROUP", "한맥가족", "hanmac-family"),
tenantId: "saman", tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
}); {
...tenant(
"internal-id",
"ORGANIZATION",
"내부 조직",
"internal",
"saman-id",
),
config: { visibility: "internal" },
},
{
...tenant(
"secret-id",
"ORGANIZATION",
"비공개 조직",
"secret",
"saman-id",
),
config: { visibility: "private" },
},
tenant(
"secret-child-id",
"USER_GROUP",
"비공개 하위",
"secret-child",
"secret-id",
),
tenant("open-id", "ORGANIZATION", "공개 조직", "open", "saman-id"),
];
expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([ const tree = buildOrgPickerTree({
"open-id", tenants,
]); users: [] satisfies UserSummary[],
tenantId: "saman",
}); });
it("includes internal tenants when explicitly requested", () => { expect(tree.roots[0]?.children.map((node) => node.id)).toEqual(["open-id"]);
const tenants = [ });
tenant(
"hanmac-family-id",
"COMPANY_GROUP",
"한맥가족",
"hanmac-family",
),
tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
{
...tenant(
"internal-id",
"ORGANIZATION",
"내부 조직",
"internal",
"saman-id",
),
config: { visibility: "internal" },
},
tenant("open-id", "ORGANIZATION", "공개 조직", "open", "saman-id"),
];
const tree = buildOrgPickerTree({ it("includes internal tenants when explicitly requested", () => {
includeInternal: true, const tenants = [
tenants, tenant("hanmac-family-id", "COMPANY_GROUP", "한맥가족", "hanmac-family"),
users: [] satisfies UserSummary[], tenant("saman-id", "COMPANY", "삼안", "saman", "hanmac-family-id"),
tenantId: "saman", {
}); ...tenant(
"internal-id",
"ORGANIZATION",
"내부 조직",
"internal",
"saman-id",
),
config: { visibility: "internal" },
},
tenant("open-id", "ORGANIZATION", "공개 조직", "open", "saman-id"),
];
expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([ const tree = buildOrgPickerTree({
"internal-id", includeInternal: true,
"open-id", tenants,
]); users: [] satisfies UserSummary[],
tenantId: "saman",
}); });
expect(tree.roots[0]?.children.map((node) => node.id)).toEqual([
"internal-id",
"open-id",
]);
});
}); });