feat: 카테고리 값에 배지 색상 설정 기능 추가
- 카테고리 값 추가/편집 다이얼로그에 색상 선택기 추가 - 18가지 기본 색상 팔레트 제공 - 선택한 색상의 실시간 배지 미리보기 - color 필드를 통해 DB에 저장 - 테이블 리스트에서 배지 형태로 표시할 준비 완료
This commit is contained in:
parent
a1cb9d2a8e
commit
1d87b6c3ac
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Reference in New Issue