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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
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 { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command";
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 [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>({});
// 🆕 그룹별 표시 항목 설정 펼침/접힘 상태
const [expandedDisplayItems, setExpandedDisplayItems] = useState<Record<string, boolean>>({});
// 🆕 원본 테이블 선택 상태
const [sourceTableSelectOpen, setSourceTableSelectOpen] = useState(false);
const [sourceTableSearchValue, setSourceTableSearchValue] = useState("");
@ -621,20 +628,40 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</p>
{localFieldGroups.map((group, index) => (
<Card key={group.id} className="border-2">
<CardContent className="space-y-2 pt-3 sm:space-y-3 sm:pt-4">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-gray-700 sm:text-sm"> {index + 1}</span>
<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>
<Collapsible
key={group.id}
open={expandedGroups[group.id] ?? true}
onOpenChange={(open) => setExpandedGroups(prev => ({ ...prev, [group.id]: open }))}
>
<Card className="border-2">
<CardContent className="space-y-2 p-3 sm:space-y-3 sm:p-4">
<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-2">
{expandedGroups[group.id] ?? true ? (
<ChevronDown className="h-4 w-4" />
) : (
<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 */}
<div className="space-y-1">
@ -682,10 +709,27 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div>
{/* 🆕 이 그룹의 항목 표시 설정 */}
<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">
<Label className="text-[10px] font-semibold sm:text-xs"> </Label>
<div className="flex gap-1">
<Collapsible
open={expandedDisplayItems[group.id] ?? false}
onOpenChange={(open) => setExpandedDisplayItems(prev => ({ ...prev, [group.id]: open }))}
>
<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
type="button"
size="sm"
@ -719,11 +763,12 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div>
</div>
<p className="text-[9px] text-muted-foreground sm:text-[10px]">
</p>
<CollapsibleContent className="space-y-2">
<p className="text-[9px] text-muted-foreground sm:text-[10px]">
</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>
@ -846,10 +891,15 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
</div>
))}
</div>
)}
</div>
</CardContent>
</Card>
)}
</CollapsibleContent>
</div>
</Collapsible>
</CollapsibleContent>
</CardContent>
</Card>
</Collapsible>
))}
<Button