forked from baron/baron-sso
웍스 동기화 이력확인 기능추가
This commit is contained in:
@@ -54,6 +54,7 @@ import {
|
||||
retryWorksmobileJob,
|
||||
type WorksmobileComparisonItem,
|
||||
type WorksmobileCredentialBatch,
|
||||
type WorksmobileOutboxItem,
|
||||
} from "../../../lib/adminApi";
|
||||
import { t } from "../../../lib/i18n";
|
||||
import {
|
||||
@@ -93,6 +94,70 @@ export function createWorksmobileCredentialBatchId() {
|
||||
return `worksmobile-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
||||
}
|
||||
|
||||
function worksmobileJobPayloadString(job: WorksmobileOutboxItem, key: string) {
|
||||
const value = job.payload?.[key];
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function worksmobileJobRequestSummary(job: WorksmobileOutboxItem) {
|
||||
const summary = job.payload?.requestSummary;
|
||||
if (!summary || typeof summary !== "object" || Array.isArray(summary)) {
|
||||
return {};
|
||||
}
|
||||
return summary as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function worksmobileSummaryString(
|
||||
summary: Record<string, unknown>,
|
||||
key: string,
|
||||
) {
|
||||
const value = summary[key];
|
||||
if (typeof value === "string") {
|
||||
return value.trim();
|
||||
}
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return String(value);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function formatWorksmobileJobTarget(job: WorksmobileOutboxItem) {
|
||||
const summary = worksmobileJobRequestSummary(job);
|
||||
return (
|
||||
worksmobileJobPayloadString(job, "displayName") ||
|
||||
worksmobileSummaryString(summary, "displayName") ||
|
||||
worksmobileSummaryString(summary, "orgUnitName") ||
|
||||
worksmobileJobPayloadString(job, "name") ||
|
||||
worksmobileJobPayloadString(job, "loginEmail") ||
|
||||
worksmobileJobPayloadString(job, "email") ||
|
||||
job.resourceId
|
||||
);
|
||||
}
|
||||
|
||||
function formatWorksmobileJobTargetSubtext(job: WorksmobileOutboxItem) {
|
||||
const summary = worksmobileJobRequestSummary(job);
|
||||
return (
|
||||
worksmobileJobPayloadString(job, "loginEmail") ||
|
||||
worksmobileSummaryString(summary, "email") ||
|
||||
worksmobileJobPayloadString(job, "email") ||
|
||||
worksmobileJobPayloadString(job, "externalKey") ||
|
||||
worksmobileSummaryString(summary, "orgUnitExternalKey") ||
|
||||
job.resourceId
|
||||
);
|
||||
}
|
||||
|
||||
function formatWorksmobileJobSummaryParts(job: WorksmobileOutboxItem) {
|
||||
const summary = worksmobileJobRequestSummary(job);
|
||||
const parts = [
|
||||
worksmobileJobPayloadString(job, "primaryLeafOrgName"),
|
||||
worksmobileJobPayloadString(job, "matchLocalPart"),
|
||||
worksmobileSummaryString(summary, "parentOrgUnitId"),
|
||||
worksmobileSummaryString(summary, "employeeNumber"),
|
||||
worksmobileSummaryString(summary, "task"),
|
||||
].filter(Boolean);
|
||||
return Array.from(new Set(parts));
|
||||
}
|
||||
|
||||
export function TenantWorksmobilePage() {
|
||||
const params = useParams<{ tenantId: string }>();
|
||||
const tenantId = params.tenantId ?? "";
|
||||
@@ -643,9 +708,10 @@ export function TenantWorksmobilePage() {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>resource</TableHead>
|
||||
<TableHead>action</TableHead>
|
||||
<TableHead>status</TableHead>
|
||||
<TableHead>대상</TableHead>
|
||||
<TableHead>작업</TableHead>
|
||||
<TableHead>변경 요약</TableHead>
|
||||
<TableHead>상태</TableHead>
|
||||
<TableHead>retry</TableHead>
|
||||
<TableHead className="w-24" />
|
||||
</TableRow>
|
||||
@@ -654,9 +720,31 @@ export function TenantWorksmobilePage() {
|
||||
{(overview?.recentJobs ?? []).map((job) => (
|
||||
<TableRow key={job.id}>
|
||||
<TableCell>
|
||||
{job.resourceType}:{job.resourceId}
|
||||
<div className="font-medium">
|
||||
{formatWorksmobileJobTarget(job)}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{job.resourceType}:
|
||||
{formatWorksmobileJobTargetSubtext(job)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{job.action}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex max-w-md flex-wrap gap-1">
|
||||
{formatWorksmobileJobSummaryParts(job).map((part) => (
|
||||
<Badge key={part} variant="secondary">
|
||||
{part}
|
||||
</Badge>
|
||||
))}
|
||||
{formatWorksmobileJobSummaryParts(job).length === 0 && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{job.resourceId}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{job.action}</TableCell>
|
||||
<TableCell>{job.status}</TableCell>
|
||||
<TableCell>{job.retryCount}</TableCell>
|
||||
<TableCell>
|
||||
|
||||
@@ -775,6 +775,7 @@ export type WorksmobileOutboxItem = {
|
||||
resourceType: string;
|
||||
resourceId: string;
|
||||
action: string;
|
||||
payload?: Record<string, unknown>;
|
||||
status: string;
|
||||
retryCount: number;
|
||||
lastError?: string;
|
||||
|
||||
@@ -634,6 +634,35 @@ test.describe("Worksmobile tenant management", () => {
|
||||
retryCount: 1,
|
||||
createdAt: "2026-05-01T00:00:00Z",
|
||||
updatedAt: "2026-05-01T00:00:00Z",
|
||||
payload: {
|
||||
loginEmail: "changed-user@example.com",
|
||||
displayName: "변경 사용자",
|
||||
primaryLeafOrgName: "인재성장",
|
||||
requestSummary: {
|
||||
email: "changed-user@example.com",
|
||||
displayName: "변경 사용자",
|
||||
userExternalKey: "user-failed",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "job-org-auto",
|
||||
resourceType: "ORGUNIT",
|
||||
resourceId: "org-auto",
|
||||
action: "UPSERT",
|
||||
status: "processed",
|
||||
retryCount: 0,
|
||||
createdAt: "2026-05-01T00:00:00Z",
|
||||
updatedAt: "2026-05-01T00:01:00Z",
|
||||
payload: {
|
||||
matchLocalPart: "people-growth",
|
||||
requestSummary: {
|
||||
orgUnitName: "인재성장",
|
||||
email: "people-growth@example.com",
|
||||
orgUnitExternalKey: "org-auto",
|
||||
parentOrgUnitId: "externalKey:parent-org",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -764,8 +793,16 @@ test.describe("Worksmobile tenant management", () => {
|
||||
await page.getByRole("button", { name: "구성원 Sync" }).click();
|
||||
await expect.poll(() => requests).toContain("user-sync");
|
||||
|
||||
await expect(page.getByRole("row", { name: /변경 사용자/ })).toContainText(
|
||||
"changed-user@example.com",
|
||||
);
|
||||
await expect(
|
||||
page.getByRole("row", { name: /ORGUNIT:people-growth/ }),
|
||||
).toContainText("people-growth@example.com");
|
||||
await expect(page.getByText("externalKey:parent-org")).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole("row", { name: /USER:user-failed/ })
|
||||
.getByRole("row", { name: /변경 사용자/ })
|
||||
.getByRole("button")
|
||||
.click();
|
||||
await expect.poll(() => requests).toContain("retry");
|
||||
|
||||
Reference in New Issue
Block a user