import { useState } from 'react' import { useTripStore } from '../../store/tripStore' import { useToast } from '../shared/Toast' import { useTranslation } from '../../i18n' import { CheckSquare, Square, Trash2, Plus, Pencil, Package, } from 'lucide-react' import type { PackingItem, PackingBag } from '../../types' import { katColor } from './packingListPanel.helpers' import { PACKING_PLACEHOLDER_NAME } from './packingListPanel.constants' import { QuantityInput } from './PackingListPanelQuantityInput' interface ArtikelZeileProps { item: PackingItem tripId: number categories: string[] onCategoryChange: () => void onDelete?: (item: PackingItem) => Promise bagTrackingEnabled?: boolean bags?: PackingBag[] onCreateBag: (name: string) => Promise canEdit?: boolean } export function ArtikelZeile({ item, tripId, categories, onCategoryChange, onDelete, bagTrackingEnabled, bags = [], onCreateBag, canEdit = true }: ArtikelZeileProps) { const isPlaceholder = item.name === PACKING_PLACEHOLDER_NAME const [editing, setEditing] = useState(false) const [editName, setEditName] = useState(isPlaceholder ? '' : item.name) const [hovered, setHovered] = useState(false) const [showCatPicker, setShowCatPicker] = useState(false) const [showBagPicker, setShowBagPicker] = useState(false) const [bagInlineCreate, setBagInlineCreate] = useState(false) const [bagInlineName, setBagInlineName] = useState('') const { togglePackingItem, updatePackingItem, deletePackingItem } = useTripStore() const toast = useToast() const { t } = useTranslation() const handleToggle = () => togglePackingItem(tripId, item.id, !item.checked) const handleSaveName = async () => { if (!editName.trim()) { setEditing(false); setEditName(isPlaceholder ? '' : item.name); return } try { await updatePackingItem(tripId, item.id, { name: editName.trim() }); setEditing(false) } catch { toast.error(t('packing.toast.saveError')) } } const handleDelete = async () => { // The panel routes deletion through onDelete so an emptied custom category // keeps its placeholder; fall back to a plain delete when used standalone. if (onDelete) { await onDelete(item); return } try { await deletePackingItem(tripId, item.id) } catch { toast.error(t('packing.toast.deleteError')) } } const handleCatChange = async (cat: string) => { setShowCatPicker(false) if (cat === item.category) return try { await updatePackingItem(tripId, item.id, { category: cat }) } catch { toast.error(t('common.error')) } } return (
setHovered(true)} onMouseLeave={() => { setHovered(false); setShowCatPicker(false); setShowBagPicker(false) }} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px', borderRadius: 10, position: 'relative', background: hovered ? 'var(--bg-secondary)' : 'transparent', transition: 'background 0.1s', }} > {editing && canEdit ? ( setEditName(e.target.value)} onBlur={handleSaveName} onKeyDown={e => { if (e.key === 'Enter') handleSaveName(); if (e.key === 'Escape') { setEditing(false); setEditName(isPlaceholder ? '' : item.name) } }} style={{ flex: 1, fontSize: 13.5, padding: '2px 8px', borderRadius: 6, border: '1px solid var(--border-primary)', outline: 'none', fontFamily: 'inherit' }} /> ) : ( canEdit && !item.checked && setEditing(true)} style={{ flex: 1, fontSize: 13.5, cursor: !canEdit || item.checked ? 'default' : 'text', color: isPlaceholder ? 'var(--text-faint)' : (item.checked ? 'var(--text-faint)' : 'var(--text-primary)'), transition: 'color 200ms cubic-bezier(0.23,1,0.32,1)', textDecoration: item.checked ? 'line-through' : 'none', }} > {item.name} )} {/* Quantity */} {canEdit && updatePackingItem(tripId, item.id, { quantity: qty })} />} {/* Weight + Bag (when enabled) */} {bagTrackingEnabled && (
{ if (!canEdit) return const raw = e.target.value.replace(/[^0-9]/g, '') const v = raw === '' ? null : parseInt(raw) try { await updatePackingItem(tripId, item.id, { weight_grams: v }) } catch { toast.error(t('packing.toast.saveError')) } }} placeholder="—" style={{ width: 36, border: 'none', fontSize: 12, textAlign: 'right', fontFamily: 'inherit', outline: 'none', color: 'var(--text-secondary)', background: 'transparent', padding: 0 }} /> g
{showBagPicker && (
{item.bag_id && ( )} {bags.map(b => ( ))} {bags.length > 0 &&
}
{bagInlineCreate ? (
setBagInlineName(e.target.value)} onKeyDown={async e => { if (e.key === 'Enter' && bagInlineName.trim()) { const newBag = await onCreateBag(bagInlineName.trim()) if (newBag) { try { await updatePackingItem(tripId, item.id, { bag_id: newBag.id }) } catch { toast.error(t('packing.toast.saveError')) } } setBagInlineName(''); setBagInlineCreate(false); setShowBagPicker(false) } if (e.key === 'Escape') { setBagInlineCreate(false); setBagInlineName('') } }} placeholder={t('packing.bagName')} style={{ flex: 1, padding: '4px 8px', borderRadius: 6, border: '1px solid var(--border-primary)', fontSize: 11, fontFamily: 'inherit', outline: 'none' }} />
) : ( )}
)}
)} {canEdit && (
{showCatPicker && (
{categories.map(cat => ( ))}
)}
)}
) }