forked from baron/baron-sso
Merge pull request 'feature/common-core' (#774) from feature/common-core into dev
Reviewed-on: baron/baron-sso#774
This commit is contained in:
@@ -9,15 +9,19 @@ import {
|
|||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { type ReactNode, useMemo, useState } from "react";
|
import { type ReactNode, useMemo, useState } from "react";
|
||||||
|
import { useAuth } from "react-oidc-context";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
type ClientSummary,
|
type ClientSummary,
|
||||||
fetchClients,
|
fetchClients,
|
||||||
|
fetchDeveloperRequestStatus,
|
||||||
fetchDevRPUsageDaily,
|
fetchDevRPUsageDaily,
|
||||||
fetchDevStats,
|
fetchDevStats,
|
||||||
type RPUsageDailyMetric,
|
type RPUsageDailyMetric,
|
||||||
type RPUsagePeriod,
|
type RPUsagePeriod,
|
||||||
} from "../../lib/devApi";
|
} from "../../lib/devApi";
|
||||||
import { t } from "../../lib/i18n";
|
import { t } from "../../lib/i18n";
|
||||||
|
import { resolveProfileRole } from "../../lib/role";
|
||||||
|
|
||||||
type ClientDistribution = {
|
type ClientDistribution = {
|
||||||
activeClients: number;
|
activeClients: number;
|
||||||
@@ -135,14 +139,12 @@ function summarizeSeries(rows: RPUsageDailyMetric[]): SeriesSummary[] {
|
|||||||
);
|
);
|
||||||
bySeries.set(key, current);
|
bySeries.set(key, current);
|
||||||
}
|
}
|
||||||
return Array.from(bySeries.values())
|
return Array.from(bySeries.values()).sort(
|
||||||
.sort(
|
(left, right) =>
|
||||||
(left, right) =>
|
right.loginRequests +
|
||||||
right.loginRequests +
|
right.otherRequests -
|
||||||
right.otherRequests -
|
(left.loginRequests + left.otherRequests),
|
||||||
(left.loginRequests + left.otherRequests),
|
);
|
||||||
)
|
|
||||||
.slice(0, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMultiLineSeries(rows: RPUsageDailyMetric[]): MultiLineSeries[] {
|
function buildMultiLineSeries(rows: RPUsageDailyMetric[]): MultiLineSeries[] {
|
||||||
@@ -222,6 +224,17 @@ function getISOWeekThursday(year: number, month: number, day: number) {
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDate(value?: string) {
|
||||||
|
if (!value) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const parsed = new Date(value);
|
||||||
|
if (Number.isNaN(parsed.getTime())) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return parsed.toLocaleDateString();
|
||||||
|
}
|
||||||
|
|
||||||
function formatPeriodLabel(date: string, period: RPUsagePeriod) {
|
function formatPeriodLabel(date: string, period: RPUsagePeriod) {
|
||||||
const parts = parseDateParts(date);
|
const parts = parseDateParts(date);
|
||||||
if (!parts) {
|
if (!parts) {
|
||||||
@@ -280,20 +293,17 @@ function RPUsageMixedChart({
|
|||||||
const colors = palette ?? usageChartPalettes[0];
|
const colors = palette ?? usageChartPalettes[0];
|
||||||
const daily = summarizeDaily(rows);
|
const daily = summarizeDaily(rows);
|
||||||
const series = summarizeSeries(rows);
|
const series = summarizeSeries(rows);
|
||||||
|
const topSeries = series.slice(0, 5);
|
||||||
|
const seriesByKey = new Map(series.map((item) => [item.key, item]));
|
||||||
const chartWidth = 720;
|
const chartWidth = 720;
|
||||||
const chartHeight = 230;
|
const chartHeight = 230;
|
||||||
const padX = 48;
|
const padX = 48;
|
||||||
const padTop = 32;
|
const padTop = 40;
|
||||||
const padBottom = 34;
|
const padBottom = 34;
|
||||||
const innerWidth = chartWidth - padX * 2;
|
const innerWidth = chartWidth - padX * 2;
|
||||||
const innerHeight = chartHeight - padTop - padBottom;
|
const innerHeight = chartHeight - padTop - padBottom;
|
||||||
const maxValue = Math.max(
|
const maxValue = Math.max(1, ...daily.map((point) => point.loginRequests));
|
||||||
1,
|
|
||||||
...daily.map((point) => point.loginRequests + point.otherRequests),
|
|
||||||
...daily.map((point) => point.loginRequests),
|
|
||||||
);
|
|
||||||
const slot = daily.length > 0 ? innerWidth / daily.length : innerWidth;
|
const slot = daily.length > 0 ? innerWidth / daily.length : innerWidth;
|
||||||
const barWidth = Math.min(28, Math.max(10, slot * 0.42));
|
|
||||||
const y = (value: number) =>
|
const y = (value: number) =>
|
||||||
padTop + innerHeight - (value / maxValue) * innerHeight;
|
padTop + innerHeight - (value / maxValue) * innerHeight;
|
||||||
const x = (index: number) => padX + slot * index + slot / 2;
|
const x = (index: number) => padX + slot * index + slot / 2;
|
||||||
@@ -325,40 +335,6 @@ function RPUsageMixedChart({
|
|||||||
className="h-[235px] min-w-[720px] w-full"
|
className="h-[235px] min-w-[720px] w-full"
|
||||||
>
|
>
|
||||||
<title>{t("ui.dev.dashboard.chart.aria", "RP 요청 현황")}</title>
|
<title>{t("ui.dev.dashboard.chart.aria", "RP 요청 현황")}</title>
|
||||||
<g transform="translate(510 10)">
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="3"
|
|
||||||
width="10"
|
|
||||||
height="10"
|
|
||||||
rx="2"
|
|
||||||
fill={colors.bar}
|
|
||||||
fillOpacity="0.7"
|
|
||||||
/>
|
|
||||||
<text x="16" y="12" className="fill-muted-foreground text-[11px]">
|
|
||||||
{t("ui.dev.dashboard.chart.other_requests", "기타 요청")}
|
|
||||||
</text>
|
|
||||||
{!multiLinePoints || multiLinePoints.length === 0 ? (
|
|
||||||
<>
|
|
||||||
<line
|
|
||||||
x1="78"
|
|
||||||
x2="98"
|
|
||||||
y1="8"
|
|
||||||
y2="8"
|
|
||||||
stroke={colors.line}
|
|
||||||
strokeWidth="3"
|
|
||||||
strokeLinecap="round"
|
|
||||||
/>
|
|
||||||
<text
|
|
||||||
x="104"
|
|
||||||
y="12"
|
|
||||||
className="fill-muted-foreground text-[11px]"
|
|
||||||
>
|
|
||||||
{t("ui.dev.dashboard.chart.login_requests", "로그인 요청")}
|
|
||||||
</text>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</g>
|
|
||||||
{[0, 0.25, 0.5, 0.75, 1].map((ratio) => {
|
{[0, 0.25, 0.5, 0.75, 1].map((ratio) => {
|
||||||
const gridY = padTop + innerHeight * ratio;
|
const gridY = padTop + innerHeight * ratio;
|
||||||
const label = Math.round(maxValue * (1 - ratio));
|
const label = Math.round(maxValue * (1 - ratio));
|
||||||
@@ -386,18 +362,8 @@ function RPUsageMixedChart({
|
|||||||
})}
|
})}
|
||||||
{daily.map((point, index) => {
|
{daily.map((point, index) => {
|
||||||
const center = x(index);
|
const center = x(index);
|
||||||
const otherHeight = (point.otherRequests / maxValue) * innerHeight;
|
|
||||||
return (
|
return (
|
||||||
<g key={point.date}>
|
<g key={point.date}>
|
||||||
<rect
|
|
||||||
x={center - barWidth / 2}
|
|
||||||
y={padTop + innerHeight - otherHeight}
|
|
||||||
width={barWidth}
|
|
||||||
height={otherHeight}
|
|
||||||
rx="3"
|
|
||||||
fill={colors.bar}
|
|
||||||
fillOpacity="0.7"
|
|
||||||
/>
|
|
||||||
<text
|
<text
|
||||||
x={center}
|
x={center}
|
||||||
y={chartHeight - 12}
|
y={chartHeight - 12}
|
||||||
@@ -459,30 +425,55 @@ function RPUsageMixedChart({
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||||
|
<span>{t("ui.dev.dashboard.chart.x_axis", "X축: 기간")}</span>
|
||||||
|
<span>{t("ui.dev.dashboard.chart.y_axis", "Y축: 로그인 요청 수")}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{multiLinePoints && multiLinePoints.length > 0 ? (
|
{multiLinePoints && multiLinePoints.length > 0 ? (
|
||||||
<div className="grid gap-x-6 gap-y-2 border-t border-border/60 pt-2 text-xs md:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-x-6 gap-y-2 border-t border-border/60 pt-2 text-xs md:grid-cols-2 xl:grid-cols-3">
|
||||||
{multiLinePoints.map((item) => (
|
{multiLinePoints.map((item) => {
|
||||||
<div key={item.key} className="flex min-w-0 items-center gap-2">
|
const seriesItem = seriesByKey.get(item.key);
|
||||||
<span
|
return (
|
||||||
className="h-2.5 w-2.5 rounded-full"
|
<div
|
||||||
style={{ backgroundColor: item.color.line }}
|
key={item.key}
|
||||||
/>
|
className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1"
|
||||||
<span className="truncate font-medium">{item.clientLabel}</span>
|
>
|
||||||
</div>
|
<span
|
||||||
))}
|
className="h-2.5 w-2.5 rounded-full"
|
||||||
|
style={{ backgroundColor: item.color.line }}
|
||||||
|
/>
|
||||||
|
<span className="font-medium">{item.clientLabel}</span>
|
||||||
|
{seriesItem ? (
|
||||||
|
<span className="whitespace-nowrap tabular-nums text-muted-foreground">
|
||||||
|
{t(
|
||||||
|
"ui.dev.dashboard.chart.series",
|
||||||
|
"로그인 {{login}} / 사용자 {{subjects}}",
|
||||||
|
{
|
||||||
|
login: seriesItem.loginRequests.toLocaleString(),
|
||||||
|
subjects: seriesItem.uniqueSubjects.toLocaleString(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : series.length > 0 ? (
|
) : topSeries.length > 0 ? (
|
||||||
<div className="grid gap-x-6 gap-y-2 border-t border-border/60 pt-2 text-xs md:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-x-6 gap-y-2 border-t border-border/60 pt-2 text-xs md:grid-cols-2 xl:grid-cols-3">
|
||||||
{series.map((item) => (
|
{topSeries.map((item) => (
|
||||||
<div key={item.key} className="flex min-w-0 items-center gap-2">
|
<div
|
||||||
<span className="truncate font-medium">{item.clientLabel}</span>
|
key={item.key}
|
||||||
<span className="ml-auto whitespace-nowrap tabular-nums">
|
className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1"
|
||||||
|
>
|
||||||
|
<span className="font-medium">{item.clientLabel}</span>
|
||||||
|
<span className="whitespace-nowrap tabular-nums text-muted-foreground">
|
||||||
{t(
|
{t(
|
||||||
"ui.dev.dashboard.chart.series",
|
"ui.dev.dashboard.chart.series",
|
||||||
"로그인 {{login}} / 기타 {{other}} / 사용자 {{subjects}}",
|
"로그인 {{login}} / 사용자 {{subjects}}",
|
||||||
{
|
{
|
||||||
login: item.loginRequests.toLocaleString(),
|
login: item.loginRequests.toLocaleString(),
|
||||||
other: item.otherRequests.toLocaleString(),
|
|
||||||
subjects: item.uniqueSubjects.toLocaleString(),
|
subjects: item.uniqueSubjects.toLocaleString(),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
@@ -496,9 +487,20 @@ function RPUsageMixedChart({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DashboardPage() {
|
function DashboardPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const auth = useAuth();
|
||||||
|
const hasAccessToken = Boolean(auth.user?.access_token);
|
||||||
|
const userProfile = auth.user?.profile as Record<string, unknown> | undefined;
|
||||||
|
const role = resolveProfileRole(userProfile);
|
||||||
|
const tenantId = userProfile?.tenant_id as string | undefined;
|
||||||
const [period, setPeriod] = useState<RPUsagePeriod>("day");
|
const [period, setPeriod] = useState<RPUsagePeriod>("day");
|
||||||
const [selectedClientIds, setSelectedClientIds] = useState<string[]>([]);
|
const [selectedClientIds, setSelectedClientIds] = useState<string[]>([]);
|
||||||
const usageDays = period === "day" ? 14 : period === "week" ? 84 : 90;
|
const usageDays = period === "day" ? 14 : period === "week" ? 84 : 90;
|
||||||
|
const { data: requestStatus, isLoading: isLoadingRequestStatus } = useQuery({
|
||||||
|
queryKey: ["developer-request", tenantId],
|
||||||
|
queryFn: () => fetchDeveloperRequestStatus(tenantId),
|
||||||
|
enabled: hasAccessToken && role === "user",
|
||||||
|
});
|
||||||
const statsQuery = useQuery({
|
const statsQuery = useQuery({
|
||||||
queryKey: ["dev-dashboard-stats"],
|
queryKey: ["dev-dashboard-stats"],
|
||||||
queryFn: fetchDevStats,
|
queryFn: fetchDevStats,
|
||||||
@@ -520,6 +522,17 @@ function DashboardPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const clients = clientsQuery.data?.items ?? [];
|
const clients = clientsQuery.data?.items ?? [];
|
||||||
|
const hasDeveloperAccess =
|
||||||
|
role === "super_admin" ||
|
||||||
|
role === "tenant_admin" ||
|
||||||
|
role === "rp_admin" ||
|
||||||
|
requestStatus?.status === "approved";
|
||||||
|
const isDeveloperRequestPending = requestStatus?.status === "pending";
|
||||||
|
const canRequestDeveloperAccess =
|
||||||
|
(role === "user" || role === "tenant_member") &&
|
||||||
|
!isLoadingRequestStatus &&
|
||||||
|
!hasDeveloperAccess &&
|
||||||
|
!isDeveloperRequestPending;
|
||||||
const distribution = useMemo(
|
const distribution = useMemo(
|
||||||
() => buildClientDistribution(clients),
|
() => buildClientDistribution(clients),
|
||||||
[clients],
|
[clients],
|
||||||
@@ -607,6 +620,59 @@ function DashboardPage() {
|
|||||||
setSelectedClientIds([]);
|
setSelectedClientIds([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ((role === "user" || role === "tenant_member") && isLoadingRequestStatus) {
|
||||||
|
return (
|
||||||
|
<div className="p-8 text-center">
|
||||||
|
{t("ui.common.loading", "Loading...")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDeveloperAccess) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-xl border border-border/60 bg-card p-8 text-center">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h2 className="text-2xl font-semibold tracking-tight">
|
||||||
|
{t("ui.dev.nav.overview", "개요")}
|
||||||
|
</h2>
|
||||||
|
<p className="font-medium text-foreground">
|
||||||
|
{isDeveloperRequestPending
|
||||||
|
? t(
|
||||||
|
"msg.dev.dashboard.access_pending",
|
||||||
|
"개발자 권한 신청을 검토 중입니다.",
|
||||||
|
)
|
||||||
|
: t(
|
||||||
|
"msg.dev.dashboard.access_denied",
|
||||||
|
"개요는 개발자 권한이 있어야 볼 수 있습니다.",
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{isDeveloperRequestPending
|
||||||
|
? t(
|
||||||
|
"msg.dev.dashboard.access_pending_detail",
|
||||||
|
"super admin이 승인하면 개요와 개발자 기능을 사용할 수 있습니다.",
|
||||||
|
)
|
||||||
|
: t(
|
||||||
|
"msg.dev.dashboard.access_denied_detail",
|
||||||
|
"개발자 권한 신청 페이지에서 신청을 등록한 뒤 승인을 받아주세요.",
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
{(isDeveloperRequestPending || canRequestDeveloperAccess) && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="font-bold text-primary hover:underline"
|
||||||
|
onClick={() => navigate("/developer-requests")}
|
||||||
|
>
|
||||||
|
{isDeveloperRequestPending
|
||||||
|
? t("ui.dev.nav.developer_request", "개발자 권한 신청")
|
||||||
|
: t("ui.dev.welcome.btn_request", "개발자 등록 신청하기")}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 animate-in fade-in duration-500">
|
<div className="space-y-4 animate-in fade-in duration-500">
|
||||||
<div className="flex flex-wrap items-end justify-between gap-4">
|
<div className="flex flex-wrap items-end justify-between gap-4">
|
||||||
@@ -730,7 +796,7 @@ function DashboardPage() {
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div className="grid gap-4 lg:grid-cols-[1.1fr,0.9fr]">
|
<div className="grid gap-4 lg:grid-cols-[0.9fr,1.1fr]">
|
||||||
<section className="rounded-xl border border-border/60 bg-card p-6">
|
<section className="rounded-xl border border-border/60 bg-card p-6">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Layers3 size={18} className="text-primary" />
|
<Layers3 size={18} className="text-primary" />
|
||||||
@@ -744,10 +810,10 @@ function DashboardPage() {
|
|||||||
<p className="mt-1 text-sm text-muted-foreground">
|
<p className="mt-1 text-sm text-muted-foreground">
|
||||||
{t(
|
{t(
|
||||||
"msg.dev.dashboard.distribution.description",
|
"msg.dev.dashboard.distribution.description",
|
||||||
"애플리케이션 유형과 headless login 사용 현황을 빠르게 확인합니다.",
|
"애플리케이션 유형 분포와 server side app의 headless login 사용 현황을 빠르게 확인합니다.",
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-4 grid gap-3 sm:grid-cols-3">
|
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||||
<div className="rounded-xl border border-border/60 bg-secondary/30 p-4">
|
<div className="rounded-xl border border-border/60 bg-secondary/30 p-4">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{t("ui.dev.dashboard.distribution.private", "Server side App")}
|
{t("ui.dev.dashboard.distribution.private", "Server side App")}
|
||||||
@@ -755,6 +821,15 @@ function DashboardPage() {
|
|||||||
<p className="mt-2 text-2xl font-semibold tabular-nums">
|
<p className="mt-2 text-2xl font-semibold tabular-nums">
|
||||||
{distribution.privateClients.toLocaleString()}
|
{distribution.privateClients.toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
|
<p className="mt-1 text-xs text-muted-foreground">
|
||||||
|
{t(
|
||||||
|
"ui.dev.dashboard.distribution.headless_hint",
|
||||||
|
"이 중 Headless Login 사용 {{count}}",
|
||||||
|
{
|
||||||
|
count: distribution.headlessClients.toLocaleString(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-border/60 bg-secondary/30 p-4">
|
<div className="rounded-xl border border-border/60 bg-secondary/30 p-4">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
@@ -764,14 +839,6 @@ function DashboardPage() {
|
|||||||
{distribution.pkceClients.toLocaleString()}
|
{distribution.pkceClients.toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-border/60 bg-secondary/30 p-4">
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("ui.dev.dashboard.distribution.headless", "Headless Login")}
|
|
||||||
</p>
|
|
||||||
<p className="mt-2 text-2xl font-semibold tabular-nums">
|
|
||||||
{distribution.headlessClients.toLocaleString()}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -803,7 +870,7 @@ function DashboardPage() {
|
|||||||
className="flex items-center justify-between gap-3 rounded-lg border border-border/40 px-3 py-2"
|
className="flex items-center justify-between gap-3 rounded-lg border border-border/40 px-3 py-2"
|
||||||
>
|
>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="truncate font-medium">
|
<p className="truncate font-semibold">
|
||||||
{client.name || t("ui.dev.clients.untitled", "Untitled")}
|
{client.name || t("ui.dev.clients.untitled", "Untitled")}
|
||||||
</p>
|
</p>
|
||||||
<p className="truncate text-xs text-muted-foreground">
|
<p className="truncate text-xs text-muted-foreground">
|
||||||
@@ -828,6 +895,10 @@ function DashboardPage() {
|
|||||||
? t("ui.dev.clients.status.inactive", "비활성")
|
? t("ui.dev.clients.status.inactive", "비활성")
|
||||||
: client.status || "-"}
|
: client.status || "-"}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("ui.dev.clients.table.created_at", "생성일")}{" "}
|
||||||
|
{formatDate(client.createdAt)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -500,6 +500,10 @@ openid = "Openid"
|
|||||||
profile = "Profile"
|
profile = "Profile"
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = "Overview is available only to users with developer access."
|
||||||
|
access_denied_detail = "Submit a request on the developer access page and wait for approval."
|
||||||
|
access_pending = "Your developer access request is under review."
|
||||||
|
access_pending_detail = "You can use the overview and developer features after a super admin approves it."
|
||||||
description = "View connected application composition and authentication operations metrics in one place."
|
description = "View connected application composition and authentication operations metrics in one place."
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -1664,7 +1668,7 @@ registry = "RP registry"
|
|||||||
rp_synced = "RP registry synced"
|
rp_synced = "RP registry synced"
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = "Headless Login"
|
headless_hint = "{{count}} with Headless Login enabled"
|
||||||
pkce = "PKCE"
|
pkce = "PKCE"
|
||||||
private = "Server side App"
|
private = "Server side App"
|
||||||
title = "Application Distribution"
|
title = "Application Distribution"
|
||||||
@@ -1672,13 +1676,13 @@ title = "Application Distribution"
|
|||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
aria = "RP request overview"
|
aria = "RP request overview"
|
||||||
filter_all = "All"
|
filter_all = "All"
|
||||||
login_requests = "Login requests"
|
|
||||||
other_requests = "Other requests"
|
|
||||||
period_day = "Day"
|
period_day = "Day"
|
||||||
period_month = "Month"
|
period_month = "Month"
|
||||||
period_week = "Week"
|
period_week = "Week"
|
||||||
series = "Login {{login}} / Other {{other}} / Users {{subjects}}"
|
series = "Login {{login}} / Users {{subjects}}"
|
||||||
title = "Login and other requests by application"
|
title = "Login requests by application"
|
||||||
|
x_axis = "X-axis: Period"
|
||||||
|
y_axis = "Y-axis: Login requests"
|
||||||
|
|
||||||
[ui.dev.dashboard.next]
|
[ui.dev.dashboard.next]
|
||||||
subtitle = "Ship the RP controls"
|
subtitle = "Ship the RP controls"
|
||||||
|
|||||||
@@ -500,6 +500,10 @@ openid = "OIDC 인증 필수 스코프"
|
|||||||
profile = "기본 프로필 정보 접근"
|
profile = "기본 프로필 정보 접근"
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = "개요는 개발자 권한이 있어야 볼 수 있습니다."
|
||||||
|
access_denied_detail = "개발자 권한 신청 페이지에서 신청을 등록한 뒤 승인을 받아주세요."
|
||||||
|
access_pending = "개발자 권한 신청을 검토 중입니다."
|
||||||
|
access_pending_detail = "super admin이 승인하면 개요와 개발자 기능을 사용할 수 있습니다."
|
||||||
description = "연동 앱 구성과 인증 운영 지표를 한 곳에서 확인합니다."
|
description = "연동 앱 구성과 인증 운영 지표를 한 곳에서 확인합니다."
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -1663,7 +1667,7 @@ registry = "RP registry"
|
|||||||
rp_synced = "RP registry synced"
|
rp_synced = "RP registry synced"
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = "Headless Login"
|
headless_hint = "이 중 Headless Login 사용 {{count}}"
|
||||||
pkce = "PKCE"
|
pkce = "PKCE"
|
||||||
private = "Server side App"
|
private = "Server side App"
|
||||||
title = "애플리케이션 구성 요약"
|
title = "애플리케이션 구성 요약"
|
||||||
@@ -1671,13 +1675,13 @@ title = "애플리케이션 구성 요약"
|
|||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
aria = "RP 요청 현황"
|
aria = "RP 요청 현황"
|
||||||
filter_all = "전체"
|
filter_all = "전체"
|
||||||
login_requests = "로그인 요청"
|
|
||||||
other_requests = "기타 요청"
|
|
||||||
period_day = "일"
|
period_day = "일"
|
||||||
period_month = "월"
|
period_month = "월"
|
||||||
period_week = "주"
|
period_week = "주"
|
||||||
series = "로그인 {{login}} / 기타 {{other}} / 사용자 {{subjects}}"
|
series = "로그인 {{login}} / 사용자 {{subjects}}"
|
||||||
title = "애플리케이션별 로그인요청/기타 요청 현황"
|
title = "애플리케이션별 로그인 요청 현황"
|
||||||
|
x_axis = "X축: 기간"
|
||||||
|
y_axis = "Y축: 로그인 요청 수"
|
||||||
|
|
||||||
[ui.dev.dashboard.next]
|
[ui.dev.dashboard.next]
|
||||||
subtitle = "Ship the RP controls"
|
subtitle = "Ship the RP controls"
|
||||||
|
|||||||
@@ -538,6 +538,10 @@ openid = ""
|
|||||||
profile = ""
|
profile = ""
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = ""
|
||||||
|
access_denied_detail = ""
|
||||||
|
access_pending = ""
|
||||||
|
access_pending_detail = ""
|
||||||
description = ""
|
description = ""
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -1720,7 +1724,7 @@ registry = ""
|
|||||||
rp_synced = ""
|
rp_synced = ""
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = ""
|
headless_hint = ""
|
||||||
pkce = ""
|
pkce = ""
|
||||||
private = ""
|
private = ""
|
||||||
title = ""
|
title = ""
|
||||||
@@ -1728,13 +1732,13 @@ title = ""
|
|||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
aria = ""
|
aria = ""
|
||||||
filter_all = ""
|
filter_all = ""
|
||||||
login_requests = ""
|
|
||||||
other_requests = ""
|
|
||||||
period_day = ""
|
period_day = ""
|
||||||
period_month = ""
|
period_month = ""
|
||||||
period_week = ""
|
period_week = ""
|
||||||
series = ""
|
series = ""
|
||||||
title = ""
|
title = ""
|
||||||
|
x_axis = ""
|
||||||
|
y_axis = ""
|
||||||
|
|
||||||
[ui.dev.dashboard.next]
|
[ui.dev.dashboard.next]
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
|
|||||||
@@ -435,6 +435,8 @@ services:
|
|||||||
- "${ADMINFRONT_PORT:-5173}:5173"
|
- "${ADMINFRONT_PORT:-5173}:5173"
|
||||||
volumes:
|
volumes:
|
||||||
- ./adminfront:/app
|
- ./adminfront:/app
|
||||||
|
- ./common:/common
|
||||||
|
- ./locales:/locales
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
networks:
|
networks:
|
||||||
- baron_net
|
- baron_net
|
||||||
@@ -459,6 +461,8 @@ services:
|
|||||||
- "${DEVFRONT_PORT:-5174}:5173"
|
- "${DEVFRONT_PORT:-5174}:5173"
|
||||||
volumes:
|
volumes:
|
||||||
- ./devfront:/app
|
- ./devfront:/app
|
||||||
|
- ./common:/common
|
||||||
|
- ./locales:/locales
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
networks:
|
networks:
|
||||||
- baron_net
|
- baron_net
|
||||||
@@ -484,6 +488,8 @@ services:
|
|||||||
- "${ORGFRONT_PORT:-5175}:5175"
|
- "${ORGFRONT_PORT:-5175}:5175"
|
||||||
volumes:
|
volumes:
|
||||||
- ./orgfront:/app
|
- ./orgfront:/app
|
||||||
|
- ./common:/common
|
||||||
|
- ./locales:/locales
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
networks:
|
networks:
|
||||||
- baron_net
|
- baron_net
|
||||||
|
|||||||
@@ -553,6 +553,10 @@ openid = "Openid"
|
|||||||
profile = "Profile"
|
profile = "Profile"
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = "Overview is available only to users with developer access."
|
||||||
|
access_denied_detail = "Submit a request on the developer access page and wait for approval."
|
||||||
|
access_pending = "Your developer access request is under review."
|
||||||
|
access_pending_detail = "You can use the overview and developer features after a super admin approves it."
|
||||||
description = "Review RP composition and authentication operations in one place."
|
description = "Review RP composition and authentication operations in one place."
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -2210,21 +2214,21 @@ registry = "RP registry"
|
|||||||
rp_synced = "RP registry synced"
|
rp_synced = "RP registry synced"
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = "Headless Login"
|
headless_hint = "{{count}} with Headless Login enabled"
|
||||||
pkce = "PKCE"
|
pkce = "PKCE"
|
||||||
private = "Server side App"
|
private = "Server side App"
|
||||||
title = "Application Distribution"
|
title = "Application Distribution"
|
||||||
|
|
||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
|
x_axis = "X-axis: Period"
|
||||||
|
y_axis = "Y-axis: Login requests"
|
||||||
aria = "RP request overview"
|
aria = "RP request overview"
|
||||||
filter_all = "All"
|
filter_all = "All"
|
||||||
login_requests = "Login requests"
|
|
||||||
other_requests = "Other requests"
|
|
||||||
period_day = "Day"
|
period_day = "Day"
|
||||||
period_month = "Month"
|
period_month = "Month"
|
||||||
period_week = "Week"
|
period_week = "Week"
|
||||||
series = "Login {{login}} / Other {{other}} / Users {{subjects}}"
|
series = "Login {{login}} / Users {{subjects}}"
|
||||||
title = "Login and other requests by application"
|
title = "Login requests by application"
|
||||||
|
|
||||||
[ui.dev.dashboard.next]
|
[ui.dev.dashboard.next]
|
||||||
subtitle = "Ship the RP controls"
|
subtitle = "Ship the RP controls"
|
||||||
@@ -2267,6 +2271,7 @@ plane = "Dev Plane"
|
|||||||
subtitle = "Manage your applications"
|
subtitle = "Manage your applications"
|
||||||
|
|
||||||
[ui.dev.nav]
|
[ui.dev.nav]
|
||||||
|
overview = "Overview"
|
||||||
clients = "Connected Application"
|
clients = "Connected Application"
|
||||||
logout = "Logout"
|
logout = "Logout"
|
||||||
developer_request = "Developer Access Request"
|
developer_request = "Developer Access Request"
|
||||||
|
|||||||
@@ -405,6 +405,7 @@ pending = "준비 중"
|
|||||||
success = "성공"
|
success = "성공"
|
||||||
|
|
||||||
[ui.dev.nav]
|
[ui.dev.nav]
|
||||||
|
overview = "개요"
|
||||||
clients = "연동 앱"
|
clients = "연동 앱"
|
||||||
logout = "로그아웃"
|
logout = "로그아웃"
|
||||||
developer_request = "개발자 권한 신청"
|
developer_request = "개발자 권한 신청"
|
||||||
@@ -1044,6 +1045,10 @@ openid = "OIDC 인증 필수 스코프"
|
|||||||
profile = "기본 프로필 정보 접근"
|
profile = "기본 프로필 정보 접근"
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = "개요는 개발자 권한이 있어야 볼 수 있습니다."
|
||||||
|
access_denied_detail = "개발자 권한 신청 페이지에서 신청을 등록한 뒤 승인을 받아주세요."
|
||||||
|
access_pending = "개발자 권한 신청을 검토 중입니다."
|
||||||
|
access_pending_detail = "super admin이 승인하면 개요와 개발자 기능을 사용할 수 있습니다."
|
||||||
description = "연동 앱 구성과 인증 운영 지표를 한 곳에서 확인합니다."
|
description = "연동 앱 구성과 인증 운영 지표를 한 곳에서 확인합니다."
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -2672,21 +2677,21 @@ registry = "RP registry"
|
|||||||
rp_synced = "RP registry synced"
|
rp_synced = "RP registry synced"
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = "Headless Login"
|
headless_hint = "이 중 Headless Login 사용 {{count}}"
|
||||||
pkce = "PKCE"
|
pkce = "PKCE"
|
||||||
private = "Server side App"
|
private = "Server side App"
|
||||||
title = "애플리케이션 구성 요약"
|
title = "애플리케이션 구성 요약"
|
||||||
|
|
||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
|
x_axis = "X축: 기간"
|
||||||
|
y_axis = "Y축: 로그인 요청 수"
|
||||||
aria = "RP 요청 현황"
|
aria = "RP 요청 현황"
|
||||||
filter_all = "전체"
|
filter_all = "전체"
|
||||||
login_requests = "로그인 요청"
|
|
||||||
other_requests = "기타 요청"
|
|
||||||
period_day = "일"
|
period_day = "일"
|
||||||
period_month = "월"
|
period_month = "월"
|
||||||
period_week = "주"
|
period_week = "주"
|
||||||
series = "로그인 {{login}} / 기타 {{other}} / 사용자 {{subjects}}"
|
series = "로그인 {{login}} / 사용자 {{subjects}}"
|
||||||
title = "애플리케이션별 로그인요청/기타 요청 현황"
|
title = "애플리케이션별 로그인 요청 현황"
|
||||||
|
|
||||||
[ui.dev.dashboard.next]
|
[ui.dev.dashboard.next]
|
||||||
subtitle = "Ship the RP controls"
|
subtitle = "Ship the RP controls"
|
||||||
|
|||||||
@@ -908,6 +908,10 @@ openid = ""
|
|||||||
profile = ""
|
profile = ""
|
||||||
|
|
||||||
[msg.dev.dashboard]
|
[msg.dev.dashboard]
|
||||||
|
access_denied = ""
|
||||||
|
access_denied_detail = ""
|
||||||
|
access_pending = ""
|
||||||
|
access_pending_detail = ""
|
||||||
description = ""
|
description = ""
|
||||||
|
|
||||||
[msg.dev.dashboard.hero]
|
[msg.dev.dashboard.hero]
|
||||||
@@ -2551,16 +2555,16 @@ registry = ""
|
|||||||
rp_synced = ""
|
rp_synced = ""
|
||||||
|
|
||||||
[ui.dev.dashboard.distribution]
|
[ui.dev.dashboard.distribution]
|
||||||
headless = ""
|
headless_hint = ""
|
||||||
pkce = ""
|
pkce = ""
|
||||||
private = ""
|
private = ""
|
||||||
title = ""
|
title = ""
|
||||||
|
|
||||||
[ui.dev.dashboard.chart]
|
[ui.dev.dashboard.chart]
|
||||||
|
x_axis = ""
|
||||||
|
y_axis = ""
|
||||||
aria = ""
|
aria = ""
|
||||||
filter_all = ""
|
filter_all = ""
|
||||||
login_requests = ""
|
|
||||||
other_requests = ""
|
|
||||||
period_day = ""
|
period_day = ""
|
||||||
period_month = ""
|
period_month = ""
|
||||||
period_week = ""
|
period_week = ""
|
||||||
@@ -2608,6 +2612,7 @@ plane = ""
|
|||||||
subtitle = ""
|
subtitle = ""
|
||||||
|
|
||||||
[ui.dev.nav]
|
[ui.dev.nav]
|
||||||
|
overview = ""
|
||||||
clients = ""
|
clients = ""
|
||||||
logout = ""
|
logout = ""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user