- Added validation to ensure that only existing SUPER_ADMIN users can grant or modify SUPER_ADMIN permissions. - Updated the user management page to reflect that both SUPER_ADMIN and COMPANY_ADMIN can access the user permissions, while COMPANY_ADMIN cannot grant SUPER_ADMIN rights. - Enhanced the user authorization modal to prevent COMPANY_ADMIN from changing SUPER_ADMIN permissions, ensuring proper access control. These changes improve the security and integrity of user role management within the application.
214 lines
7.7 KiB
TypeScript
214 lines
7.7 KiB
TypeScript
import { Search, Plus, ChevronDown, ChevronUp } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { UserSearchFilter } from "@/types/user";
|
|
import { useState } from "react";
|
|
|
|
interface UserToolbarProps {
|
|
searchFilter: UserSearchFilter;
|
|
totalCount: number;
|
|
isSearching?: boolean;
|
|
onSearchChange: (searchFilter: Partial<UserSearchFilter>) => void;
|
|
onCreateClick: () => void;
|
|
}
|
|
|
|
/**
|
|
* 사용자 관리 툴바 컴포넌트
|
|
* 통합 검색 + 고급 검색 옵션 지원
|
|
*/
|
|
export function UserToolbar({
|
|
searchFilter,
|
|
totalCount,
|
|
isSearching = false,
|
|
onSearchChange,
|
|
onCreateClick,
|
|
}: UserToolbarProps) {
|
|
const [showAdvancedSearch, setShowAdvancedSearch] = useState(false);
|
|
|
|
// 통합 검색어 변경
|
|
const handleV2SearchChange = (value: string) => {
|
|
onSearchChange({
|
|
searchValue: value,
|
|
// 통합 검색 시 고급 검색 필드들 클리어
|
|
searchType: undefined,
|
|
search_sabun: undefined,
|
|
search_companyName: undefined,
|
|
search_deptName: undefined,
|
|
search_positionName: undefined,
|
|
search_userId: undefined,
|
|
search_userName: undefined,
|
|
search_tel: undefined,
|
|
search_email: undefined,
|
|
});
|
|
};
|
|
|
|
// 고급 검색 필드 변경
|
|
const handleAdvancedSearchChange = (field: string, value: string) => {
|
|
onSearchChange({
|
|
[field]: value,
|
|
// 고급 검색 시 통합 검색어 클리어
|
|
searchValue: undefined,
|
|
});
|
|
};
|
|
|
|
// 고급 검색 모드인지 확인
|
|
const isAdvancedSearchMode = !!(
|
|
searchFilter.search_sabun ||
|
|
searchFilter.search_companyName ||
|
|
searchFilter.search_deptName ||
|
|
searchFilter.search_positionName ||
|
|
searchFilter.search_userId ||
|
|
searchFilter.search_userName ||
|
|
searchFilter.search_tel ||
|
|
searchFilter.search_email
|
|
);
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* 검색 및 액션 영역 */}
|
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
|
{/* 검색 영역 */}
|
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
|
|
<div className="w-full sm:w-[400px]">
|
|
<div className="relative">
|
|
<Search
|
|
className={`absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 ${
|
|
isSearching ? "animate-pulse text-primary" : "text-muted-foreground"
|
|
}`}
|
|
/>
|
|
<Input
|
|
placeholder="통합 검색..."
|
|
value={searchFilter.searchValue || ""}
|
|
onChange={(e) => handleV2SearchChange(e.target.value)}
|
|
disabled={isAdvancedSearchMode}
|
|
autoComplete="off"
|
|
className={`h-10 pl-10 text-sm ${
|
|
isSearching ? "border-primary ring-2 ring-primary/20" : ""
|
|
} ${isAdvancedSearchMode ? "cursor-not-allowed bg-muted text-muted-foreground" : ""}`}
|
|
/>
|
|
</div>
|
|
{isSearching && <p className="mt-1.5 text-xs text-primary">검색 중...</p>}
|
|
{isAdvancedSearchMode && (
|
|
<p className="mt-1.5 text-xs text-warning">
|
|
고급 검색 모드가 활성화되어 있습니다. 통합 검색을 사용하려면 고급 검색 조건을 초기화하세요.
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* 고급 검색 토글 버튼 */}
|
|
<Button
|
|
variant="outline"
|
|
size="default"
|
|
onClick={() => setShowAdvancedSearch(!showAdvancedSearch)}
|
|
className="h-10 gap-2 text-sm font-medium"
|
|
>
|
|
고급 검색
|
|
{showAdvancedSearch ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* 액션 버튼 영역 */}
|
|
<div className="flex items-center gap-4">
|
|
{/* 조회 결과 정보 */}
|
|
<div className="text-sm text-muted-foreground">
|
|
총 <span className="font-semibold text-foreground">{totalCount.toLocaleString()}</span> 명
|
|
</div>
|
|
|
|
{/* 사용자 등록 버튼 */}
|
|
<Button onClick={onCreateClick} size="default" className="h-10 gap-2 text-sm font-medium">
|
|
<Plus className="h-4 w-4" />
|
|
사용자 등록
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 고급 검색 옵션 */}
|
|
{showAdvancedSearch && (
|
|
<div className="space-y-4">
|
|
<div className="space-y-1">
|
|
<h4 className="text-sm font-semibold">고급 검색 옵션</h4>
|
|
<p className="text-xs text-muted-foreground">각 필드별로 개별 검색 조건을 설정할 수 있습니다</p>
|
|
</div>
|
|
|
|
{/* 고급 검색 필드들 */}
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
<Input
|
|
placeholder="회사명 검색"
|
|
value={searchFilter.search_companyName || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_companyName", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="부서명 검색"
|
|
value={searchFilter.search_deptName || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_deptName", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="직책 검색"
|
|
value={searchFilter.search_positionName || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_positionName", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="사용자 ID 검색"
|
|
value={searchFilter.search_userId || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_userId", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="사용자명 검색"
|
|
value={searchFilter.search_userName || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_userName", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="전화번호/휴대폰 검색"
|
|
value={searchFilter.search_tel || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_tel", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
|
|
<Input
|
|
placeholder="이메일 검색"
|
|
value={searchFilter.search_email || ""}
|
|
onChange={(e) => handleAdvancedSearchChange("search_email", e.target.value)}
|
|
className="h-10 text-sm"
|
|
/>
|
|
</div>
|
|
|
|
{/* 고급 검색 초기화 버튼 */}
|
|
{isAdvancedSearchMode && (
|
|
<div>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() =>
|
|
onSearchChange({
|
|
search_sabun: undefined,
|
|
search_companyName: undefined,
|
|
search_deptName: undefined,
|
|
search_positionName: undefined,
|
|
search_userId: undefined,
|
|
search_userName: undefined,
|
|
search_tel: undefined,
|
|
search_email: undefined,
|
|
})
|
|
}
|
|
className="h-9 text-sm text-muted-foreground hover:text-foreground"
|
|
>
|
|
고급 검색 조건 초기화
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|