feat: implement label management with color filtering
This commit is contained in:
@@ -15,8 +15,8 @@ import {
|
||||
import { Badge } from './ui/badge'
|
||||
import { Tag, X, Plus, Palette } from 'lucide-react'
|
||||
import { LABEL_COLORS, LabelColorName } from '@/lib/types'
|
||||
import { getLabelColor, setLabelColor, deleteLabelColor, getAllLabelColors } from '@/lib/label-storage'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useLabels, Label } from '@/context/LabelContext'
|
||||
|
||||
interface LabelManagerProps {
|
||||
existingLabels: string[]
|
||||
@@ -24,30 +24,31 @@ interface LabelManagerProps {
|
||||
}
|
||||
|
||||
export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
const { labels, loading, addLabel, updateLabel, deleteLabel, getLabelColor } = useLabels()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [newLabel, setNewLabel] = useState('')
|
||||
const [selectedLabels, setSelectedLabels] = useState<string[]>(existingLabels)
|
||||
const [allLabelsInStorage, setAllLabelsInStorage] = useState<string[]>([])
|
||||
const [editingColor, setEditingColor] = useState<string | null>(null)
|
||||
|
||||
// Load all labels from localStorage
|
||||
// Sync selected labels with existingLabels prop
|
||||
useEffect(() => {
|
||||
const allColors = getAllLabelColors()
|
||||
setAllLabelsInStorage(Object.keys(allColors))
|
||||
}, [open])
|
||||
setSelectedLabels(existingLabels)
|
||||
}, [existingLabels])
|
||||
|
||||
const handleAddLabel = () => {
|
||||
const handleAddLabel = async () => {
|
||||
const trimmed = newLabel.trim()
|
||||
if (trimmed && !selectedLabels.includes(trimmed)) {
|
||||
const updated = [...selectedLabels, trimmed]
|
||||
setSelectedLabels(updated)
|
||||
setNewLabel('')
|
||||
|
||||
// Set default color if doesn't exist
|
||||
if (getLabelColor(trimmed) === 'gray') {
|
||||
const colors = Object.keys(LABEL_COLORS) as LabelColorName[]
|
||||
const randomColor = colors[Math.floor(Math.random() * colors.length)]
|
||||
setLabelColor(trimmed, randomColor)
|
||||
try {
|
||||
// Get existing label color or use random
|
||||
const existingLabel = labels.find(l => l.name === trimmed)
|
||||
const color = existingLabel?.color || (Object.keys(LABEL_COLORS) as LabelColorName[])[Math.floor(Math.random() * Object.keys(LABEL_COLORS).length)]
|
||||
|
||||
await addLabel(trimmed, color)
|
||||
const updated = [...selectedLabels, trimmed]
|
||||
setSelectedLabels(updated)
|
||||
setNewLabel('')
|
||||
} catch (error) {
|
||||
console.error('Failed to add label:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,12 +65,16 @@ export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleChangeColor = (label: string, color: LabelColorName) => {
|
||||
setLabelColor(label, color)
|
||||
setEditingColor(null)
|
||||
// Force re-render
|
||||
const allColors = getAllLabelColors()
|
||||
setAllLabelsInStorage(Object.keys(allColors))
|
||||
const handleChangeColor = async (label: string, color: LabelColorName) => {
|
||||
const labelObj = labels.find(l => l.name === label)
|
||||
if (labelObj) {
|
||||
try {
|
||||
await updateLabel(labelObj.id, { color })
|
||||
setEditingColor(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to update label color:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
@@ -130,13 +135,13 @@ export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
<h4 className="text-sm font-medium mb-2">Selected Labels</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedLabels.map((label) => {
|
||||
const colorName = getLabelColor(label)
|
||||
const colorClasses = LABEL_COLORS[colorName]
|
||||
const labelObj = labels.find(l => l.name === label)
|
||||
const colorClasses = labelObj ? LABEL_COLORS[labelObj.color] : LABEL_COLORS.gray
|
||||
const isEditing = editingColor === label
|
||||
|
||||
return (
|
||||
<div key={label} className="relative">
|
||||
{isEditing ? (
|
||||
{isEditing && labelObj ? (
|
||||
<div className="absolute z-10 top-8 left-0 bg-white dark:bg-zinc-900 border rounded-lg shadow-lg p-2">
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{(Object.keys(LABEL_COLORS) as LabelColorName[]).map((color) => {
|
||||
@@ -147,7 +152,7 @@ export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
className={cn(
|
||||
'h-8 w-8 rounded-full border-2 transition-transform hover:scale-110',
|
||||
classes.bg,
|
||||
colorName === color ? 'border-gray-900 dark:border-gray-100' : 'border-gray-300 dark:border-gray-600'
|
||||
labelObj.color === color ? 'border-gray-900 dark:border-gray-100' : 'border-gray-300 dark:border-gray-600'
|
||||
)}
|
||||
onClick={() => handleChangeColor(label, color)}
|
||||
title={color}
|
||||
@@ -185,20 +190,19 @@ export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Available labels from storage */}
|
||||
{allLabelsInStorage.length > 0 && (
|
||||
{/* Available labels from context */}
|
||||
{!loading && labels.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-2">All Labels</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{allLabelsInStorage
|
||||
.filter(label => !selectedLabels.includes(label))
|
||||
{labels
|
||||
.filter(label => !selectedLabels.includes(label.name))
|
||||
.map((label) => {
|
||||
const colorName = getLabelColor(label)
|
||||
const colorClasses = LABEL_COLORS[colorName]
|
||||
const colorClasses = LABEL_COLORS[label.color]
|
||||
|
||||
return (
|
||||
<Badge
|
||||
key={label}
|
||||
key={label.id}
|
||||
className={cn(
|
||||
'text-xs border cursor-pointer',
|
||||
colorClasses.bg,
|
||||
@@ -206,9 +210,9 @@ export function LabelManager({ existingLabels, onUpdate }: LabelManagerProps) {
|
||||
colorClasses.border,
|
||||
'hover:opacity-80'
|
||||
)}
|
||||
onClick={() => handleSelectExisting(label)}
|
||||
onClick={() => handleSelectExisting(label.name)}
|
||||
>
|
||||
{label}
|
||||
{label.name}
|
||||
</Badge>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user