From 0f82932b352cee65163ff0cd4d60f4413f1c35e7 Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 19 Mar 2026 13:01:55 +0900 Subject: [PATCH] =?UTF-8?q?=ED=85=8C=EB=84=8C=ED=8A=B8=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20UI=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devfront/src/features/profile/ProfilePage.tsx | 3 + .../profile/ProfileTenantSwitcher.tsx | 84 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 devfront/src/features/profile/ProfileTenantSwitcher.tsx diff --git a/devfront/src/features/profile/ProfilePage.tsx b/devfront/src/features/profile/ProfilePage.tsx index e54723d5..424c8ee3 100644 --- a/devfront/src/features/profile/ProfilePage.tsx +++ b/devfront/src/features/profile/ProfilePage.tsx @@ -17,6 +17,7 @@ import { CardTitle, } from "../../components/ui/card"; import { t } from "../../lib/i18n"; +import ProfileTenantSwitcher from "./ProfileTenantSwitcher"; import { fetchMe } from "../auth/authApi"; function ProfilePage() { @@ -165,6 +166,8 @@ function ProfilePage() { + + )} diff --git a/devfront/src/features/profile/ProfileTenantSwitcher.tsx b/devfront/src/features/profile/ProfileTenantSwitcher.tsx new file mode 100644 index 00000000..87c5cc6e --- /dev/null +++ b/devfront/src/features/profile/ProfileTenantSwitcher.tsx @@ -0,0 +1,84 @@ +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { Building2, Save } from "lucide-react"; +import { useState } from "react"; +import { Button } from "../../components/ui/button"; +import { toast } from "../../components/ui/use-toast"; +import { fetchMyTenants } from "../../lib/devApi"; +import { t } from "../../lib/i18n"; + +export default function ProfileTenantSwitcher() { + const queryClient = useQueryClient(); + + const { data: tenants, isLoading } = useQuery({ + queryKey: ["myTenants"], + queryFn: fetchMyTenants, + }); + + const [selectedTenantId, setSelectedTenantId] = useState(() => { + return window.localStorage.getItem("dev_tenant_id") || ""; + }); + + const handleSave = () => { + window.localStorage.setItem("dev_tenant_id", selectedTenantId); + + // Invalidate queries to refresh data with new tenant context + queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] !== "userMe" && query.queryKey[0] !== "myTenants", + }); + + toast(t("ui.dev.tenant.switch_success", "테넌트 전환 완료"), "success"); + }; + + if (isLoading || !tenants || tenants.length === 0) { + return null; + } + + // If there's only one tenant, the user doesn't need to switch. + // Still show it as read-only or hidden. Let's just show it as disabled. + const isSingleTenant = tenants.length <= 1; + + return ( +
+
+ +

{t("ui.dev.tenant.workspace", "작업 테넌트 (컨텍스트)")}

+
+

+ {t("ui.dev.tenant.workspace_desc", "현재 작업 중인 테넌트를 선택하고 저장하여 API 요청 컨텍스트를 변경합니다.")} +

+ +
+ + + +
+ + {isSingleTenant && ( +

+ {t("ui.dev.tenant.single_notice", "단일 테넌트에 소속되어 전환할 필요가 없습니다.")} +

+ )} +
+ ); +}