forked from baron/baron-sso
최근 변경 앱 상세 다국어 정리
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
OverviewMetric,
|
||||
OverviewSelectionChips,
|
||||
} from "../../../../common/core/components/overview";
|
||||
import { formatAuditDateParts } from "../../../../common/core/audit";
|
||||
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
@@ -81,6 +80,7 @@ type UsageChartPalette = {
|
||||
};
|
||||
|
||||
const deletedRecentChangeFilterId = "__deleted_recent_clients__";
|
||||
const localeStorageKey = "locale";
|
||||
|
||||
type RecentChangePoint = {
|
||||
date: string;
|
||||
@@ -102,6 +102,73 @@ type RecentChangeSeries = {
|
||||
points: RecentChangePoint[];
|
||||
};
|
||||
|
||||
type AppLocale = "ko" | "en";
|
||||
|
||||
function resolveAppLocale(): AppLocale {
|
||||
if (typeof window === "undefined") {
|
||||
return "ko";
|
||||
}
|
||||
|
||||
const stored = window.localStorage.getItem(localeStorageKey);
|
||||
if (stored === "ko" || stored === "en") {
|
||||
return stored;
|
||||
}
|
||||
|
||||
const pathLocale = window.location.pathname.split("/")[1];
|
||||
if (pathLocale === "ko" || pathLocale === "en") {
|
||||
return pathLocale;
|
||||
}
|
||||
|
||||
return window.navigator.language.toLowerCase().startsWith("ko")
|
||||
? "ko"
|
||||
: "en";
|
||||
}
|
||||
|
||||
function formatRecentChangeTimestamp(value: string) {
|
||||
if (!value) {
|
||||
return { date: "-", time: "-" };
|
||||
}
|
||||
|
||||
const parsed = new Date(value);
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
return { date: value, time: "-" };
|
||||
}
|
||||
|
||||
const locale = resolveAppLocale();
|
||||
if (locale === "ko") {
|
||||
const date = parsed.toISOString().slice(0, 10);
|
||||
const timeParts = new Intl.DateTimeFormat("ko-KR", {
|
||||
hour12: false,
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}).formatToParts(parsed);
|
||||
const hour = timeParts.find((part) => part.type === "hour")?.value ?? "00";
|
||||
const minute =
|
||||
timeParts.find((part) => part.type === "minute")?.value ?? "00";
|
||||
const second =
|
||||
timeParts.find((part) => part.type === "second")?.value ?? "00";
|
||||
|
||||
return {
|
||||
date,
|
||||
time: `${hour}시 ${minute}분 ${second}초`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
date: new Intl.DateTimeFormat("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
}).format(parsed),
|
||||
time: new Intl.DateTimeFormat("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}).format(parsed),
|
||||
};
|
||||
}
|
||||
|
||||
const usageChartPalettes: UsageChartPalette[] = [
|
||||
{ bar: "#7dd3fc", line: "#10b981", point: "#059669" },
|
||||
{ bar: "#f9a8d4", line: "#f97316", point: "#ea580c" },
|
||||
@@ -1068,7 +1135,7 @@ function GlobalOverviewPage() {
|
||||
id: deletedRecentChangeFilterId,
|
||||
label: t(
|
||||
"ui.dev.dashboard.recent_changes.deleted_group",
|
||||
"삭제된 RP",
|
||||
"삭제된 앱",
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -1399,9 +1466,9 @@ function GlobalOverviewPage() {
|
||||
<OverviewMetric
|
||||
icon={<Layers3 size={14} />}
|
||||
label={t(
|
||||
"ui.dev.dashboard.recent_changes.summary.deleted_clients",
|
||||
"삭제된 RP 수",
|
||||
)}
|
||||
"ui.dev.dashboard.recent_changes.summary.deleted_clients",
|
||||
"삭제된 앱 수",
|
||||
)}
|
||||
value={deletedRecentChangedClientCount.toLocaleString()}
|
||||
/>
|
||||
<OverviewMetric
|
||||
@@ -1466,7 +1533,8 @@ function GlobalOverviewPage() {
|
||||
</div>
|
||||
) : (
|
||||
visibleRecentClientChanges.map((item) => {
|
||||
const { date, time } = formatAuditDateParts(item.timestamp);
|
||||
const { date, time } =
|
||||
formatRecentChangeTimestamp(item.timestamp);
|
||||
return (
|
||||
<div
|
||||
key={item.eventId}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type AuditDetails,
|
||||
type CommonAuditLog,
|
||||
} from "../../../../common/core/audit";
|
||||
import { t } from "../../lib/i18n";
|
||||
import type { ClientSummary, DevAuditLog } from "../../lib/devApi";
|
||||
|
||||
export type RecentClientChange = {
|
||||
@@ -28,27 +29,6 @@ const recentClientActions = new Set([
|
||||
"DELETE_CLIENT",
|
||||
]);
|
||||
|
||||
const recentClientFieldLabels: Record<string, string> = {
|
||||
name: "이름",
|
||||
type: "유형",
|
||||
status: "상태",
|
||||
scopes: "스코프",
|
||||
tenant_access_restricted: "테넌트 접근 제한",
|
||||
allowed_tenants: "허용 테넌트",
|
||||
id_token_claims: "커스텀 클레임",
|
||||
token_endpoint_auth_method: "인증 방식",
|
||||
jwks_uri: "JWKS URI",
|
||||
backchannel_logout_uri: "Backchannel Logout URI",
|
||||
backchannel_logout_session_required: "세션 필수",
|
||||
headless_login_enabled: "헤드리스 로그인",
|
||||
headless_token_endpoint_auth_method: "헤드리스 인증 방식",
|
||||
headless_jwks_uri: "헤드리스 JWKS URI",
|
||||
redirect_uri_count: "Redirect URI 수",
|
||||
scope_count: "Scope 수",
|
||||
relation: "관계",
|
||||
subject: "대상",
|
||||
};
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
@@ -56,24 +36,43 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
export function getRecentClientActionLabel(action: string) {
|
||||
switch (action) {
|
||||
case "CREATE_CLIENT":
|
||||
return "클라이언트 생성";
|
||||
return t("ui.dev.clients.recent_changes.guide.create", "앱 생성");
|
||||
case "UPDATE_CLIENT":
|
||||
return "설정 변경";
|
||||
return t("ui.dev.clients.recent_changes.guide.settings", "설정 변경");
|
||||
case "UPDATE_CLIENT_STATUS":
|
||||
return "상태 변경";
|
||||
return t("ui.dev.clients.recent_changes.guide.status", "상태 변경");
|
||||
case "ROTATE_SECRET":
|
||||
return "클라이언트 시크릿 재발급";
|
||||
return t(
|
||||
"ui.dev.clients.recent_changes.guide.secret",
|
||||
"클라이언트 시크릿 재발급",
|
||||
);
|
||||
case "ADD_RELATION":
|
||||
return "관계 추가";
|
||||
return t("ui.dev.clients.relationships.add_title", "관계 추가");
|
||||
case "REMOVE_RELATION":
|
||||
return "관계 삭제";
|
||||
return t("ui.common.remove", "Remove");
|
||||
case "DELETE_CLIENT":
|
||||
return "클라이언트 삭제";
|
||||
return t("ui.dev.clients.recent_changes.guide.delete", "앱 삭제");
|
||||
default:
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
function getRecentClientFieldLabel(key: string) {
|
||||
switch (key) {
|
||||
case "relation":
|
||||
return t("ui.dev.clients.relationships.relation", "관계");
|
||||
case "subject":
|
||||
return t("ui.dev.clients.relationships.subject", "대상");
|
||||
case "client_secret":
|
||||
return t(
|
||||
"ui.dev.clients.details.credentials.client_secret",
|
||||
"클라이언트 시크릿",
|
||||
);
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildRecentClientChangeDetails(
|
||||
action: string,
|
||||
details: AuditDetails,
|
||||
@@ -82,17 +81,32 @@ export function buildRecentClientChangeDetails(
|
||||
const after = isRecord(details.after) ? details.after : {};
|
||||
|
||||
if (action === "ROTATE_SECRET") {
|
||||
return [{ label: "클라이언트 시크릿", value: "재발급" }];
|
||||
return [
|
||||
{
|
||||
label: getRecentClientFieldLabel("client_secret"),
|
||||
value: t("msg.dev.clients.secret_rotated", "재발급"),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (action === "ADD_RELATION" || action === "REMOVE_RELATION") {
|
||||
const source = action === "ADD_RELATION" ? after : before;
|
||||
return [
|
||||
...(source.relation
|
||||
? [{ label: "관계", value: formatAuditValue(source.relation) }]
|
||||
? [
|
||||
{
|
||||
label: getRecentClientFieldLabel("relation"),
|
||||
value: formatAuditValue(source.relation),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(source.subject
|
||||
? [{ label: "대상", value: formatAuditValue(source.subject) }]
|
||||
? [
|
||||
{
|
||||
label: getRecentClientFieldLabel("subject"),
|
||||
value: formatAuditValue(source.subject),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
@@ -112,7 +126,7 @@ export function buildRecentClientChangeDetails(
|
||||
}
|
||||
}
|
||||
|
||||
const label = recentClientFieldLabels[key] ?? key;
|
||||
const label = getRecentClientFieldLabel(key);
|
||||
if (action === "CREATE_CLIENT") {
|
||||
if (afterValue === undefined) {
|
||||
return null;
|
||||
|
||||
@@ -1746,7 +1746,7 @@ title = "Quick links"
|
||||
title = "My Applications"
|
||||
|
||||
[ui.dev.dashboard.recent_changes]
|
||||
deleted_group = "Deleted RPs"
|
||||
deleted_group = "Deleted applications"
|
||||
aria = "Recent application changes"
|
||||
period = "Recent change aggregation period"
|
||||
series = "Changes {{changes}} / Actors {{actors}}"
|
||||
@@ -1755,7 +1755,7 @@ y_axis = "Y axis: change count"
|
||||
|
||||
[ui.dev.dashboard.recent_changes.summary]
|
||||
changed_clients = "Changed apps"
|
||||
deleted_clients = "Deleted RPs"
|
||||
deleted_clients = "Deleted applications"
|
||||
latest_change = "Latest change"
|
||||
total_changes = "Recent changes"
|
||||
|
||||
|
||||
@@ -1745,7 +1745,7 @@ title = "빠른 이동"
|
||||
title = "내 애플리케이션"
|
||||
|
||||
[ui.dev.dashboard.recent_changes]
|
||||
deleted_group = "삭제된 RP"
|
||||
deleted_group = "삭제된 앱"
|
||||
aria = "최근 변경된 앱 현황"
|
||||
period = "최근 변경 집계 단위"
|
||||
series = "변경 {{changes}} / 작업자 {{actors}}"
|
||||
@@ -1754,7 +1754,7 @@ y_axis = "Y축: 변경 수"
|
||||
|
||||
[ui.dev.dashboard.recent_changes.summary]
|
||||
changed_clients = "변경된 앱 수"
|
||||
deleted_clients = "삭제된 RP 수"
|
||||
deleted_clients = "삭제된 앱 수"
|
||||
latest_change = "마지막 변경일"
|
||||
total_changes = "최근 변경 건수"
|
||||
|
||||
|
||||
@@ -2314,7 +2314,7 @@ title = "My Applications"
|
||||
|
||||
[ui.dev.dashboard.recent_changes]
|
||||
aria = "Recent changed application status"
|
||||
deleted_group = "Deleted RPs"
|
||||
deleted_group = "Deleted applications"
|
||||
period = "Recent change aggregation period"
|
||||
series = "Changes {{changes}} / Actors {{actors}}"
|
||||
title = "Recent Changed Apps"
|
||||
@@ -2322,7 +2322,7 @@ y_axis = "Y axis: change count"
|
||||
|
||||
[ui.dev.dashboard.recent_changes.summary]
|
||||
changed_clients = "Changed applications"
|
||||
deleted_clients = "Deleted RPs"
|
||||
deleted_clients = "Deleted applications"
|
||||
latest_change = "Latest change"
|
||||
total_changes = "Recent change count"
|
||||
|
||||
|
||||
@@ -2778,7 +2778,7 @@ title = "내 애플리케이션"
|
||||
|
||||
[ui.dev.dashboard.recent_changes]
|
||||
aria = "최근 변경된 앱 현황"
|
||||
deleted_group = "삭제된 RP"
|
||||
deleted_group = "삭제된 앱"
|
||||
period = "최근 변경 집계 단위"
|
||||
series = "변경 {{changes}} / 작업자 {{actors}}"
|
||||
title = "최근 변경된 앱"
|
||||
@@ -2786,7 +2786,7 @@ y_axis = "Y축: 변경 수"
|
||||
|
||||
[ui.dev.dashboard.recent_changes.summary]
|
||||
changed_clients = "변경된 앱 수"
|
||||
deleted_clients = "삭제된 RP 수"
|
||||
deleted_clients = "삭제된 앱 수"
|
||||
latest_change = "마지막 변경일"
|
||||
total_changes = "최근 변경 건수"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user