fix(pop): POP 화면 관리 좌측 패널 UX 개선 (스크롤 + 접기/펼치기)
- 좌측 패널 스크롤 수정: 부모에 overflow-hidden, ScrollArea에 min-h-0 추가하여 미분류 목록이 많을 때 스크롤바가 정상 작동하도록 개선 - 카테고리 그룹 기본 접힌 상태: loadGroups 자동 확장 로직 제거하여 페이지 진입 시 깔끔한 트리 뷰 제공 - 미분류 회사코드별 접기/펼치기: 최고관리자/COMPANY_7 등 회사코드 그룹마다 토글 헤더 추가, 항목 수 Badge 표시
This commit is contained in:
parent
ce5c2426b5
commit
15e22ba401
|
|
@ -285,7 +285,7 @@ export default function PopScreenManagementPage() {
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 overflow-hidden flex">
|
<div className="flex-1 overflow-hidden flex">
|
||||||
{/* 왼쪽: 카테고리 트리 + 화면 목록 */}
|
{/* 왼쪽: 카테고리 트리 + 화면 목록 */}
|
||||||
<div className="w-[320px] min-w-[280px] max-w-[400px] flex flex-col border-r bg-background">
|
<div className="w-[320px] min-w-[280px] max-w-[400px] flex flex-col border-r bg-background overflow-hidden">
|
||||||
{/* 검색 */}
|
{/* 검색 */}
|
||||||
<div className="shrink-0 p-3 border-b">
|
<div className="shrink-0 p-3 border-b">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,9 @@ export function PopCategoryTree({
|
||||||
const [movingFromGroupId, setMovingFromGroupId] = useState<number | null>(null);
|
const [movingFromGroupId, setMovingFromGroupId] = useState<number | null>(null);
|
||||||
const [moveSearchTerm, setMoveSearchTerm] = useState("");
|
const [moveSearchTerm, setMoveSearchTerm] = useState("");
|
||||||
|
|
||||||
|
// 미분류 회사코드별 접기/펼치기
|
||||||
|
const [expandedCompanyCodes, setExpandedCompanyCodes] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
// 화면 맵 생성 (screen_id로 빠르게 조회)
|
// 화면 맵 생성 (screen_id로 빠르게 조회)
|
||||||
const screensMap = useMemo(() => {
|
const screensMap = useMemo(() => {
|
||||||
const map = new Map<number, ScreenDefinition>();
|
const map = new Map<number, ScreenDefinition>();
|
||||||
|
|
@ -517,14 +520,6 @@ export function PopCategoryTree({
|
||||||
// 그룹 목록 조회
|
// 그룹 목록 조회
|
||||||
const data = await getPopScreenGroups(searchTerm);
|
const data = await getPopScreenGroups(searchTerm);
|
||||||
setGroups(data);
|
setGroups(data);
|
||||||
|
|
||||||
// 첫 로드 시 루트 그룹들 자동 확장
|
|
||||||
if (expandedGroups.size === 0 && data.length > 0) {
|
|
||||||
const rootIds = data
|
|
||||||
.filter((g) => g.hierarchy_path === "POP" || g.hierarchy_path?.split("/").length === 2)
|
|
||||||
.map((g) => g.id);
|
|
||||||
setExpandedGroups(new Set(rootIds));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("POP 그룹 로드 실패:", error);
|
console.error("POP 그룹 로드 실패:", error);
|
||||||
toast.error("그룹 목록 로드에 실패했습니다.");
|
toast.error("그룹 목록 로드에 실패했습니다.");
|
||||||
|
|
@ -934,7 +929,7 @@ export function PopCategoryTree({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full overflow-hidden">
|
||||||
{/* 헤더 */}
|
{/* 헤더 */}
|
||||||
<div className="shrink-0 p-3 border-b flex items-center justify-between">
|
<div className="shrink-0 p-3 border-b flex items-center justify-between">
|
||||||
<h3 className="text-sm font-medium">POP 카테고리</h3>
|
<h3 className="text-sm font-medium">POP 카테고리</h3>
|
||||||
|
|
@ -949,7 +944,7 @@ export function PopCategoryTree({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 트리 영역 */}
|
{/* 트리 영역 */}
|
||||||
<ScrollArea className="flex-1">
|
<ScrollArea className="flex-1 min-h-0">
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
{treeData.length === 0 && ungroupedScreens.length === 0 ? (
|
{treeData.length === 0 && ungroupedScreens.length === 0 ? (
|
||||||
<div className="text-center text-sm text-muted-foreground py-8">
|
<div className="text-center text-sm text-muted-foreground py-8">
|
||||||
|
|
@ -1009,16 +1004,42 @@ export function PopCategoryTree({
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
const companyKeys = Object.keys(grouped).sort();
|
const companyKeys = Object.keys(grouped).sort();
|
||||||
const hasMultipleCompanies = companyKeys.length > 1;
|
|
||||||
|
|
||||||
return companyKeys.map((companyCode) => (
|
const toggleCompanyCode = (code: string) => {
|
||||||
|
setExpandedCompanyCodes((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
if (next.has(code)) {
|
||||||
|
next.delete(code);
|
||||||
|
} else {
|
||||||
|
next.add(code);
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return companyKeys.map((companyCode) => {
|
||||||
|
const isExpanded = expandedCompanyCodes.has(companyCode);
|
||||||
|
const label = companyCode === "*" ? "최고관리자" : companyCode;
|
||||||
|
|
||||||
|
return (
|
||||||
<div key={`ungrouped-company-${companyCode}`}>
|
<div key={`ungrouped-company-${companyCode}`}>
|
||||||
{hasMultipleCompanies && (
|
<div
|
||||||
<div className="text-[10px] font-medium text-muted-foreground px-2 py-1 mt-1 bg-muted/50 rounded">
|
className="flex items-center gap-1 px-2 py-1 mt-1 bg-muted/50 rounded cursor-pointer select-none hover:bg-muted transition-colors"
|
||||||
{companyCode === "*" ? "최고관리자" : companyCode}
|
onClick={() => toggleCompanyCode(companyCode)}
|
||||||
</div>
|
>
|
||||||
)}
|
{isExpanded ? (
|
||||||
{grouped[companyCode].map((screen) => (
|
<ChevronDown className="h-3 w-3 text-muted-foreground shrink-0" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-3 w-3 text-muted-foreground shrink-0" />
|
||||||
|
)}
|
||||||
|
<span className="text-[10px] font-medium text-muted-foreground">
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
<Badge variant="outline" className="ml-auto h-4 text-[9px] px-1">
|
||||||
|
{grouped[companyCode].length}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{isExpanded && grouped[companyCode].map((screen) => (
|
||||||
<div
|
<div
|
||||||
key={`ungrouped-${screen.screenId}`}
|
key={`ungrouped-${screen.screenId}`}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -1027,7 +1048,7 @@ export function PopCategoryTree({
|
||||||
? "bg-primary/10 text-primary"
|
? "bg-primary/10 text-primary"
|
||||||
: "hover:bg-muted",
|
: "hover:bg-muted",
|
||||||
"group",
|
"group",
|
||||||
hasMultipleCompanies && "pl-4"
|
"pl-4"
|
||||||
)}
|
)}
|
||||||
onClick={() => onScreenSelect(screen)}
|
onClick={() => onScreenSelect(screen)}
|
||||||
onDoubleClick={() => onScreenDesign(screen)}
|
onDoubleClick={() => onScreenDesign(screen)}
|
||||||
|
|
@ -1078,7 +1099,8 @@ export function PopCategoryTree({
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
));
|
);
|
||||||
|
});
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue