editable-column-header.tsx8.3 KBView on GitHub /**
* Editable Column Header Component
* Allows users to edit column name, type, and description
*/
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
Braces,
Calendar,
CheckSquare,
ChevronDown,
Clock,
Hash,
Mail,
Trash2,
Type,
} from 'lucide-react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import type { CRMColumn, ColumnType } from '../store/crmSlice';
import { Textarea } from '@/components/ui/textarea';
import { memo, useCallback, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
// Icon mapping for column types
export const getColumnIcon = (type: ColumnType) => {
switch (type) {
case 'text':
return <Type className="h-4 w-4" />;
case 'number':
return <Hash className="h-4 w-4" />;
case 'date':
return <Calendar className="h-4 w-4" />;
case 'json':
return <Braces className="h-4 w-4" />;
case 'checkbox':
return <CheckSquare className="h-4 w-4" />;
case 'select':
return <ChevronDown className="h-4 w-4" />;
case 'email':
return <Mail className="h-4 w-4" />;
case 'timeline':
return <Clock className="h-4 w-4" />;
default:
return <Type className="h-4 w-4" />;
}
};
interface EditableColumnHeaderProps {
colConfig: CRMColumn;
onUpdate: (updatedColumn: CRMColumn) => void;
onDelete: (columnId: string) => void;
}
export const EditableColumnHeader = memo(
({ colConfig, onUpdate, onDelete }: EditableColumnHeaderProps) => {
const [isOpen, setIsOpen] = useState(false);
const [editingColumn, setEditingColumn] = useState<CRMColumn | null>(null);
const handleOpenChange = useCallback(
(open: boolean) => {
if (open) {
setEditingColumn({ ...colConfig });
setIsOpen(true);
} else {
setIsOpen(false);
setEditingColumn(null);
}
},
[colConfig],
);
const handleSave = useCallback(() => {
if (editingColumn) {
onUpdate(editingColumn);
setIsOpen(false);
setEditingColumn(null);
}
}, [editingColumn, onUpdate]);
const handleDelete = useCallback(() => {
onDelete(colConfig.id);
setIsOpen(false);
setEditingColumn(null);
}, [colConfig.id, onDelete]);
return (
<div className="flex min-w-0 items-center gap-2">
<Popover open={isOpen} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild>
<button className="flex min-w-0 flex-1 items-center gap-2 text-left hover:opacity-70">
<div className="text-muted-foreground shrink-0">{getColumnIcon(colConfig.type)}</div>
<span className="truncate font-medium">{colConfig.name}</span>
</button>
</PopoverTrigger>
<PopoverContent className="w-96" align="start">
{editingColumn && (
<div className="space-y-4">
<div>
<h4 className="mb-3 font-medium leading-none">Edit Column</h4>
</div>
<div className="space-y-2">
<Label htmlFor={`edit-name-${colConfig.id}`}>Column Name</Label>
<Input
id={`edit-name-${colConfig.id}`}
value={editingColumn.name}
onChange={(e) => setEditingColumn({ ...editingColumn, name: e.target.value })}
placeholder="Column name"
/>
</div>
<div className="space-y-2">
<Label>Column Type</Label>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="w-full justify-between">
<span className="flex items-center gap-2">
{getColumnIcon(editingColumn.type)}
<span className="capitalize">{editingColumn.type}</span>
</span>
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-full">
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'text' })}
>
<Type className="mr-2 h-4 w-4" />
Text
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'email' })}
>
<Mail className="mr-2 h-4 w-4" />
Email
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'number' })}
>
<Hash className="mr-2 h-4 w-4" />
Number
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'date' })}
>
<Calendar className="mr-2 h-4 w-4" />
Date
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'checkbox' })}
>
<CheckSquare className="mr-2 h-4 w-4" />
Checkbox
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'select' })}
>
<ChevronDown className="mr-2 h-4 w-4" />
Select
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setEditingColumn({ ...editingColumn, type: 'timeline' })}
>
<Clock className="mr-2 h-4 w-4" />
Timeline
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="space-y-2">
<Label htmlFor={`edit-description-${colConfig.id}`}>Description</Label>
<Textarea
id={`edit-description-${colConfig.id}`}
value={editingColumn.description}
onChange={(e) =>
setEditingColumn({ ...editingColumn, description: e.target.value })
}
placeholder="Add a description for this column..."
rows={3}
/>
</div>
{colConfig.isFixed && (
<div className="bg-muted rounded-md p-3">
<p className="text-muted-foreground text-sm">
This is a fixed column and cannot be deleted.
</p>
</div>
)}
<div className="flex gap-2">
{!colConfig.isFixed && (
<Button
variant="destructive"
size="sm"
className="flex-1"
onClick={handleDelete}
>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</Button>
)}
<Button
variant="outline"
size="sm"
className="flex-1"
onClick={() => {
setIsOpen(false);
setEditingColumn(null);
}}
>
Cancel
</Button>
<Button size="sm" className="flex-1" onClick={handleSave}>
Save
</Button>
</div>
</div>
)}
</PopoverContent>
</Popover>
</div>
);
},
);
EditableColumnHeader.displayName = 'EditableColumnHeader';