import { useState, useRef, useEffect } from 'react' import type { ChecklistItem as ChecklistItemType } from '../types' import DependencyManager from './DependencyManager' interface ChecklistItemProps { item: ChecklistItemType onUpdate: (id: number, updates: Partial) => Promise onDelete: (id: number) => Promise onLock: (id: number, user: string) => Promise depth?: number children?: ChecklistItemType[] allItems?: ChecklistItemType[] } export default function ChecklistItem({ item, onUpdate, onDelete, onLock, depth = 0, children = [], allItems = [] }: ChecklistItemProps) { const [isEditing, setIsEditing] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const [isDependencyModalOpen, setIsDependencyModalOpen] = useState(false) const [userName, setUserName] = useState('') const contentRef = useRef(null) const isLocked = item.locked_by && item.lock_until && new Date(item.lock_until) > new Date() const isLockedByMe = isLocked && item.locked_by === userName // Check if all dependencies are completed const dependenciesCompleted = item.dependencies?.every(depId => { const depItem = allItems.find(i => i.id === depId) return depItem?.checked }) ?? true // Check if item can be completed (all dependencies met) const canComplete = dependenciesCompleted || item.checked // Get dependency items for display const dependencyItems = item.dependencies?.map(depId => allItems.find(i => i.id === depId) ).filter(Boolean) ?? [] useEffect(() => { // Generate a random user name if not set if (!userName) { const randomName = `user_${Math.random().toString(36).substr(2, 9)}` setUserName(randomName) } }, [userName]) useEffect(() => { if (isEditing && contentRef.current) { contentRef.current.focus() // Select all text when entering edit mode const range = document.createRange() range.selectNodeContents(contentRef.current) const selection = window.getSelection() selection?.removeAllRanges() selection?.addRange(range) } }, [isEditing]) const handleEdit = async () => { if (isLocked && !isLockedByMe) { alert('This item is being edited by someone else') return } if (!isLockedByMe) { try { await onLock(item.id, userName) } catch (error) { console.error('Failed to lock item:', error) return } } setIsEditing(true) } const handleSave = async () => { const newContent = contentRef.current?.textContent?.trim() || '' if (newContent === '') return try { await onUpdate(item.id, { content: newContent }) setIsEditing(false) } catch (error) { console.error('Failed to update item:', error) } } const handleCancel = () => { if (contentRef.current) { contentRef.current.textContent = item.content } setIsEditing(false) } const handleDelete = async () => { if (confirm('Are you sure you want to delete this item?')) { try { setIsDeleting(true) await onDelete(item.id) } catch (error) { console.error('Failed to delete item:', error) } finally { setIsDeleting(false) } } } const handleToggleCheck = async () => { // Don't allow unchecking if already checked if (item.checked) { try { await onUpdate(item.id, { checked: false }) } catch (error) { console.error('Failed to uncheck item:', error) } return } // Check if dependencies are met before allowing completion if (!dependenciesCompleted) { alert(`Cannot complete this item. The following dependencies must be completed first:\n\n${dependencyItems.map(dep => `• ${dep?.content}`).join('\n')}`) return } try { await onUpdate(item.id, { checked: true }) } catch (error) { console.error('Failed to toggle item:', error) if (error instanceof Error && error.message.includes('dependency')) { alert(error.message) } } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault() handleSave() } else if (e.key === 'Escape') { e.preventDefault() handleCancel() } } const handleBlur = () => { // Small delay to allow for button clicks setTimeout(() => { if (isEditing) { handleSave() } }, 100) } return (
  • {/* Checkbox */}
    {/* Content */}
    {item.content} {/* Dependency warning */} {!item.checked && !canComplete && dependencyItems.length > 0 && (
    ⚠️ Depends on: {dependencyItems.map(dep => dep?.content).join(', ')}
    )}
    {/* Actions */}
    {/* Dependency indicators */} {item.dependencies && item.dependencies.length > 0 && (
    {dependenciesCompleted ? 'Ready' : `${item.dependencies.length} deps`}
    )} {isLocked && !isLockedByMe && ( {item.locked_by} )} {!isEditing && ( <> )}
    {/* Children */} {children.length > 0 && (
    {children.map(child => ( ))}
    )} {/* Dependency Manager Modal */} setIsDependencyModalOpen(false)} isOpen={isDependencyModalOpen} />
  • ) }