feat: 카테고리 값에 배지 색상 설정 기능 추가

- 카테고리 값 추가/편집 다이얼로그에 색상 선택기 추가
- 18가지 기본 색상 팔레트 제공
- 선택한 색상의 실시간 배지 미리보기
- color 필드를 통해 DB에 저장
- 테이블 리스트에서 배지 형태로 표시할 준비 완료
This commit is contained in:
kjs 2025-11-06 12:09:28 +09:00
parent a1cb9d2a8e
commit 1d87b6c3ac
2 changed files with 152 additions and 33 deletions

View File

@ -11,9 +11,33 @@ import {
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import { TableCategoryValue } from "@/types/tableCategoryValue";
// 기본 색상 팔레트
const DEFAULT_COLORS = [
"#ef4444", // red
"#f97316", // orange
"#f59e0b", // amber
"#eab308", // yellow
"#84cc16", // lime
"#22c55e", // green
"#10b981", // emerald
"#14b8a6", // teal
"#06b6d4", // cyan
"#0ea5e9", // sky
"#3b82f6", // blue
"#6366f1", // indigo
"#8b5cf6", // violet
"#a855f7", // purple
"#d946ef", // fuchsia
"#ec4899", // pink
"#64748b", // slate
"#6b7280", // gray
];
interface CategoryValueAddDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
@ -26,6 +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 generateCode = (label: string): string => {
@ -59,13 +84,14 @@ export const CategoryValueAddDialog: React.FC<
valueCode,
valueLabel: valueLabel.trim(),
description: description.trim(),
color: "#3b82f6",
color: color,
isDefault: false,
});
// 초기화
setValueLabel("");
setDescription("");
setColor("#3b82f6");
};
return (
@ -81,23 +107,56 @@ export const CategoryValueAddDialog: React.FC<
</DialogHeader>
<div className="space-y-3 sm:space-y-4">
<Input
id="valueLabel"
placeholder="이름 (예: 개발, 긴급, 진행중)"
value={valueLabel}
onChange={(e) => setValueLabel(e.target.value)}
className="h-8 text-xs sm:h-10 sm:text-sm"
autoFocus
/>
<div>
<Label htmlFor="valueLabel" className="text-xs sm:text-sm">
</Label>
<Input
id="valueLabel"
placeholder="예: 개발, 긴급, 진행중"
value={valueLabel}
onChange={(e) => setValueLabel(e.target.value)}
className="h-8 text-xs sm:h-10 sm:text-sm mt-1.5"
autoFocus
/>
</div>
<Textarea
id="description"
placeholder="설명 (선택사항)"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="text-xs sm:text-sm"
rows={3}
/>
<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>
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
</div>
</div>
<div>
<Label htmlFor="description" className="text-xs sm:text-sm">
()
</Label>
<Textarea
id="description"
placeholder="설명을 입력하세요"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="text-xs sm:text-sm mt-1.5"
rows={3}
/>
</div>
</div>
<DialogFooter className="gap-2 sm:gap-0">

View File

@ -11,7 +11,9 @@ import {
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import { TableCategoryValue } from "@/types/tableCategoryValue";
interface CategoryValueEditDialogProps {
@ -22,15 +24,39 @@ interface CategoryValueEditDialogProps {
columnLabel: string;
}
// 기본 색상 팔레트
const DEFAULT_COLORS = [
"#ef4444", // red
"#f97316", // orange
"#f59e0b", // amber
"#eab308", // yellow
"#84cc16", // lime
"#22c55e", // green
"#10b981", // emerald
"#14b8a6", // teal
"#06b6d4", // cyan
"#0ea5e9", // sky
"#3b82f6", // blue
"#6366f1", // indigo
"#8b5cf6", // violet
"#a855f7", // purple
"#d946ef", // fuchsia
"#ec4899", // pink
"#64748b", // slate
"#6b7280", // gray
];
export const CategoryValueEditDialog: React.FC<
CategoryValueEditDialogProps
> = ({ open, onOpenChange, value, onUpdate, columnLabel }) => {
const [valueLabel, setValueLabel] = useState(value.valueLabel);
const [description, setDescription] = useState(value.description || "");
const [color, setColor] = useState(value.color || "#3b82f6");
useEffect(() => {
setValueLabel(value.valueLabel);
setDescription(value.description || "");
setColor(value.color || "#3b82f6");
}, [value]);
const handleSubmit = () => {
@ -41,6 +67,7 @@ export const CategoryValueEditDialog: React.FC<
onUpdate(value.valueId!, {
valueLabel: valueLabel.trim(),
description: description.trim(),
color: color,
});
};
@ -57,23 +84,56 @@ export const CategoryValueEditDialog: React.FC<
</DialogHeader>
<div className="space-y-3 sm:space-y-4">
<Input
id="valueLabel"
placeholder="이름 (예: 개발, 긴급, 진행중)"
value={valueLabel}
onChange={(e) => setValueLabel(e.target.value)}
className="h-8 text-xs sm:h-10 sm:text-sm"
autoFocus
/>
<div>
<Label htmlFor="valueLabel" className="text-xs sm:text-sm">
</Label>
<Input
id="valueLabel"
placeholder="예: 개발, 긴급, 진행중"
value={valueLabel}
onChange={(e) => setValueLabel(e.target.value)}
className="h-8 text-xs sm:h-10 sm:text-sm mt-1.5"
autoFocus
/>
</div>
<Textarea
id="description"
placeholder="설명 (선택사항)"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="text-xs sm:text-sm"
rows={3}
/>
<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>
<Badge style={{ backgroundColor: color, borderColor: color }} className="text-white">
</Badge>
</div>
</div>
<div>
<Label htmlFor="description" className="text-xs sm:text-sm">
()
</Label>
<Textarea
id="description"
placeholder="설명을 입력하세요"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="text-xs sm:text-sm mt-1.5"
rows={3}
/>
</div>
</div>
<DialogFooter className="gap-2 sm:gap-0">