1
0
forked from baron/baron-sso

조직 연동 오류 해결

This commit is contained in:
2026-05-20 11:17:31 +09:00
parent 42b49674cc
commit fd82dd9bdd
15 changed files with 592 additions and 30 deletions

View File

@@ -4,11 +4,14 @@ import {
canCreateWorksmobileRow,
canOpenWorksmobilePasswordManage,
canSelectWorksmobileRow,
comparisonFilterOptions,
filterVisibleWorksmobileComparisonRows,
filterWorksmobileComparisonRows,
filterWorksmobileComparisonRowsBySearch,
formatWorksmobileOrgDetails,
formatWorksmobilePersonName,
formatWorksmobileUpdateDetails,
getDefaultGroupComparisonFilters,
getDefaultWorksmobileComparisonColumns,
getWorksmobileComparisonStatusLabel,
getWorksmobileRowSelectionKey,
@@ -24,6 +27,7 @@ describe("TenantWorksmobilePage comparison helpers", () => {
it("summarizes comparison rows by status", () => {
const summary = summarizeWorksmobileComparison([
{ resourceType: "USER", status: "matched" },
{ resourceType: "GROUP", status: "needs_update" },
{ resourceType: "USER", status: "missing_in_worksmobile" },
{ resourceType: "USER", status: "missing_in_baron" },
{ resourceType: "USER", status: "missing_external_key" },
@@ -31,8 +35,9 @@ describe("TenantWorksmobilePage comparison helpers", () => {
]);
expect(summary).toEqual({
total: 5,
total: 6,
matched: 1,
needsUpdate: 1,
missingInWorksmobile: 1,
missingInBaron: 2,
missingExternalKey: 1,
@@ -50,6 +55,9 @@ describe("TenantWorksmobilePage comparison helpers", () => {
expect(getWorksmobileComparisonStatusLabel("missing_external_key")).toBe(
"ex_key 없음",
);
expect(getWorksmobileComparisonStatusLabel("needs_update")).toBe(
"업데이트 필요",
);
expect(getWorksmobileComparisonStatusLabel("unknown_status")).toBe(
"unknown_status",
);
@@ -426,11 +434,52 @@ describe("TenantWorksmobilePage comparison helpers", () => {
it("orders user comparison filter options from Baron-only first", () => {
expect(userFilterOptions.map((option) => option.value)).toEqual([
"baron_only",
"needs_update",
"works_only",
"matched",
]);
});
it("keeps all organization/group comparison filter labels available", () => {
expect(comparisonFilterOptions).toEqual([
{ value: "baron_only", label: "바론에만 있음" },
{ value: "needs_update", label: "업데이트 필요" },
{ value: "works_only", label: "웍스에만 있음" },
{ value: "matched", label: "양쪽 다 있음" },
]);
});
it("shows update-needed group rows by default", () => {
const rows = [
{ resourceType: "GROUP", status: "needs_update", baronId: "org-1" },
{ resourceType: "GROUP", status: "matched", baronId: "org-2" },
];
expect(
filterWorksmobileComparisonRows(rows, getDefaultGroupComparisonFilters()),
).toEqual([rows[0]]);
});
it("formats update details for changed organization rows", () => {
expect(
formatWorksmobileUpdateDetails({
resourceType: "GROUP",
status: "needs_update",
baronId: "818c856b-9545-442f-b827-d1c569f200b0",
baronName: "삼안기술개발센터(조직도용)",
worksmobileName: "기술개발센터(조직도용)",
baronParentId: "9caf62e1-297d-4e8f-870b-61780998bbeb",
baronParentWorksmobileId: "works-saman",
baronParentWorksmobileName: "삼안",
worksmobileParentId: "works-other",
worksmobileParentName: "다른 상위",
}),
).toEqual([
"이름: 기술개발센터(조직도용) -> 삼안기술개발센터(조직도용)",
"상위: 다른 상위 -> 삼안",
]);
});
it("formats WORKS account name with level on one line", () => {
expect(
formatWorksmobilePersonName({

View File

@@ -63,6 +63,8 @@ import {
filterWorksmobileComparisonRowsBySearch,
formatWorksmobileOrgDetails,
formatWorksmobilePersonName,
formatWorksmobileUpdateDetails,
getDefaultGroupComparisonFilters,
getDefaultWorksmobileComparisonColumns,
getWorksmobileComparisonStatusLabel,
getWorksmobileRowSelectionKey,
@@ -81,7 +83,7 @@ export function TenantWorksmobilePage() {
>(["baron_only", "works_only"]);
const [groupFilters, setGroupFilters] = React.useState<
WorksmobileComparisonFilter[]
>(["baron_only", "works_only"]);
>(getDefaultGroupComparisonFilters);
const [includeUserMissingExternalKey, setIncludeUserMissingExternalKey] =
React.useState(false);
const [includeGroupMissingExternalKey, setIncludeGroupMissingExternalKey] =
@@ -594,6 +596,9 @@ function getWorksmobileComparisonStatusVariant(status: string) {
if (status === "matched") {
return "success";
}
if (status === "needs_update") {
return "warning";
}
if (status === "missing_external_key") {
return "warning";
}
@@ -622,6 +627,10 @@ function ComparisonSummary({
<span className="text-muted-foreground">Baron </span>
<span className="font-mono">{summary.missingInBaron}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"> </span>
<span className="font-mono">{summary.needsUpdate}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">ex_key </span>
<span className="font-mono">{summary.missingExternalKey}</span>
@@ -860,7 +869,7 @@ function ComparisonTable({
return (
<div className="min-w-0 space-y-2">
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex min-w-0 flex-wrap items-center gap-3">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-3">
<h4 className="text-lg font-semibold leading-none">{title}</h4>
<Input
type="search"
@@ -890,7 +899,7 @@ function ComparisonTable({
}
/>
</div>
<div className="ml-auto flex flex-wrap items-center justify-end gap-2">
<div className="flex shrink-0 flex-wrap items-center justify-end gap-2">
<Dialog
open={columnSettingsOpen}
onOpenChange={setColumnSettingsOpen}
@@ -1058,6 +1067,14 @@ function ComparisonTable({
>
{getWorksmobileComparisonStatusLabel(row.status)}
</Badge>
{formatWorksmobileUpdateDetails(row).map((detail) => (
<div
key={detail}
className="mt-1 max-w-56 whitespace-normal text-xs text-muted-foreground"
>
{detail}
</div>
))}
</TableCell>
)}
{showBaronIdColumn && isColumnVisible("baronId") && (

View File

@@ -3,11 +3,13 @@ import type { WorksmobileComparisonItem } from "../../../lib/adminApi";
export type WorksmobileComparisonFilter =
| "works_only"
| "baron_only"
| "needs_update"
| "matched";
export type WorksmobileComparisonSummary = {
total: number;
matched: number;
needsUpdate: number;
missingInWorksmobile: number;
missingInBaron: number;
missingExternalKey: number;
@@ -52,6 +54,8 @@ export function summarizeWorksmobileComparison(
(summary, row) => {
if (row.status === "matched") {
summary.matched += 1;
} else if (row.status === "needs_update") {
summary.needsUpdate += 1;
} else if (row.status === "missing_in_worksmobile") {
summary.missingInWorksmobile += 1;
} else if (row.status === "missing_in_baron") {
@@ -64,6 +68,7 @@ export function summarizeWorksmobileComparison(
{
total: rows.length,
matched: 0,
needsUpdate: 0,
missingInWorksmobile: 0,
missingInBaron: 0,
missingExternalKey: 0,
@@ -77,6 +82,8 @@ export function getWorksmobileComparisonStatusLabel(status: string) {
return "일치";
case "missing_in_worksmobile":
return "WORKS 없음";
case "needs_update":
return "업데이트 필요";
case "missing_in_baron":
return "Baron 없음";
case "missing_external_key":
@@ -292,6 +299,42 @@ export function formatWorksmobileOrgDetails(row: WorksmobileComparisonItem) {
return details;
}
export function formatWorksmobileUpdateDetails(row: WorksmobileComparisonItem) {
if (row.status !== "needs_update") {
return [];
}
const details: string[] = [];
const baronName = row.baronName?.trim();
const worksmobileName = row.worksmobileName?.trim();
if (baronName && worksmobileName && baronName !== worksmobileName) {
details.push(`이름: ${worksmobileName} -> ${baronName}`);
}
const expectedParent =
row.baronParentWorksmobileName ??
row.baronParentName ??
row.baronParentWorksmobileId ??
row.baronParentId ??
"";
const actualParent =
row.worksmobileParentName ??
row.worksmobileParentExternalKey ??
row.worksmobileParentId ??
"";
const expectedParentKey =
row.baronParentWorksmobileId ?? row.baronParentId ?? "";
const actualParentKey =
row.worksmobileParentId ?? row.worksmobileParentExternalKey ?? "";
if (expectedParentKey !== actualParentKey) {
details.push(
`상위: ${actualParent || "없음"} -> ${expectedParent || "없음"}`,
);
}
return details;
}
export function buildWorksmobilePasswordManageUrl({
tenantId,
domainId,
@@ -345,15 +388,21 @@ export const comparisonFilterOptions: Array<{
label: string;
}> = [
{ value: "baron_only", label: "바론에만 있음" },
{ value: "needs_update", label: "업데이트 필요" },
{ value: "works_only", label: "웍스에만 있음" },
{ value: "matched", label: "양쪽 다 있음" },
];
export const userFilterOptions = comparisonFilterOptions;
export function getDefaultGroupComparisonFilters(): WorksmobileComparisonFilter[] {
return ["baron_only", "needs_update", "works_only"];
}
const worksmobileFilterStatuses: Record<WorksmobileComparisonFilter, string[]> =
{
baron_only: ["missing_in_worksmobile"],
needs_update: ["needs_update"],
works_only: ["missing_in_baron"],
matched: ["matched"],
};