refactor: 설정 패널 UI 개선 - Collapsible로 정리

- 필드 그룹을 Collapsible로 변경하여 펼침/접힘 가능
- 항목 표시 설정도 Collapsible로 분리하여 깔끔하게 정리
- 그룹 제목에 displayItems 개수 표시
- 기본적으로 그룹은 펼쳐진 상태, 표시 설정은 접힌 상태
- ChevronDown/ChevronRight 아이콘으로 펼침 상태 표시
- 복잡한 설정을 단계적으로 볼 수 있어 가독성 대폭 향상
This commit is contained in:
kjs 2025-11-18 10:25:28 +09:00
parent eef1451c5a
commit def94c41f4
1 changed files with 77 additions and 27 deletions

View File

@ -7,7 +7,8 @@ import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Plus, X } from "lucide-react"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Plus, X, ChevronDown, ChevronRight } from "lucide-react";
import { SelectedItemsDetailInputConfig, AdditionalFieldDefinition, FieldGroup, DisplayItem, DisplayItemType, EmptyBehavior, DisplayFieldFormat } from "./types"; import { SelectedItemsDetailInputConfig, AdditionalFieldDefinition, FieldGroup, DisplayItem, DisplayItemType, EmptyBehavior, DisplayFieldFormat } from "./types";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
@ -46,6 +47,12 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
// 🆕 필드 그룹 상태 // 🆕 필드 그룹 상태
const [localFieldGroups, setLocalFieldGroups] = useState<FieldGroup[]>(config.fieldGroups || []); const [localFieldGroups, setLocalFieldGroups] = useState<FieldGroup[]>(config.fieldGroups || []);
// 🆕 그룹별 펼침/접힘 상태
const [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>({});
// 🆕 그룹별 표시 항목 설정 펼침/접힘 상태
const [expandedDisplayItems, setExpandedDisplayItems] = useState<Record<string, boolean>>({});
// 🆕 원본 테이블 선택 상태 // 🆕 원본 테이블 선택 상태
const [sourceTableSelectOpen, setSourceTableSelectOpen] = useState(false); const [sourceTableSelectOpen, setSourceTableSelectOpen] = useState(false);
const [sourceTableSearchValue, setSourceTableSearchValue] = useState(""); const [sourceTableSearchValue, setSourceTableSearchValue] = useState("");
@ -621,20 +628,40 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</p> </p>
{localFieldGroups.map((group, index) => ( {localFieldGroups.map((group, index) => (
<Card key={group.id} className="border-2"> <Collapsible
<CardContent className="space-y-2 pt-3 sm:space-y-3 sm:pt-4"> key={group.id}
<div className="flex items-center justify-between"> open={expandedGroups[group.id] ?? true}
<span className="text-xs font-semibold text-gray-700 sm:text-sm"> {index + 1}</span> onOpenChange={(open) => setExpandedGroups(prev => ({ ...prev, [group.id]: open }))}
<Button >
type="button" <Card className="border-2">
variant="ghost" <CardContent className="space-y-2 p-3 sm:space-y-3 sm:p-4">
size="icon" <div className="flex items-center justify-between">
onClick={() => removeFieldGroup(group.id)} <CollapsibleTrigger asChild>
className="h-6 w-6 text-red-500 hover:text-red-700 sm:h-7 sm:w-7" <Button variant="ghost" size="sm" className="h-auto p-0 hover:bg-transparent">
> <div className="flex items-center gap-2">
<X className="h-3 w-3 sm:h-4 sm:w-4" /> {expandedGroups[group.id] ?? true ? (
</Button> <ChevronDown className="h-4 w-4" />
</div> ) : (
<ChevronRight className="h-4 w-4" />
)}
<span className="text-xs font-semibold sm:text-sm">
{index + 1}: {group.title || group.id}
</span>
</div>
</Button>
</CollapsibleTrigger>
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => removeFieldGroup(group.id)}
className="h-6 w-6 text-red-500 hover:text-red-700 sm:h-7 sm:w-7"
>
<X className="h-3 w-3 sm:h-4 sm:w-4" />
</Button>
</div>
<CollapsibleContent className="space-y-2 sm:space-y-3">{/* 기존 그룹 설정 내용 */}
{/* 그룹 ID */} {/* 그룹 ID */}
<div className="space-y-1"> <div className="space-y-1">
@ -682,10 +709,27 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div> </div>
{/* 🆕 이 그룹의 항목 표시 설정 */} {/* 🆕 이 그룹의 항목 표시 설정 */}
<div className="space-y-2 rounded-lg border-2 border-dashed border-primary/30 bg-primary/5 p-2"> <Collapsible
<div className="flex items-center justify-between"> open={expandedDisplayItems[group.id] ?? false}
<Label className="text-[10px] font-semibold sm:text-xs"> </Label> onOpenChange={(open) => setExpandedDisplayItems(prev => ({ ...prev, [group.id]: open }))}
<div className="flex gap-1"> >
<div className="space-y-2 rounded-lg border-2 border-dashed border-primary/30 bg-primary/5 p-2">
<div className="flex items-center justify-between">
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="h-auto p-0 hover:bg-transparent">
<div className="flex items-center gap-1">
{expandedDisplayItems[group.id] ? (
<ChevronDown className="h-3 w-3" />
) : (
<ChevronRight className="h-3 w-3" />
)}
<Label className="text-[10px] font-semibold sm:text-xs cursor-pointer">
({(group.displayItems || []).length})
</Label>
</div>
</Button>
</CollapsibleTrigger>
<div className="flex gap-1">
<Button <Button
type="button" type="button"
size="sm" size="sm"
@ -719,11 +763,12 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div> </div>
</div> </div>
<p className="text-[9px] text-muted-foreground sm:text-[10px]"> <CollapsibleContent className="space-y-2">
<p className="text-[9px] text-muted-foreground sm:text-[10px]">
</p>
</p>
{(!group.displayItems || group.displayItems.length === 0) ? ( {(!group.displayItems || group.displayItems.length === 0) ? (
<div className="rounded border border-dashed p-2 text-center text-[10px] text-muted-foreground"> <div className="rounded border border-dashed p-2 text-center text-[10px] text-muted-foreground">
( " / " ) ( " / " )
</div> </div>
@ -846,10 +891,15 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div> </div>
))} ))}
</div> </div>
)} )}
</div> </CollapsibleContent>
</CardContent> </div>
</Card> </Collapsible>
</CollapsibleContent>
</CardContent>
</Card>
</Collapsible>
))} ))}
<Button <Button