From 7fe86e8aa441c99c59b8e4c80ca0e1495587c228 Mon Sep 17 00:00:00 2001 From: kyy Date: Tue, 26 May 2026 15:38:01 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=BC=EB=B0=98=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=97=B0=EB=8F=99=20=EC=95=B1=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EB=85=B8=EC=B6=9C=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devfront/src/features/clients/ClientsPage.tsx | 24 ++++++++++++------- .../clients/clientCreateAccess.test.ts | 9 +++++++ .../features/clients/clientCreateAccess.ts | 4 ++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/devfront/src/features/clients/ClientsPage.tsx b/devfront/src/features/clients/ClientsPage.tsx index edcd5edd..74e49484 100644 --- a/devfront/src/features/clients/ClientsPage.tsx +++ b/devfront/src/features/clients/ClientsPage.tsx @@ -97,6 +97,14 @@ function ClientsPage() { enabled: hasAccessToken, }); + const { data: me, isLoading: isLoadingMe } = useQuery({ + queryKey: ["userMe"], + queryFn: fetchMe, + enabled: hasAccessToken, + }); + + const profileRole = me?.role?.trim() || role; + const { data: requestStatus, isLoading: isLoadingRequest, @@ -104,21 +112,16 @@ function ClientsPage() { } = useQuery({ queryKey: ["developer-request", tenantId], queryFn: () => fetchDeveloperRequestStatus(tenantId), - enabled: hasAccessToken && (role === "user" || role === "tenant_member"), + enabled: hasAccessToken && (profileRole === "user" || profileRole === "tenant_member"), }); const { data: tenants } = useQuery({ queryKey: ["myTenants"], queryFn: fetchMyTenants, enabled: hasAccessToken, }); - const { data: me } = useQuery({ - queryKey: ["userMe"], - queryFn: fetchMe, - enabled: hasAccessToken, - }); const createAccessState = resolveClientCreateAccess({ - role, + role: profileRole, requestStatus: requestStatus?.status, }); const canCreateClient = createAccessState === "can_create"; @@ -193,7 +196,6 @@ function ClientsPage() { (userProfile?.phone as string | undefined) || (userProfile?.phone_number as string | undefined) || ""; - const profileRole = me?.role || role; const profileRoleLabel = t(`ui.admin.role.${profileRole}`, profileRole); type StatTone = "up" | "down" | "stable"; @@ -236,7 +238,11 @@ function ClientsPage() { }, ]; - const isLoading = isLoadingClients || isLoadingStats || isLoadingRequest; + const isLoading = + isLoadingClients || + isLoadingStats || + isLoadingRequest || + (hasAccessToken && !profileRole && isLoadingMe); const requestSort = (key: ClientSortKey) => { setSortConfig((current) => toggleSort(current, key)); diff --git a/devfront/src/features/clients/clientCreateAccess.test.ts b/devfront/src/features/clients/clientCreateAccess.test.ts index 11ebcff8..79200c10 100644 --- a/devfront/src/features/clients/clientCreateAccess.test.ts +++ b/devfront/src/features/clients/clientCreateAccess.test.ts @@ -19,6 +19,15 @@ describe("client create access", () => { ).toBe("request_required"); }); + it("treats unresolved roles as request required instead of allowing creation", () => { + expect( + resolveClientCreateAccess({ + role: "", + requestStatus: undefined, + }), + ).toBe("request_required"); + }); + it("shows pending state while a developer request is under review", () => { expect( resolveClientCreateAccess({ diff --git a/devfront/src/features/clients/clientCreateAccess.ts b/devfront/src/features/clients/clientCreateAccess.ts index 150dce0e..64e3e556 100644 --- a/devfront/src/features/clients/clientCreateAccess.ts +++ b/devfront/src/features/clients/clientCreateAccess.ts @@ -19,6 +19,10 @@ export function resolveClientCreateAccess({ role, requestStatus, }: ResolveClientCreateAccessParams): ClientCreateAccessState { + if (!role.trim()) { + return "request_required"; + } + if (!canSelfRequestDeveloperAccess(role)) { return "can_create"; }