fix: 항목 표시 설정을 그룹별로 분리
- FieldGroup에 displayItems 추가 (그룹별 독립적인 표시 설정) - SelectedItemsDetailInputConfig에서 전역 displayItems 제거 - renderDisplayItems에 groupId 파라미터 추가하여 그룹별 설정 사용 - 설정 패널에서 그룹별로 displayItems 관리 - 각 그룹마다 다른 표시 형식 가능 (예: 거래처 정보 vs 단가 정보) - 그룹의 필드만 선택 가능하도록 필터링
This commit is contained in:
parent
e234f88577
commit
eef1451c5a
|
|
@ -544,13 +544,19 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
|||
}
|
||||
};
|
||||
|
||||
// 🆕 displayItems를 렌더링하는 헬퍼 함수
|
||||
const renderDisplayItems = useCallback((entry: GroupEntry, item: ItemData) => {
|
||||
const displayItems = componentConfig.displayItems || [];
|
||||
// 🆕 displayItems를 렌더링하는 헬퍼 함수 (그룹별)
|
||||
const renderDisplayItems = useCallback((entry: GroupEntry, item: ItemData, groupId: string) => {
|
||||
// 🆕 해당 그룹의 displayItems 가져오기
|
||||
const group = (componentConfig.fieldGroups || []).find(g => g.id === groupId);
|
||||
const displayItems = group?.displayItems || [];
|
||||
|
||||
if (displayItems.length === 0) {
|
||||
// displayItems가 없으면 기본 방식 (모든 필드 나열)
|
||||
const fields = componentConfig.additionalFields || [];
|
||||
// displayItems가 없으면 기본 방식 (해당 그룹의 필드만 나열)
|
||||
const fields = (componentConfig.additionalFields || []).filter(f =>
|
||||
componentConfig.fieldGroups && componentConfig.fieldGroups.length > 0
|
||||
? f.groupId === groupId
|
||||
: true
|
||||
);
|
||||
return fields.map((f) => entry[f.name] || "-").join(" / ");
|
||||
}
|
||||
|
||||
|
|
@ -694,7 +700,7 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
|||
})}
|
||||
</>
|
||||
);
|
||||
}, [componentConfig.displayItems, componentConfig.additionalFields]);
|
||||
}, [componentConfig.fieldGroups, componentConfig.additionalFields]);
|
||||
|
||||
// 빈 상태 렌더링
|
||||
if (items.length === 0) {
|
||||
|
|
@ -805,7 +811,7 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
|
|||
onClick={() => handleEditGroupEntry(item.id, group.id, entry.id)}
|
||||
>
|
||||
<span className="flex items-center gap-1">
|
||||
{idx + 1}. {renderDisplayItems(entry, item)}
|
||||
{idx + 1}. {renderDisplayItems(entry, item, group.id)}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -46,9 +46,6 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
|
|||
// 🆕 필드 그룹 상태
|
||||
const [localFieldGroups, setLocalFieldGroups] = useState<FieldGroup[]>(config.fieldGroups || []);
|
||||
|
||||
// 🆕 항목 표시 설정 상태
|
||||
const [displayItems, setDisplayItems] = useState<DisplayItem[]>(config.displayItems || []);
|
||||
|
||||
// 🆕 원본 테이블 선택 상태
|
||||
const [sourceTableSelectOpen, setSourceTableSelectOpen] = useState(false);
|
||||
const [sourceTableSearchValue, setSourceTableSearchValue] = useState("");
|
||||
|
|
@ -172,20 +169,17 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
|
|||
);
|
||||
}, [allTables, sourceTableSearchValue]);
|
||||
|
||||
// 🆕 항목 표시 설정 핸들러
|
||||
const handleDisplayItemsChange = (items: DisplayItem[]) => {
|
||||
setDisplayItems(items);
|
||||
handleChange("displayItems", items);
|
||||
};
|
||||
|
||||
const addDisplayItem = (type: DisplayItemType) => {
|
||||
// 🆕 그룹별 항목 표시 설정 핸들러
|
||||
const addDisplayItemToGroup = (groupId: string, type: DisplayItemType) => {
|
||||
const newItem: DisplayItem = {
|
||||
type,
|
||||
id: `display-${Date.now()}`,
|
||||
};
|
||||
|
||||
if (type === "field") {
|
||||
newItem.fieldName = localFields[0]?.name || "";
|
||||
// 해당 그룹의 필드만 선택 가능하도록
|
||||
const groupFields = localFields.filter(f => f.groupId === groupId);
|
||||
newItem.fieldName = groupFields[0]?.name || "";
|
||||
newItem.format = "text";
|
||||
newItem.emptyBehavior = "default";
|
||||
} else if (type === "icon") {
|
||||
|
|
@ -194,17 +188,50 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
|
|||
newItem.value = "텍스트";
|
||||
}
|
||||
|
||||
handleDisplayItemsChange([...displayItems, newItem]);
|
||||
const updatedGroups = localFieldGroups.map(g => {
|
||||
if (g.id === groupId) {
|
||||
return {
|
||||
...g,
|
||||
displayItems: [...(g.displayItems || []), newItem]
|
||||
};
|
||||
}
|
||||
return g;
|
||||
});
|
||||
|
||||
setLocalFieldGroups(updatedGroups);
|
||||
handleChange("fieldGroups", updatedGroups);
|
||||
};
|
||||
|
||||
const removeDisplayItem = (index: number) => {
|
||||
handleDisplayItemsChange(displayItems.filter((_, i) => i !== index));
|
||||
const removeDisplayItemFromGroup = (groupId: string, itemIndex: number) => {
|
||||
const updatedGroups = localFieldGroups.map(g => {
|
||||
if (g.id === groupId) {
|
||||
return {
|
||||
...g,
|
||||
displayItems: (g.displayItems || []).filter((_, i) => i !== itemIndex)
|
||||
};
|
||||
}
|
||||
return g;
|
||||
});
|
||||
|
||||
setLocalFieldGroups(updatedGroups);
|
||||
handleChange("fieldGroups", updatedGroups);
|
||||
};
|
||||
|
||||
const updateDisplayItem = (index: number, updates: Partial<DisplayItem>) => {
|
||||
const updated = [...displayItems];
|
||||
updated[index] = { ...updated[index], ...updates };
|
||||
handleDisplayItemsChange(updated);
|
||||
const updateDisplayItemInGroup = (groupId: string, itemIndex: number, updates: Partial<DisplayItem>) => {
|
||||
const updatedGroups = localFieldGroups.map(g => {
|
||||
if (g.id === groupId) {
|
||||
const updatedItems = [...(g.displayItems || [])];
|
||||
updatedItems[itemIndex] = { ...updatedItems[itemIndex], ...updates };
|
||||
return {
|
||||
...g,
|
||||
displayItems: updatedItems
|
||||
};
|
||||
}
|
||||
return g;
|
||||
});
|
||||
|
||||
setLocalFieldGroups(updatedGroups);
|
||||
handleChange("fieldGroups", updatedGroups);
|
||||
};
|
||||
|
||||
// 🆕 선택된 원본 테이블 표시명
|
||||
|
|
@ -653,6 +680,174 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
|
|||
min="0"
|
||||
/>
|
||||
</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">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItemToGroup(group.id, "icon")}
|
||||
className="h-5 px-1 text-[9px] sm:h-6 sm:px-2 sm:text-[10px]"
|
||||
>
|
||||
<Plus className="mr-0.5 h-2 w-2 sm:mr-1 sm:h-3 sm:w-3" />
|
||||
아이콘
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItemToGroup(group.id, "field")}
|
||||
className="h-5 px-1 text-[9px] sm:h-6 sm:px-2 sm:text-[10px]"
|
||||
>
|
||||
<Plus className="mr-0.5 h-2 w-2 sm:mr-1 sm:h-3 sm:w-3" />
|
||||
필드
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItemToGroup(group.id, "text")}
|
||||
className="h-5 px-1 text-[9px] sm:h-6 sm:px-2 sm:text-[10px]"
|
||||
>
|
||||
<Plus className="mr-0.5 h-2 w-2 sm:mr-1 sm:h-3 sm:w-3" />
|
||||
텍스트
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-[9px] text-muted-foreground sm:text-[10px]">
|
||||
이 그룹의 입력 항목이 추가되면 어떻게 표시될지 설정
|
||||
</p>
|
||||
|
||||
{(!group.displayItems || group.displayItems.length === 0) ? (
|
||||
<div className="rounded border border-dashed p-2 text-center text-[10px] text-muted-foreground">
|
||||
미설정 (모든 필드를 " / "로 구분하여 표시)
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
{group.displayItems.map((item, itemIndex) => (
|
||||
<div key={item.id} className="rounded border bg-card p-2 space-y-1">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[9px] font-medium sm:text-[10px]">
|
||||
{item.type === "icon" && "🎨"}
|
||||
{item.type === "field" && "📝"}
|
||||
{item.type === "text" && "💬"}
|
||||
{item.type === "badge" && "🏷️"}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeDisplayItemFromGroup(group.id, itemIndex)}
|
||||
className="h-4 w-4 p-0"
|
||||
>
|
||||
<X className="h-2 w-2" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 아이콘 설정 */}
|
||||
{item.type === "icon" && (
|
||||
<Input
|
||||
value={item.icon || ""}
|
||||
onChange={(e) => updateDisplayItemInGroup(group.id, itemIndex, { icon: e.target.value })}
|
||||
placeholder="Building"
|
||||
className="h-6 text-[9px] sm:text-[10px]"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 텍스트 설정 */}
|
||||
{item.type === "text" && (
|
||||
<Input
|
||||
value={item.value || ""}
|
||||
onChange={(e) => updateDisplayItemInGroup(group.id, itemIndex, { value: e.target.value })}
|
||||
placeholder="| , / , -"
|
||||
className="h-6 text-[9px] sm:text-[10px]"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 필드 설정 */}
|
||||
{item.type === "field" && (
|
||||
<div className="space-y-1">
|
||||
{/* 필드 선택 */}
|
||||
<Select
|
||||
value={item.fieldName || ""}
|
||||
onValueChange={(value) => updateDisplayItemInGroup(group.id, itemIndex, { fieldName: value })}
|
||||
>
|
||||
<SelectTrigger className="h-6 text-[9px] sm:text-[10px]">
|
||||
<SelectValue placeholder="필드 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{localFields.filter(f => f.groupId === group.id).map((field) => (
|
||||
<SelectItem key={field.name} value={field.name} className="text-[9px] sm:text-[10px]">
|
||||
{field.label || field.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* 라벨 */}
|
||||
<Input
|
||||
value={item.label || ""}
|
||||
onChange={(e) => updateDisplayItemInGroup(group.id, itemIndex, { label: e.target.value })}
|
||||
placeholder="거래처:"
|
||||
className="h-6 text-[9px] sm:text-[10px]"
|
||||
/>
|
||||
|
||||
{/* 표시 형식 */}
|
||||
<Select
|
||||
value={item.format || "text"}
|
||||
onValueChange={(value) => updateDisplayItemInGroup(group.id, itemIndex, { format: value as DisplayFieldFormat })}
|
||||
>
|
||||
<SelectTrigger className="h-6 text-[9px] sm:text-[10px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="text" className="text-[9px] sm:text-[10px]">텍스트</SelectItem>
|
||||
<SelectItem value="currency" className="text-[9px] sm:text-[10px]">금액</SelectItem>
|
||||
<SelectItem value="number" className="text-[9px] sm:text-[10px]">숫자</SelectItem>
|
||||
<SelectItem value="date" className="text-[9px] sm:text-[10px]">날짜</SelectItem>
|
||||
<SelectItem value="badge" className="text-[9px] sm:text-[10px]">배지</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* 빈 값 처리 */}
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
<Select
|
||||
value={item.emptyBehavior || "default"}
|
||||
onValueChange={(value) => updateDisplayItemInGroup(group.id, itemIndex, { emptyBehavior: value as EmptyBehavior })}
|
||||
>
|
||||
<SelectTrigger className="h-6 text-[9px] sm:text-[10px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="hide" className="text-[9px] sm:text-[10px]">숨김</SelectItem>
|
||||
<SelectItem value="default" className="text-[9px] sm:text-[10px]">기본값</SelectItem>
|
||||
<SelectItem value="blank" className="text-[9px] sm:text-[10px]">빈칸</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* 기본값 */}
|
||||
{item.emptyBehavior === "default" && (
|
||||
<Input
|
||||
value={item.defaultValue || ""}
|
||||
onChange={(e) => updateDisplayItemInGroup(group.id, itemIndex, { defaultValue: e.target.value })}
|
||||
placeholder="미입력"
|
||||
className="h-6 text-[9px] sm:text-[10px]"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
|
@ -761,252 +956,6 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* 🆕 항목 표시 설정 */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-xs font-semibold sm:text-sm">항목 표시 설정</Label>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItem("icon")}
|
||||
className="h-6 px-2 text-[10px] sm:h-7 sm:px-3 sm:text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
아이콘
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItem("field")}
|
||||
className="h-6 px-2 text-[10px] sm:h-7 sm:px-3 sm:text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
필드
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => addDisplayItem("text")}
|
||||
className="h-6 px-2 text-[10px] sm:h-7 sm:px-3 sm:text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
텍스트
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-muted-foreground sm:text-xs">
|
||||
각 입력 항목이 추가되면 어떻게 표시될지 설정합니다
|
||||
</p>
|
||||
|
||||
{displayItems.length === 0 ? (
|
||||
<div className="rounded-lg border border-dashed p-3 text-center text-xs text-muted-foreground">
|
||||
표시 항목이 없습니다. 위의 버튼으로 추가하세요.
|
||||
<br />
|
||||
<span className="text-[10px]">(미설정 시 모든 필드를 " / "로 구분하여 표시)</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{displayItems.map((item, index) => (
|
||||
<Card key={item.id} className="border">
|
||||
<CardContent className="p-3 space-y-2">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-medium">
|
||||
{item.type === "icon" && "🎨 아이콘"}
|
||||
{item.type === "field" && "📝 필드"}
|
||||
{item.type === "text" && "💬 텍스트"}
|
||||
{item.type === "badge" && "🏷️ 배지"}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeDisplayItem(index)}
|
||||
className="h-5 w-5 p-0"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 아이콘 설정 */}
|
||||
{item.type === "icon" && (
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">아이콘 이름</Label>
|
||||
<Input
|
||||
value={item.icon || ""}
|
||||
onChange={(e) => updateDisplayItem(index, { icon: e.target.value })}
|
||||
placeholder="예: Building, User, Package"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
<p className="mt-1 text-[9px] text-muted-foreground">
|
||||
lucide-react 아이콘 이름을 입력하세요
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 텍스트 설정 */}
|
||||
{item.type === "text" && (
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">텍스트</Label>
|
||||
<Input
|
||||
value={item.value || ""}
|
||||
onChange={(e) => updateDisplayItem(index, { value: e.target.value })}
|
||||
placeholder="예: | , / , - "
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 필드 설정 */}
|
||||
{item.type === "field" && (
|
||||
<div className="space-y-2">
|
||||
{/* 필드 선택 */}
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">필드</Label>
|
||||
<Select
|
||||
value={item.fieldName || ""}
|
||||
onValueChange={(value) => updateDisplayItem(index, { fieldName: value })}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue placeholder="필드 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{localFields.map((field) => (
|
||||
<SelectItem key={field.name} value={field.name} className="text-xs">
|
||||
{field.label || field.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 라벨 */}
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">라벨</Label>
|
||||
<Input
|
||||
value={item.label || ""}
|
||||
onChange={(e) => updateDisplayItem(index, { label: e.target.value })}
|
||||
placeholder="예: 거래처:, 단가:"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 표시 형식 */}
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">표시 형식</Label>
|
||||
<Select
|
||||
value={item.format || "text"}
|
||||
onValueChange={(value) => updateDisplayItem(index, { format: value as DisplayFieldFormat })}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="text" className="text-xs">일반 텍스트</SelectItem>
|
||||
<SelectItem value="currency" className="text-xs">금액 (천단위 구분)</SelectItem>
|
||||
<SelectItem value="number" className="text-xs">숫자 (천단위 구분)</SelectItem>
|
||||
<SelectItem value="date" className="text-xs">날짜 (YYYY.MM.DD)</SelectItem>
|
||||
<SelectItem value="badge" className="text-xs">배지</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 빈 값 처리 */}
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">값이 없을 때</Label>
|
||||
<Select
|
||||
value={item.emptyBehavior || "default"}
|
||||
onValueChange={(value) => updateDisplayItem(index, { emptyBehavior: value as EmptyBehavior })}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="hide" className="text-xs">항목 숨김</SelectItem>
|
||||
<SelectItem value="default" className="text-xs">기본값 표시</SelectItem>
|
||||
<SelectItem value="blank" className="text-xs">빈 칸으로 표시</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 기본값 (emptyBehavior가 "default"일 때만) */}
|
||||
{item.emptyBehavior === "default" && (
|
||||
<div>
|
||||
<Label className="text-[10px] sm:text-xs">기본값</Label>
|
||||
<Input
|
||||
value={item.defaultValue || ""}
|
||||
onChange={(e) => updateDisplayItem(index, { defaultValue: e.target.value })}
|
||||
placeholder="예: 미입력, 0, -"
|
||||
className="h-7 text-xs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 스타일 설정 */}
|
||||
<div className="space-y-2 rounded border bg-muted/30 p-2">
|
||||
<Label className="text-[10px] font-semibold sm:text-xs">스타일</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<label className="flex items-center gap-1 text-[10px] sm:text-xs">
|
||||
<Checkbox
|
||||
checked={item.bold || false}
|
||||
onCheckedChange={(checked) => updateDisplayItem(index, { bold: checked as boolean })}
|
||||
/>
|
||||
굵게
|
||||
</label>
|
||||
<label className="flex items-center gap-1 text-[10px] sm:text-xs">
|
||||
<Checkbox
|
||||
checked={item.underline || false}
|
||||
onCheckedChange={(checked) => updateDisplayItem(index, { underline: checked as boolean })}
|
||||
/>
|
||||
밑줄
|
||||
</label>
|
||||
<label className="flex items-center gap-1 text-[10px] sm:text-xs">
|
||||
<Checkbox
|
||||
checked={item.italic || false}
|
||||
onCheckedChange={(checked) => updateDisplayItem(index, { italic: checked as boolean })}
|
||||
/>
|
||||
기울임
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* 색상 */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label className="text-[9px] sm:text-[10px]">텍스트 색상</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={item.color || "#000000"}
|
||||
onChange={(e) => updateDisplayItem(index, { color: e.target.value })}
|
||||
className="h-7 w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[9px] sm:text-[10px]">배경 색상</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={item.backgroundColor || "#ffffff"}
|
||||
onChange={(e) => updateDisplayItem(index, { backgroundColor: e.target.value })}
|
||||
className="h-7 w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 사용 예시 */}
|
||||
<div className="rounded-lg bg-blue-50 p-2 text-xs sm:p-3 sm:text-sm">
|
||||
<p className="mb-1 font-medium text-blue-900">💡 사용 예시</p>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ export interface FieldGroup {
|
|||
description?: string;
|
||||
/** 그룹 표시 순서 */
|
||||
order?: number;
|
||||
/** 🆕 이 그룹의 항목 표시 설정 (그룹별로 다른 표시 형식 가능) */
|
||||
displayItems?: DisplayItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -115,12 +117,6 @@ export interface SelectedItemsDetailInputConfig extends ComponentConfig {
|
|||
*/
|
||||
inputMode?: "inline" | "modal";
|
||||
|
||||
/**
|
||||
* 🆕 항목 표시 설정 (각 그룹의 입력 항목을 어떻게 표시할지)
|
||||
* 예: [{ type: "icon", icon: "Building" }, { type: "field", fieldName: "customer_name", label: "거래처:" }]
|
||||
*/
|
||||
displayItems?: DisplayItem[];
|
||||
|
||||
/**
|
||||
* 빈 상태 메시지
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue