1
0
forked from baron/baron-sso

누락 locale 키 추가 및 린트 정리

This commit is contained in:
2026-06-09 17:49:22 +09:00
parent 437a3ad98d
commit b919f600e1
11 changed files with 266 additions and 74 deletions

View File

@@ -17,6 +17,7 @@ import { useCallback, useEffect, useState } from "react";
import { useAuth } from "react-oidc-context";
import { Link, useNavigate, useParams } from "react-router-dom";
import { PageHeader } from "../../../../common/core/components/page";
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import {
@@ -30,7 +31,6 @@ import { Input } from "../../components/ui/input";
import { Label } from "../../components/ui/label";
import { Switch } from "../../components/ui/switch";
import { Textarea } from "../../components/ui/textarea";
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
import { toast } from "../../components/ui/use-toast";
import type {
ClientStatus,
@@ -1208,7 +1208,10 @@ function ClientGeneralPage() {
"msg.dev.clients.general.create_forbidden_detail",
"개발자 권한 신청에서 연동 앱 추가 권한을 선택한 뒤 승인받아주세요.",
)}
actionLabel={t("ui.dev.welcome.btn_request", "개발자 등록 신청하기")}
actionLabel={t(
"ui.dev.welcome.btn_request",
"개발자 등록 신청하기",
)}
onAction={() => navigate("/developer-requests")}
/>
</div>

View File

@@ -17,11 +17,11 @@ import {
toggleSort,
} from "../../../../common/core/utils";
import { SearchFilterBar } from "../../../../common/ui/search-filter-bar";
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard";
import { ForbiddenMessage } from "../../components/common/ForbiddenMessage";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
@@ -243,7 +243,10 @@ function ClientsPage() {
"msg.dev.clients.access_denied_detail",
"개발자 권한 신청에서 개요 또는 연동 앱 추가 권한을 선택한 뒤 승인받아주세요.",
)}
actionLabel={t("ui.dev.welcome.btn_request", "개발자 등록 신청하기")}
actionLabel={t(
"ui.dev.welcome.btn_request",
"개발자 등록 신청하기",
)}
onAction={() => navigate("/developer-requests")}
/>
</div>
@@ -506,11 +509,11 @@ function ClientsPage() {
"msg.dev.clients.empty_can_create",
"아직 등록된 연동 앱이 없습니다.",
)
: isClientCreatePending
? t(
"msg.dev.clients.empty_pending",
"개발자 권한 신청을 검토 중입니다.",
)
: isClientCreatePending
? t(
"msg.dev.clients.empty_pending",
"개발자 권한 신청을 검토 중입니다.",
)
: t(
"msg.dev.clients.empty",
"조회 가능한 RP가 없습니다.",
@@ -528,7 +531,7 @@ function ClientsPage() {
"msg.dev.clients.empty_can_create_detail",
"연동 앱 추가 버튼으로 새 RP를 생성하면 이 목록에 표시됩니다.",
)
: isClientCreatePending
: isClientCreatePending
? t(
"msg.dev.clients.empty_pending_detail",
"super admin이 승인하면 연동 앱을 추가할 수 있습니다.",

View File

@@ -25,18 +25,16 @@ export function normalizeDeveloperAccessPages(
): DeveloperAccessPage[] {
const normalized = new Set<DeveloperAccessPage>();
for (const raw of pages) {
const page = String(raw ?? "").trim().toLowerCase();
const page = String(raw ?? "")
.trim()
.toLowerCase();
if (!page) {
continue;
}
if (page === "all") {
return ["all"];
}
if (
page === "overview" ||
page === "client_create" ||
page === "audit"
) {
if (page === "overview" || page === "client_create" || page === "audit") {
normalized.add(page);
}
}

View File

@@ -27,20 +27,20 @@ import { Textarea } from "../../components/ui/textarea";
import { toast } from "../../components/ui/use-toast";
import {
createDeveloperGrant,
fetchDeveloperGrants,
fetchDevUsers,
fetchDevUser,
revokeDeveloperGrant,
type DevAssignableUser,
fetchDeveloperGrants,
fetchDevUser,
fetchDevUsers,
revokeDeveloperGrant,
} from "../../lib/devApi";
import { t } from "../../lib/i18n";
import { resolveProfileRole } from "../../lib/role";
import { fetchMe } from "../auth/authApi";
import {
developerAccessPageOptions,
normalizeDeveloperAccessPages,
normalizeDeveloperAccessPageSelection,
type DeveloperAccessPage,
developerAccessPageOptions,
normalizeDeveloperAccessPageSelection,
normalizeDeveloperAccessPages,
} from "../developer-access/developerAccessPages";
function formatUserLabel(user: DevAssignableUser) {
@@ -74,10 +74,7 @@ export default function DeveloperGrantsPage() {
const [grantNotes, setGrantNotes] = useState("");
const [adminNotes, setAdminNotes] = useState<Record<number, string>>({});
const {
data: userSearchData,
isFetching: isUserSearchLoading,
} = useQuery({
const { data: userSearchData, isFetching: isUserSearchLoading } = useQuery({
queryKey: ["developer-grant-users", deferredUserSearch],
queryFn: () => fetchDevUsers(deferredUserSearch, 10),
enabled:
@@ -87,14 +84,12 @@ export default function DeveloperGrantsPage() {
selectedUser == null,
});
const {
data: selectedUserDetail,
isFetching: isSelectedUserDetailLoading,
} = useQuery({
queryKey: ["developer-grant-user", selectedUser?.id],
queryFn: () => fetchDevUser(selectedUser?.id || ""),
enabled: hasAccessToken && isSuperAdmin && selectedUser != null,
});
const { data: selectedUserDetail, isFetching: isSelectedUserDetailLoading } =
useQuery({
queryKey: ["developer-grant-user", selectedUser?.id],
queryFn: () => fetchDevUser(selectedUser?.id || ""),
enabled: hasAccessToken && isSuperAdmin && selectedUser != null,
});
const {
data: grants,
@@ -145,13 +140,8 @@ export default function DeveloperGrantsPage() {
});
const revokeGrantMutation = useMutation({
mutationFn: ({
id,
adminNotes,
}: {
id: number;
adminNotes: string;
}) => revokeDeveloperGrant(id, adminNotes),
mutationFn: ({ id, adminNotes }: { id: number; adminNotes: string }) =>
revokeDeveloperGrant(id, adminNotes),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["developer-grants"] });
toast(
@@ -203,10 +193,7 @@ export default function DeveloperGrantsPage() {
const handleGrant = () => {
if (!selectedUser) {
toast(
t(
"msg.dev.grants.user_required",
"부여할 사용자를 선택해주세요.",
),
t("msg.dev.grants.user_required", "부여할 사용자를 선택해주세요."),
"error",
);
return;
@@ -374,10 +361,7 @@ export default function DeveloperGrantsPage() {
<CardHeader className="space-y-1 pb-3">
<div className="flex items-center justify-between gap-2">
<CardTitle className="text-base">
{t(
"ui.dev.grants.selected_info",
"선택된 사용자 정보",
)}
{t("ui.dev.grants.selected_info", "선택된 사용자 정보")}
</CardTitle>
<Badge variant="secondary">
{t("ui.dev.grants.read_only", "읽기 전용")}
@@ -476,7 +460,9 @@ export default function DeveloperGrantsPage() {
<input
type="checkbox"
checked={checked}
onChange={() => handleAccessPageToggle(option.value)}
onChange={() =>
handleAccessPageToggle(option.value)
}
/>
<span className="font-medium">{option.label}</span>
</label>
@@ -577,8 +563,12 @@ export default function DeveloperGrantsPage() {
<TableRow>
<TableHead>{t("ui.dev.grants.user", "사용자")}</TableHead>
<TableHead>{t("ui.dev.grants.tenant", "테넌트")}</TableHead>
<TableHead>{t("ui.dev.grants.reason", "부여 사유")}</TableHead>
<TableHead>{t("ui.dev.grants.pages", "권한 페이지")}</TableHead>
<TableHead>
{t("ui.dev.grants.reason", "부여 사유")}
</TableHead>
<TableHead>
{t("ui.dev.grants.pages", "권한 페이지")}
</TableHead>
<TableHead>{t("ui.dev.grants.status", "상태")}</TableHead>
<TableHead>{t("ui.dev.grants.date", "부여일")}</TableHead>
<TableHead className="text-right">
@@ -597,7 +587,7 @@ export default function DeveloperGrantsPage() {
<div className="text-xs text-muted-foreground">
{grant.email || grant.userId}
</div>
<div className="font-mono text-xs text-muted-foreground">
<div className="font-mono text-xs text-muted-foreground">
{grant.userId}
</div>
</div>
@@ -605,7 +595,9 @@ export default function DeveloperGrantsPage() {
<TableCell>
<div className="space-y-1">
<div className="font-medium">
{grant.organization || grant.tenantId || t("ui.common.na", "없음")}
{grant.organization ||
grant.tenantId ||
t("ui.common.na", "없음")}
</div>
<div className="font-mono text-xs text-muted-foreground">
{grant.tenantId || t("ui.common.na", "없음")}

View File

@@ -48,10 +48,10 @@ import { t } from "../../lib/i18n";
import { resolveProfileRole } from "../../lib/role";
import { fetchMe } from "../auth/authApi";
import {
developerAccessPageOptions,
normalizeDeveloperAccessPages,
normalizeDeveloperAccessPageSelection,
type DeveloperAccessPage,
developerAccessPageOptions,
normalizeDeveloperAccessPageSelection,
normalizeDeveloperAccessPages,
} from "../developer-access/developerAccessPages";
export default function DeveloperRequestPage() {
@@ -158,9 +158,7 @@ export default function DeveloperRequestPage() {
);
}
const hasActiveRequest = requests?.some(
(r) => r.status === "pending",
);
const hasActiveRequest = requests?.some((r) => r.status === "pending");
const approvedRequestCount =
requests?.filter((request) => request.status === "approved").length ?? 0;
const isActionPending =
@@ -269,7 +267,8 @@ export default function DeveloperRequestPage() {
</TableCell>
)}
<TableCell>
{req.organization?.trim() || t("ui.common.na", "없음")}
{req.organization?.trim() ||
t("ui.common.na", "없음")}
</TableCell>
<TableCell className="max-w-md">
<div className="truncate" title={req.reason}>
@@ -284,15 +283,15 @@ export default function DeveloperRequestPage() {
<TableCell>
<div className="flex flex-wrap gap-1">
{req.accessPages?.length ? (
normalizeDeveloperAccessPages(req.accessPages).map(
(page) => (
<Badge key={page} variant="outline">
{developerAccessPageOptions.find(
(option) => option.value === page,
)?.label ?? page}
</Badge>
),
)
normalizeDeveloperAccessPages(
req.accessPages,
).map((page) => (
<Badge key={page} variant="outline">
{developerAccessPageOptions.find(
(option) => option.value === page,
)?.label ?? page}
</Badge>
))
) : (
<Badge variant="secondary">
{t("ui.common.na", "없음")}