forked from baron/baron-sso
개요 차단 화면에 개발자 권한 신청 버튼 추가
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
|||||||
} from "../../lib/devApi";
|
} from "../../lib/devApi";
|
||||||
import { t } from "../../lib/i18n";
|
import { t } from "../../lib/i18n";
|
||||||
import { resolveProfileRole } from "../../lib/role";
|
import { resolveProfileRole } from "../../lib/role";
|
||||||
|
import { fetchMe } from "../auth/authApi";
|
||||||
|
|
||||||
type ClientDistribution = {
|
type ClientDistribution = {
|
||||||
activeClients: number;
|
activeClients: number;
|
||||||
@@ -480,13 +481,19 @@ function GlobalOverviewPage() {
|
|||||||
const userProfile = auth.user?.profile as Record<string, unknown> | undefined;
|
const userProfile = auth.user?.profile as Record<string, unknown> | undefined;
|
||||||
const role = resolveProfileRole(userProfile);
|
const role = resolveProfileRole(userProfile);
|
||||||
const tenantId = userProfile?.tenant_id as string | undefined;
|
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<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({
|
const { data: requestStatus, isLoading: isLoadingRequestStatus } = useQuery({
|
||||||
queryKey: ["developer-request", tenantId],
|
queryKey: ["developer-request", tenantId],
|
||||||
queryFn: () => fetchDeveloperRequestStatus(tenantId),
|
queryFn: () => fetchDeveloperRequestStatus(tenantId),
|
||||||
enabled: hasAccessToken && role === "user",
|
enabled: hasAccessToken && profileRole === "user",
|
||||||
});
|
});
|
||||||
const statsQuery = useQuery({
|
const statsQuery = useQuery({
|
||||||
queryKey: ["dev-dashboard-stats"],
|
queryKey: ["dev-dashboard-stats"],
|
||||||
@@ -510,13 +517,13 @@ function GlobalOverviewPage() {
|
|||||||
|
|
||||||
const clients = clientsQuery.data?.items ?? [];
|
const clients = clientsQuery.data?.items ?? [];
|
||||||
const hasDeveloperAccess =
|
const hasDeveloperAccess =
|
||||||
role === "super_admin" ||
|
profileRole === "super_admin" ||
|
||||||
role === "tenant_admin" ||
|
profileRole === "tenant_admin" ||
|
||||||
role === "rp_admin" ||
|
profileRole === "rp_admin" ||
|
||||||
requestStatus?.status === "approved";
|
requestStatus?.status === "approved";
|
||||||
const isDeveloperRequestPending = requestStatus?.status === "pending";
|
const isDeveloperRequestPending = requestStatus?.status === "pending";
|
||||||
const canRequestDeveloperAccess =
|
const canRequestDeveloperAccess =
|
||||||
(role === "user" || role === "tenant_member") &&
|
(profileRole === "user" || profileRole === "tenant_member") &&
|
||||||
!isLoadingRequestStatus &&
|
!isLoadingRequestStatus &&
|
||||||
!hasDeveloperAccess &&
|
!hasDeveloperAccess &&
|
||||||
!isDeveloperRequestPending;
|
!isDeveloperRequestPending;
|
||||||
@@ -607,7 +614,10 @@ function GlobalOverviewPage() {
|
|||||||
setSelectedClientIds([]);
|
setSelectedClientIds([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((role === "user" || role === "tenant_member") && isLoadingRequestStatus) {
|
if (
|
||||||
|
(profileRole === "user" || profileRole === "tenant_member") &&
|
||||||
|
(isLoadingMe || isLoadingRequestStatus)
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="p-8 text-center">
|
<div className="p-8 text-center">
|
||||||
{t("ui.common.loading", "Loading...")}
|
{t("ui.common.loading", "Loading...")}
|
||||||
@@ -650,9 +660,7 @@ function GlobalOverviewPage() {
|
|||||||
className="font-bold text-primary hover:underline"
|
className="font-bold text-primary hover:underline"
|
||||||
onClick={() => navigate("/developer-requests")}
|
onClick={() => navigate("/developer-requests")}
|
||||||
>
|
>
|
||||||
{isDeveloperRequestPending
|
{t("ui.dev.nav.developer_request", "개발자 권한 신청")}
|
||||||
? t("ui.dev.nav.developer_request", "개발자 권한 신청")
|
|
||||||
: t("ui.dev.welcome.btn_request", "개발자 등록 신청하기")}
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,6 +39,33 @@ test.describe("DevFront role report", () => {
|
|||||||
await captureEvidence(page, testInfo, "role-user-empty-rps");
|
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 ({
|
test("rp_admin sees only assigned Gitea app and its logs", async ({
|
||||||
page,
|
page,
|
||||||
}, testInfo) => {
|
}, testInfo) => {
|
||||||
|
|||||||
@@ -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") {
|
if (pathname === "/api/v1/dev/clients" && method === "GET") {
|
||||||
return json(route, {
|
return json(route, {
|
||||||
items: state.clients.map((client) => ({
|
items: state.clients.map((client) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user