forked from baron/baron-sso
최근 변경 앱 상세 다국어 정리
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
|||||||
OverviewMetric,
|
OverviewMetric,
|
||||||
OverviewSelectionChips,
|
OverviewSelectionChips,
|
||||||
} from "../../../../common/core/components/overview";
|
} from "../../../../common/core/components/overview";
|
||||||
import { formatAuditDateParts } from "../../../../common/core/audit";
|
|
||||||
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
|
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
|
||||||
import { Badge } from "../../components/ui/badge";
|
import { Badge } from "../../components/ui/badge";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
@@ -81,6 +80,7 @@ type UsageChartPalette = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deletedRecentChangeFilterId = "__deleted_recent_clients__";
|
const deletedRecentChangeFilterId = "__deleted_recent_clients__";
|
||||||
|
const localeStorageKey = "locale";
|
||||||
|
|
||||||
type RecentChangePoint = {
|
type RecentChangePoint = {
|
||||||
date: string;
|
date: string;
|
||||||
@@ -102,6 +102,73 @@ type RecentChangeSeries = {
|
|||||||
points: RecentChangePoint[];
|
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[] = [
|
const usageChartPalettes: UsageChartPalette[] = [
|
||||||
{ bar: "#7dd3fc", line: "#10b981", point: "#059669" },
|
{ bar: "#7dd3fc", line: "#10b981", point: "#059669" },
|
||||||
{ bar: "#f9a8d4", line: "#f97316", point: "#ea580c" },
|
{ bar: "#f9a8d4", line: "#f97316", point: "#ea580c" },
|
||||||
@@ -1068,7 +1135,7 @@ function GlobalOverviewPage() {
|
|||||||
id: deletedRecentChangeFilterId,
|
id: deletedRecentChangeFilterId,
|
||||||
label: t(
|
label: t(
|
||||||
"ui.dev.dashboard.recent_changes.deleted_group",
|
"ui.dev.dashboard.recent_changes.deleted_group",
|
||||||
"삭제된 RP",
|
"삭제된 앱",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1399,9 +1466,9 @@ function GlobalOverviewPage() {
|
|||||||
<OverviewMetric
|
<OverviewMetric
|
||||||
icon={<Layers3 size={14} />}
|
icon={<Layers3 size={14} />}
|
||||||
label={t(
|
label={t(
|
||||||
"ui.dev.dashboard.recent_changes.summary.deleted_clients",
|
"ui.dev.dashboard.recent_changes.summary.deleted_clients",
|
||||||
"삭제된 RP 수",
|
"삭제된 앱 수",
|
||||||
)}
|
)}
|
||||||
value={deletedRecentChangedClientCount.toLocaleString()}
|
value={deletedRecentChangedClientCount.toLocaleString()}
|
||||||
/>
|
/>
|
||||||
<OverviewMetric
|
<OverviewMetric
|
||||||
@@ -1466,7 +1533,8 @@ function GlobalOverviewPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
visibleRecentClientChanges.map((item) => {
|
visibleRecentClientChanges.map((item) => {
|
||||||
const { date, time } = formatAuditDateParts(item.timestamp);
|
const { date, time } =
|
||||||
|
formatRecentChangeTimestamp(item.timestamp);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={item.eventId}
|
key={item.eventId}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
type AuditDetails,
|
type AuditDetails,
|
||||||
type CommonAuditLog,
|
type CommonAuditLog,
|
||||||
} from "../../../../common/core/audit";
|
} from "../../../../common/core/audit";
|
||||||
|
import { t } from "../../lib/i18n";
|
||||||
import type { ClientSummary, DevAuditLog } from "../../lib/devApi";
|
import type { ClientSummary, DevAuditLog } from "../../lib/devApi";
|
||||||
|
|
||||||
export type RecentClientChange = {
|
export type RecentClientChange = {
|
||||||
@@ -28,27 +29,6 @@ const recentClientActions = new Set([
|
|||||||
"DELETE_CLIENT",
|
"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> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
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) {
|
export function getRecentClientActionLabel(action: string) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "CREATE_CLIENT":
|
case "CREATE_CLIENT":
|
||||||
return "클라이언트 생성";
|
return t("ui.dev.clients.recent_changes.guide.create", "앱 생성");
|
||||||
case "UPDATE_CLIENT":
|
case "UPDATE_CLIENT":
|
||||||
return "설정 변경";
|
return t("ui.dev.clients.recent_changes.guide.settings", "설정 변경");
|
||||||
case "UPDATE_CLIENT_STATUS":
|
case "UPDATE_CLIENT_STATUS":
|
||||||
return "상태 변경";
|
return t("ui.dev.clients.recent_changes.guide.status", "상태 변경");
|
||||||
case "ROTATE_SECRET":
|
case "ROTATE_SECRET":
|
||||||
return "클라이언트 시크릿 재발급";
|
return t(
|
||||||
|
"ui.dev.clients.recent_changes.guide.secret",
|
||||||
|
"클라이언트 시크릿 재발급",
|
||||||
|
);
|
||||||
case "ADD_RELATION":
|
case "ADD_RELATION":
|
||||||
return "관계 추가";
|
return t("ui.dev.clients.relationships.add_title", "관계 추가");
|
||||||
case "REMOVE_RELATION":
|
case "REMOVE_RELATION":
|
||||||
return "관계 삭제";
|
return t("ui.common.remove", "Remove");
|
||||||
case "DELETE_CLIENT":
|
case "DELETE_CLIENT":
|
||||||
return "클라이언트 삭제";
|
return t("ui.dev.clients.recent_changes.guide.delete", "앱 삭제");
|
||||||
default:
|
default:
|
||||||
return action;
|
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(
|
export function buildRecentClientChangeDetails(
|
||||||
action: string,
|
action: string,
|
||||||
details: AuditDetails,
|
details: AuditDetails,
|
||||||
@@ -82,17 +81,32 @@ export function buildRecentClientChangeDetails(
|
|||||||
const after = isRecord(details.after) ? details.after : {};
|
const after = isRecord(details.after) ? details.after : {};
|
||||||
|
|
||||||
if (action === "ROTATE_SECRET") {
|
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") {
|
if (action === "ADD_RELATION" || action === "REMOVE_RELATION") {
|
||||||
const source = action === "ADD_RELATION" ? after : before;
|
const source = action === "ADD_RELATION" ? after : before;
|
||||||
return [
|
return [
|
||||||
...(source.relation
|
...(source.relation
|
||||||
? [{ label: "관계", value: formatAuditValue(source.relation) }]
|
? [
|
||||||
|
{
|
||||||
|
label: getRecentClientFieldLabel("relation"),
|
||||||
|
value: formatAuditValue(source.relation),
|
||||||
|
},
|
||||||
|
]
|
||||||
: []),
|
: []),
|
||||||
...(source.subject
|
...(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 (action === "CREATE_CLIENT") {
|
||||||
if (afterValue === undefined) {
|
if (afterValue === undefined) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1746,7 +1746,7 @@ title = "Quick links"
|
|||||||
title = "My Applications"
|
title = "My Applications"
|
||||||
|
|
||||||
[ui.dev.dashboard.recent_changes]
|
[ui.dev.dashboard.recent_changes]
|
||||||
deleted_group = "Deleted RPs"
|
deleted_group = "Deleted applications"
|
||||||
aria = "Recent application changes"
|
aria = "Recent application changes"
|
||||||
period = "Recent change aggregation period"
|
period = "Recent change aggregation period"
|
||||||
series = "Changes {{changes}} / Actors {{actors}}"
|
series = "Changes {{changes}} / Actors {{actors}}"
|
||||||
@@ -1755,7 +1755,7 @@ y_axis = "Y axis: change count"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes.summary]
|
[ui.dev.dashboard.recent_changes.summary]
|
||||||
changed_clients = "Changed apps"
|
changed_clients = "Changed apps"
|
||||||
deleted_clients = "Deleted RPs"
|
deleted_clients = "Deleted applications"
|
||||||
latest_change = "Latest change"
|
latest_change = "Latest change"
|
||||||
total_changes = "Recent changes"
|
total_changes = "Recent changes"
|
||||||
|
|
||||||
|
|||||||
@@ -1745,7 +1745,7 @@ title = "빠른 이동"
|
|||||||
title = "내 애플리케이션"
|
title = "내 애플리케이션"
|
||||||
|
|
||||||
[ui.dev.dashboard.recent_changes]
|
[ui.dev.dashboard.recent_changes]
|
||||||
deleted_group = "삭제된 RP"
|
deleted_group = "삭제된 앱"
|
||||||
aria = "최근 변경된 앱 현황"
|
aria = "최근 변경된 앱 현황"
|
||||||
period = "최근 변경 집계 단위"
|
period = "최근 변경 집계 단위"
|
||||||
series = "변경 {{changes}} / 작업자 {{actors}}"
|
series = "변경 {{changes}} / 작업자 {{actors}}"
|
||||||
@@ -1754,7 +1754,7 @@ y_axis = "Y축: 변경 수"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes.summary]
|
[ui.dev.dashboard.recent_changes.summary]
|
||||||
changed_clients = "변경된 앱 수"
|
changed_clients = "변경된 앱 수"
|
||||||
deleted_clients = "삭제된 RP 수"
|
deleted_clients = "삭제된 앱 수"
|
||||||
latest_change = "마지막 변경일"
|
latest_change = "마지막 변경일"
|
||||||
total_changes = "최근 변경 건수"
|
total_changes = "최근 변경 건수"
|
||||||
|
|
||||||
|
|||||||
@@ -2314,7 +2314,7 @@ title = "My Applications"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes]
|
[ui.dev.dashboard.recent_changes]
|
||||||
aria = "Recent changed application status"
|
aria = "Recent changed application status"
|
||||||
deleted_group = "Deleted RPs"
|
deleted_group = "Deleted applications"
|
||||||
period = "Recent change aggregation period"
|
period = "Recent change aggregation period"
|
||||||
series = "Changes {{changes}} / Actors {{actors}}"
|
series = "Changes {{changes}} / Actors {{actors}}"
|
||||||
title = "Recent Changed Apps"
|
title = "Recent Changed Apps"
|
||||||
@@ -2322,7 +2322,7 @@ y_axis = "Y axis: change count"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes.summary]
|
[ui.dev.dashboard.recent_changes.summary]
|
||||||
changed_clients = "Changed applications"
|
changed_clients = "Changed applications"
|
||||||
deleted_clients = "Deleted RPs"
|
deleted_clients = "Deleted applications"
|
||||||
latest_change = "Latest change"
|
latest_change = "Latest change"
|
||||||
total_changes = "Recent change count"
|
total_changes = "Recent change count"
|
||||||
|
|
||||||
|
|||||||
@@ -2778,7 +2778,7 @@ title = "내 애플리케이션"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes]
|
[ui.dev.dashboard.recent_changes]
|
||||||
aria = "최근 변경된 앱 현황"
|
aria = "최근 변경된 앱 현황"
|
||||||
deleted_group = "삭제된 RP"
|
deleted_group = "삭제된 앱"
|
||||||
period = "최근 변경 집계 단위"
|
period = "최근 변경 집계 단위"
|
||||||
series = "변경 {{changes}} / 작업자 {{actors}}"
|
series = "변경 {{changes}} / 작업자 {{actors}}"
|
||||||
title = "최근 변경된 앱"
|
title = "최근 변경된 앱"
|
||||||
@@ -2786,7 +2786,7 @@ y_axis = "Y축: 변경 수"
|
|||||||
|
|
||||||
[ui.dev.dashboard.recent_changes.summary]
|
[ui.dev.dashboard.recent_changes.summary]
|
||||||
changed_clients = "변경된 앱 수"
|
changed_clients = "변경된 앱 수"
|
||||||
deleted_clients = "삭제된 RP 수"
|
deleted_clients = "삭제된 앱 수"
|
||||||
latest_change = "마지막 변경일"
|
latest_change = "마지막 변경일"
|
||||||
total_changes = "최근 변경 건수"
|
total_changes = "최근 변경 건수"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user