ERP-node/frontend/components/admin/CompanySwitcher.tsx

196 lines
6.5 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Building2, Search } from "lucide-react";
import { useAuth } from "@/hooks/useAuth";
import { apiClient } from "@/lib/api/client";
import { logger } from "@/lib/utils/logger";
interface Company {
company_code: string;
company_name: string;
status: string;
}
interface CompanySwitcherProps {
onClose?: () => void;
isOpen?: boolean; // Dialog 열림 상태 (AppLayout에서 전달)
}
/**
* WACE 관리자 전용: 회사 선택 및 전환 컴포넌트
*
* - WACE 관리자(company_code = "*", userType = "SUPER_ADMIN")만 표시
* - 회사 선택 시 해당 회사로 전환하여 시스템 사용
* - JWT 토큰 재발급으로 company_code 변경
*/
export function CompanySwitcher({ onClose, isOpen = false }: CompanySwitcherProps = {}) {
const { user, switchCompany } = useAuth();
const [companies, setCompanies] = useState<Company[]>([]);
const [filteredCompanies, setFilteredCompanies] = useState<Company[]>([]);
const [searchText, setSearchText] = useState("");
const [loading, setLoading] = useState(false);
// WACE 관리자 권한 체크 (userType만 확인)
const isWaceAdmin = user?.userType === "SUPER_ADMIN";
// 현재 선택된 회사명 표시
const currentCompanyName = React.useMemo(() => {
if (!user?.companyCode) return "로딩 중...";
if (user.companyCode === "*") {
return "WACE (최고 관리자)";
}
// companies 배열에서 현재 회사 찾기
const currentCompany = companies.find(c => c.company_code === user.companyCode);
return currentCompany?.company_name || user.companyCode;
}, [user?.companyCode, companies]);
// 회사 목록 조회
useEffect(() => {
if (isWaceAdmin && isOpen) {
fetchCompanies();
}
}, [isWaceAdmin, isOpen]);
// 검색 필터링
useEffect(() => {
if (searchText.trim() === "") {
setFilteredCompanies(companies);
} else {
const filtered = companies.filter(company =>
company.company_name.toLowerCase().includes(searchText.toLowerCase()) ||
company.company_code.toLowerCase().includes(searchText.toLowerCase())
);
setFilteredCompanies(filtered);
}
}, [searchText, companies]);
const fetchCompanies = async () => {
try {
setLoading(true);
const response = await apiClient.get("/admin/companies/db");
if (response.data.success) {
// 활성 상태의 회사만 필터링 + company_code="*" 제외 (WACE는 별도 추가)
const activeCompanies = response.data.data
.filter((c: Company) => c.company_code !== "*") // DB의 "*" 제외
.filter((c: Company) => c.status === "active" || !c.status)
.sort((a: Company, b: Company) => a.company_name.localeCompare(b.company_name));
// WACE 복귀 옵션 추가
const companiesWithWace: Company[] = [
{
company_code: "*",
company_name: "WACE (최고 관리자)",
status: "active",
},
...activeCompanies,
];
setCompanies(companiesWithWace);
setFilteredCompanies(companiesWithWace);
}
} catch (error) {
logger.error("회사 목록 조회 실패", error);
} finally {
setLoading(false);
}
};
const handleCompanySwitch = async (companyCode: string) => {
try {
setLoading(true);
const result = await switchCompany(companyCode);
if (!result.success) {
alert(result.message || "회사 전환에 실패했습니다.");
setLoading(false);
return;
}
logger.info("회사 전환 성공", { companyCode });
// 즉시 페이지 새로고침 (토큰이 이미 저장됨)
window.location.reload();
} catch (error: any) {
logger.error("회사 전환 실패", error);
alert(error.message || "회사 전환 중 오류가 발생했습니다.");
setLoading(false);
}
};
// WACE 관리자가 아니면 렌더링하지 않음
if (!isWaceAdmin) {
return null;
}
return (
<div className="space-y-4">
{/* 현재 회사 정보 */}
<div className="rounded-lg border bg-gradient-to-r from-primary/10 to-primary/5 p-4">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/20">
<Building2 className="h-5 w-5 text-primary" />
</div>
<div>
<p className="text-xs text-muted-foreground"> </p>
<p className="text-sm font-semibold">{currentCompanyName}</p>
</div>
</div>
</div>
{/* 회사 검색 */}
<div className="relative">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="회사명 또는 코드 검색..."
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="h-10 pl-10 text-sm"
/>
</div>
{/* 회사 목록 */}
<div className="max-h-[400px] space-y-2 overflow-y-auto rounded-lg border p-2">
{loading ? (
<div className="p-4 text-center text-sm text-muted-foreground">
...
</div>
) : filteredCompanies.length === 0 ? (
<div className="p-4 text-center text-sm text-muted-foreground">
.
</div>
) : (
filteredCompanies.map((company) => (
<div
key={company.company_code}
className={`flex cursor-pointer items-center justify-between rounded-md px-3 py-2 text-sm transition-colors hover:bg-accent ${
company.company_code === user?.companyCode
? "bg-accent/50 font-semibold"
: ""
}`}
onClick={() => handleCompanySwitch(company.company_code)}
>
<div className="flex flex-col">
<span className="font-medium">{company.company_name}</span>
<span className="text-xs text-muted-foreground">
{company.company_code}
</span>
</div>
{company.company_code === user?.companyCode && (
<span className="text-xs text-primary"></span>
)}
</div>
))
)}
</div>
</div>
);
}