feat: 카테고리 배지 없음 옵션 추가

- 카테고리 색상 설정 시 '배지 없음' 옵션 추가
- color='none'일 때 배지 대신 일반 텍스트로 표시
- CategoryValueEditDialog, CategoryValueAddDialog에 배지 없음 버튼 추가
- InteractiveDataTable, TableListComponent에서 배지 없음 처리
- CategoryValueManager에서 배지 없음 표시 추가
- 기본 색상을 배지 없음으로 변경
This commit is contained in:
kjs 2025-11-13 15:24:31 +09:00
parent f73f788b0a
commit a828f54663
5 changed files with 91 additions and 53 deletions

View File

@ -1961,7 +1961,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
// 실제 웹 타입으로 스위치 (input_type="category"도 포함됨)
switch (actualWebType) {
case "category": {
// 카테고리 타입: 배지로 표시
// 카테고리 타입: 배지로 표시 (배지 없음 옵션 지원)
if (!value) return "";
const mapping = categoryMappings[column.columnName];
@ -1971,6 +1971,11 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const displayLabel = categoryData?.label || String(value);
const displayColor = categoryData?.color || "#64748b"; // 기본 slate 색상
// 배지 없음 옵션: color가 "none"이면 텍스트만 표시
if (displayColor === "none") {
return <span className="text-sm">{displayLabel}</span>;
}
return (
<Badge
style={{

View File

@ -50,7 +50,7 @@ export const CategoryValueAddDialog: React.FC<
> = ({ open, onOpenChange, onAdd, columnLabel }) => {
const [valueLabel, setValueLabel] = useState("");
const [description, setDescription] = useState("");
const [color, setColor] = useState("#3b82f6");
const [color, setColor] = useState("none");
// 라벨에서 코드 자동 생성
const generateCode = (label: string): string => {
@ -91,7 +91,7 @@ export const CategoryValueAddDialog: React.FC<
// 초기화
setValueLabel("");
setDescription("");
setColor("#3b82f6");
setColor("none");
};
return (
@ -123,24 +123,41 @@ export const CategoryValueAddDialog: React.FC<
<div>
<Label className="text-xs sm:text-sm"> </Label>
<div className="mt-1.5 flex items-center gap-3">
<div className="grid grid-cols-9 gap-2">
{DEFAULT_COLORS.map((c) => (
<button
key={c}
type="button"
onClick={() => setColor(c)}
className={`h-7 w-7 rounded-md border-2 transition-all ${
color === c ? "border-foreground scale-110" : "border-transparent hover:scale-105"
}`}
style={{ backgroundColor: c }}
title={c}
/>
))}
<div className="mt-1.5 space-y-2">
<div className="flex items-center gap-3">
<div className="grid grid-cols-9 gap-2">
{DEFAULT_COLORS.map((c) => (
<button
key={c}
type="button"
onClick={() => setColor(c)}
className={`h-7 w-7 rounded-md border-2 transition-all ${
color === c ? "border-foreground scale-110" : "border-transparent hover:scale-105"
}`}
style={{ backgroundColor: c }}
title={c}
/>
))}
</div>
{color && color !== "none" ? (
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
) : (
<span className="text-xs text-muted-foreground"> </span>
)}
</div>
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
<button
type="button"
onClick={() => setColor("none")}
className={`text-xs px-3 py-1.5 rounded-md border transition-colors ${
color === "none"
? "border-primary bg-primary/10 text-primary font-medium"
: "border-border hover:bg-accent"
}`}
>
</button>
</div>
</div>

View File

@ -51,12 +51,12 @@ export const CategoryValueEditDialog: React.FC<
> = ({ open, onOpenChange, value, onUpdate, columnLabel }) => {
const [valueLabel, setValueLabel] = useState(value.valueLabel);
const [description, setDescription] = useState(value.description || "");
const [color, setColor] = useState(value.color || "#3b82f6");
const [color, setColor] = useState(value.color || "none");
useEffect(() => {
setValueLabel(value.valueLabel);
setDescription(value.description || "");
setColor(value.color || "#3b82f6");
setColor(value.color || "none");
}, [value]);
const handleSubmit = () => {
@ -100,24 +100,41 @@ export const CategoryValueEditDialog: React.FC<
<div>
<Label className="text-xs sm:text-sm"> </Label>
<div className="mt-1.5 flex items-center gap-3">
<div className="grid grid-cols-9 gap-2">
{DEFAULT_COLORS.map((c) => (
<button
key={c}
type="button"
onClick={() => setColor(c)}
className={`h-7 w-7 rounded-md border-2 transition-all ${
color === c ? "border-foreground scale-110" : "border-transparent hover:scale-105"
}`}
style={{ backgroundColor: c }}
title={c}
/>
))}
<div className="mt-1.5 space-y-2">
<div className="flex items-center gap-3">
<div className="grid grid-cols-9 gap-2">
{DEFAULT_COLORS.map((c) => (
<button
key={c}
type="button"
onClick={() => setColor(c)}
className={`h-7 w-7 rounded-md border-2 transition-all ${
color === c ? "border-foreground scale-110" : "border-transparent hover:scale-105"
}`}
style={{ backgroundColor: c }}
title={c}
/>
))}
</div>
{color && color !== "none" ? (
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
) : (
<span className="text-xs text-muted-foreground"> </span>
)}
</div>
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
<button
type="button"
onClick={() => setColor("none")}
className={`text-xs px-3 py-1.5 rounded-md border transition-colors ${
color === "none"
? "border-primary bg-primary/10 text-primary font-medium"
: "border-border hover:bg-accent"
}`}
>
</button>
</div>
</div>

View File

@ -349,13 +349,18 @@ export const CategoryValueManager: React.FC<CategoryValueManagerProps> = ({
/>
<div className="flex flex-1 items-center gap-2">
{/* 색상 표시 (앞쪽으로 이동) */}
{value.color && (
{/* 색상 표시 (배지 없음 옵션 지원) */}
{value.color && value.color !== "none" && (
<div
className="h-4 w-4 rounded-full border flex-shrink-0"
style={{ backgroundColor: value.color }}
/>
)}
{value.color === "none" && (
<span className="text-[10px] text-muted-foreground px-1.5 py-0.5 bg-muted rounded">
</span>
)}
{/* 라벨 */}
<span className={`text-sm font-medium ${isInactive ? "line-through" : ""}`}>

View File

@ -1374,28 +1374,22 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
);
}
// 카테고리 타입: 배지로 표시
// 카테고리 타입: 배지로 표시 (배지 없음 옵션 지원)
if (inputType === "category") {
if (!value) return "";
const mapping = categoryMappings[column.columnName];
const categoryData = mapping?.[String(value)];
// console.log(`🎨 [카테고리 배지] ${column.columnName}:`, {
// value,
// stringValue: String(value),
// mapping,
// categoryData,
// hasMapping: !!mapping,
// hasCategoryData: !!categoryData,
// allCategoryMappings: categoryMappings, // 전체 매핑 확인
// categoryMappingsKeys: Object.keys(categoryMappings),
// });
// 매핑 데이터가 있으면 라벨과 색상 사용, 없으면 코드값과 기본색상
const displayLabel = categoryData?.label || String(value);
const displayColor = categoryData?.color || "#64748b"; // 기본 slate 색상
// 배지 없음 옵션: color가 "none"이면 텍스트만 표시
if (displayColor === "none") {
return <span className="text-sm">{displayLabel}</span>;
}
const { Badge } = require("@/components/ui/badge");
return (
<Badge