forked from baron/baron-sso
테넌트 전환 UI 구현 및 프로필 연동
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "../../components/ui/card";
|
} from "../../components/ui/card";
|
||||||
import { t } from "../../lib/i18n";
|
import { t } from "../../lib/i18n";
|
||||||
|
import ProfileTenantSwitcher from "./ProfileTenantSwitcher";
|
||||||
import { fetchMe } from "../auth/authApi";
|
import { fetchMe } from "../auth/authApi";
|
||||||
|
|
||||||
function ProfilePage() {
|
function ProfilePage() {
|
||||||
@@ -165,6 +166,8 @@ function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<ProfileTenantSwitcher />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
84
devfront/src/features/profile/ProfileTenantSwitcher.tsx
Normal file
84
devfront/src/features/profile/ProfileTenantSwitcher.tsx
Normal file
@@ -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<string>(() => {
|
||||||
|
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 (
|
||||||
|
<div className="flex flex-col gap-4 mt-6 p-4 rounded-lg border border-border bg-card">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Building2 className="h-5 w-5 text-primary" />
|
||||||
|
<h3 className="font-semibold">{t("ui.dev.tenant.workspace", "작업 테넌트 (컨텍스트)")}</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground -mt-2 mb-2">
|
||||||
|
{t("ui.dev.tenant.workspace_desc", "현재 작업 중인 테넌트를 선택하고 저장하여 API 요청 컨텍스트를 변경합니다.")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<select
|
||||||
|
aria-label={t("ui.dev.tenant.workspace", "작업 테넌트 (컨텍스트)")}
|
||||||
|
value={selectedTenantId}
|
||||||
|
onChange={(e) => setSelectedTenantId(e.target.value)}
|
||||||
|
disabled={isSingleTenant}
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{tenants.map((tenant) => (
|
||||||
|
<option key={tenant.id} value={tenant.id}>
|
||||||
|
{tenant.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={isSingleTenant}
|
||||||
|
className="gap-2"
|
||||||
|
>
|
||||||
|
<Save size={16} />
|
||||||
|
{t("ui.common.save", "저장")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isSingleTenant && (
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
{t("ui.dev.tenant.single_notice", "단일 테넌트에 소속되어 전환할 필요가 없습니다.")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user