From 6512fea8fed9587f5a70e816b31a311ea11f954b Mon Sep 17 00:00:00 2001 From: kyy Date: Tue, 26 May 2026 15:50:39 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=9C=EC=9A=94=20=EC=B0=A8=EB=8B=A8=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=97=90=20=EA=B0=9C=EB=B0=9C=EC=9E=90=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=8B=A0=EC=B2=AD=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/overview/GlobalOverviewPage.tsx | 26 +++++++++++------- .../tests/devfront-role-switch-report.spec.ts | 27 +++++++++++++++++++ devfront/tests/helpers/devfront-fixtures.ts | 9 +++++++ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/devfront/src/features/overview/GlobalOverviewPage.tsx b/devfront/src/features/overview/GlobalOverviewPage.tsx index 685f9762..16e64ec5 100644 --- a/devfront/src/features/overview/GlobalOverviewPage.tsx +++ b/devfront/src/features/overview/GlobalOverviewPage.tsx @@ -27,6 +27,7 @@ import { } from "../../lib/devApi"; import { t } from "../../lib/i18n"; import { resolveProfileRole } from "../../lib/role"; +import { fetchMe } from "../auth/authApi"; type ClientDistribution = { activeClients: number; @@ -480,13 +481,19 @@ function GlobalOverviewPage() { const userProfile = auth.user?.profile as Record | undefined; const role = resolveProfileRole(userProfile); const tenantId = userProfile?.tenant_id as string | undefined; + const { data: me, isLoading: isLoadingMe } = useQuery({ + queryKey: ["userMe"], + queryFn: fetchMe, + enabled: hasAccessToken, + }); + const profileRole = me?.role?.trim() || role; const [period, setPeriod] = useState("day"); const [selectedClientIds, setSelectedClientIds] = useState([]); 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", + enabled: hasAccessToken && profileRole === "user", }); const statsQuery = useQuery({ queryKey: ["dev-dashboard-stats"], @@ -510,13 +517,13 @@ function GlobalOverviewPage() { const clients = clientsQuery.data?.items ?? []; const hasDeveloperAccess = - role === "super_admin" || - role === "tenant_admin" || - role === "rp_admin" || + profileRole === "super_admin" || + profileRole === "tenant_admin" || + profileRole === "rp_admin" || requestStatus?.status === "approved"; const isDeveloperRequestPending = requestStatus?.status === "pending"; const canRequestDeveloperAccess = - (role === "user" || role === "tenant_member") && + (profileRole === "user" || profileRole === "tenant_member") && !isLoadingRequestStatus && !hasDeveloperAccess && !isDeveloperRequestPending; @@ -607,7 +614,10 @@ function GlobalOverviewPage() { setSelectedClientIds([]); }; - if ((role === "user" || role === "tenant_member") && isLoadingRequestStatus) { + if ( + (profileRole === "user" || profileRole === "tenant_member") && + (isLoadingMe || isLoadingRequestStatus) + ) { return (
{t("ui.common.loading", "Loading...")} @@ -650,9 +660,7 @@ function GlobalOverviewPage() { className="font-bold text-primary hover:underline" onClick={() => navigate("/developer-requests")} > - {isDeveloperRequestPending - ? t("ui.dev.nav.developer_request", "개발자 권한 신청") - : t("ui.dev.welcome.btn_request", "개발자 등록 신청하기")} + {t("ui.dev.nav.developer_request", "개발자 권한 신청")} )}
diff --git a/devfront/tests/devfront-role-switch-report.spec.ts b/devfront/tests/devfront-role-switch-report.spec.ts index 073aa617..b42dbe25 100644 --- a/devfront/tests/devfront-role-switch-report.spec.ts +++ b/devfront/tests/devfront-role-switch-report.spec.ts @@ -39,6 +39,33 @@ test.describe("DevFront role report", () => { await captureEvidence(page, testInfo, "role-user-empty-rps"); }); + test("user sees developer request entry point on overview", async ({ + page, + }, testInfo) => { + await seedAuth(page, "user"); + await installDevApiMock(page, { + clients: [], + consents: [] as Consent[], + auditLogs: [] as AuditLog[], + auditLogsByCursor: undefined, + developerRequests: [], + }); + + await page.goto("/"); + await expect( + page.getByText( + /대시보드는 개발자 권한이 있어야 볼 수 있습니다|개발자 권한 신청을 검토 중입니다./, + ), + ).toBeVisible(); + const requestBtn = page.getByRole("button", { + name: /개발자 권한 신청/, + }); + await expect(requestBtn).toBeVisible(); + await requestBtn.click(); + await expect(page).toHaveURL(/\/developer-requests$/); + await captureEvidence(page, testInfo, "role-user-overview-request-entry"); + }); + test("rp_admin sees only assigned Gitea app and its logs", async ({ page, }, testInfo) => { diff --git a/devfront/tests/helpers/devfront-fixtures.ts b/devfront/tests/helpers/devfront-fixtures.ts index 349c9ece..befdc222 100644 --- a/devfront/tests/helpers/devfront-fixtures.ts +++ b/devfront/tests/helpers/devfront-fixtures.ts @@ -408,6 +408,15 @@ export async function installDevApiMock(page: Page, state: DevApiMockState) { }); } + if (pathname === "/api/v1/dev/rp-usage/daily" && method === "GET") { + return json(route, { + items: [], + days: Number.parseInt(searchParams.get("days") || "14", 10), + period: + (searchParams.get("period") as "day" | "week" | "month") || "day", + }); + } + if (pathname === "/api/v1/dev/clients" && method === "GET") { return json(route, { items: state.clients.map((client) => ({