196 lines
6.5 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
|