import { useState, useEffect, useRef } from 'react' import { adminApi } from '../../api/client' import { useToast } from '../shared/Toast' import { useTranslation } from '../../i18n' import { Plus, Trash2, Edit2, Package, X, Check, ChevronDown, ChevronRight, FolderPlus } from 'lucide-react' interface TemplateCategory { id: number; template_id: number; name: string; sort_order: number } interface TemplateItem { id: number; category_id: number; name: string; sort_order: number } interface Template { id: number; name: string; item_count: number; category_count: number; created_by_name: string } export default function PackingTemplateManager() { const [templates, setTemplates] = useState([]) const [isLoading, setIsLoading] = useState(true) const [showCreate, setShowCreate] = useState(false) const [createName, setCreateName] = useState('') // Expanded template state const [expandedId, setExpandedId] = useState(null) const [categories, setCategories] = useState([]) const [items, setItems] = useState([]) // Editing states const [editingTemplate, setEditingTemplate] = useState(null) const [editTemplateName, setEditTemplateName] = useState('') const [editingCatId, setEditingCatId] = useState(null) const [editCatName, setEditCatName] = useState('') const [editingItemId, setEditingItemId] = useState(null) const [editItemName, setEditItemName] = useState('') // Adding states const [addingCategory, setAddingCategory] = useState(false) const [newCatName, setNewCatName] = useState('') const [addingItemToCatId, setAddingItemToCatId] = useState(null) const [newItemName, setNewItemName] = useState('') const addItemRef = useRef(null) const toast = useToast() const { t } = useTranslation() useEffect(() => { loadTemplates() }, []) const loadTemplates = async () => { setIsLoading(true) try { const data = await adminApi.packingTemplates() setTemplates(data.templates || []) } catch { toast.error(t('admin.packingTemplates.loadError')) } finally { setIsLoading(false) } } const toggleExpand = async (id: number) => { if (expandedId === id) { setExpandedId(null); return } setExpandedId(id) setAddingCategory(false) setAddingItemToCatId(null) try { const data = await adminApi.getPackingTemplate(id) setCategories(data.categories || []) setItems(data.items || []) } catch { toast.error(t('admin.packingTemplates.loadError')) } } // Template CRUD const handleCreateTemplate = async () => { if (!createName.trim()) return try { const data = await adminApi.createPackingTemplate({ name: createName.trim() }) setTemplates(prev => [{ ...data.template, item_count: 0, category_count: 0 }, ...prev]) setCreateName(''); setShowCreate(false) setExpandedId(data.template.id); setCategories([]); setItems([]) toast.success(t('admin.packingTemplates.created')) } catch { toast.error(t('admin.packingTemplates.createError')) } } const handleDeleteTemplate = async (id: number) => { try { await adminApi.deletePackingTemplate(id) setTemplates(prev => prev.filter(t => t.id !== id)) if (expandedId === id) setExpandedId(null) toast.success(t('admin.packingTemplates.deleted')) } catch { toast.error(t('admin.packingTemplates.deleteError')) } } const handleRenameTemplate = async (id: number) => { if (!editTemplateName.trim()) { setEditingTemplate(null); return } try { await adminApi.updatePackingTemplate(id, { name: editTemplateName.trim() }) setTemplates(prev => prev.map(t => t.id === id ? { ...t, name: editTemplateName.trim() } : t)) setEditingTemplate(null) } catch { toast.error(t('admin.packingTemplates.saveError')) } } // Category CRUD const handleAddCategory = async () => { if (!newCatName.trim() || !expandedId) return try { const data = await adminApi.addTemplateCategory(expandedId, { name: newCatName.trim() }) setCategories(prev => [...prev, data.category]) setNewCatName(''); setAddingCategory(false) } catch { toast.error(t('admin.packingTemplates.saveError')) } } const handleRenameCategory = async (catId: number) => { if (!editCatName.trim() || !expandedId) { setEditingCatId(null); return } try { await adminApi.updateTemplateCategory(expandedId, catId, { name: editCatName.trim() }) setCategories(prev => prev.map(c => c.id === catId ? { ...c, name: editCatName.trim() } : c)) setEditingCatId(null) } catch { toast.error(t('admin.packingTemplates.saveError')) } } const handleDeleteCategory = async (catId: number) => { if (!expandedId) return try { await adminApi.deleteTemplateCategory(expandedId, catId) setCategories(prev => prev.filter(c => c.id !== catId)) setItems(prev => prev.filter(i => i.category_id !== catId)) } catch { toast.error(t('admin.packingTemplates.deleteError')) } } // Item CRUD const handleAddItem = async (catId: number) => { if (!newItemName.trim() || !expandedId) return try { const data = await adminApi.addTemplateItem(expandedId, catId, { name: newItemName.trim() }) setItems(prev => [...prev, data.item]) setNewItemName('') setTimeout(() => addItemRef.current?.focus(), 30) } catch { toast.error(t('admin.packingTemplates.saveError')) } } const handleRenameItem = async (itemId: number) => { if (!editItemName.trim() || !expandedId) { setEditingItemId(null); return } try { await adminApi.updateTemplateItem(expandedId, itemId, { name: editItemName.trim() }) setItems(prev => prev.map(i => i.id === itemId ? { ...i, name: editItemName.trim() } : i)) setEditingItemId(null) } catch { toast.error(t('admin.packingTemplates.saveError')) } } const handleDeleteItem = async (itemId: number) => { if (!expandedId) return try { await adminApi.deleteTemplateItem(expandedId, itemId) setItems(prev => prev.filter(i => i.id !== itemId)) } catch { toast.error(t('admin.packingTemplates.deleteError')) } } const inputStyle = 'w-full px-3 py-2 border border-slate-200 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent outline-none' const btnIcon = 'p-1.5 rounded-lg transition-colors' return (
{/* Header */}

{t('admin.packingTemplates.title')}

{t('admin.packingTemplates.subtitle')}

{/* Create template */} {showCreate && (
setCreateName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleCreateTemplate(); if (e.key === 'Escape') setShowCreate(false) }} placeholder={t('admin.packingTemplates.namePlaceholder')} className={inputStyle} />
)} {/* Template list */} {isLoading ? (
) : templates.length === 0 ? (
{t('admin.packingTemplates.empty')}
) : (
{templates.map(tmpl => (
{/* Template row */}
{editingTemplate === tmpl.id ? ( setEditTemplateName(e.target.value)} onBlur={() => handleRenameTemplate(tmpl.id)} onKeyDown={e => { if (e.key === 'Enter') handleRenameTemplate(tmpl.id); if (e.key === 'Escape') setEditingTemplate(null) }} className="flex-1 px-2 py-0.5 border border-slate-300 rounded text-sm" /> ) : ( toggleExpand(tmpl.id)} className="flex-1 text-sm font-medium text-slate-700 cursor-pointer">{tmpl.name} )} {tmpl.category_count} {t('admin.packingTemplates.categories')} ยท {tmpl.item_count} {t('admin.packingTemplates.items')}
{/* Expanded content */} {expandedId === tmpl.id && (
{categories.map(cat => { const catItems = items.filter(i => i.category_id === cat.id) return (
{/* Category header */}
{editingCatId === cat.id ? ( <> setEditCatName(e.target.value)} onBlur={() => handleRenameCategory(cat.id)} onKeyDown={e => { if (e.key === 'Enter') handleRenameCategory(cat.id); if (e.key === 'Escape') setEditingCatId(null) }} className="flex-1 px-2 py-0.5 border border-slate-300 rounded text-sm font-semibold" /> ) : ( {cat.name} )} {catItems.length}
{/* Items */} {(catItems.length > 0 || addingItemToCatId === cat.id) && (
{catItems.map(item => (
{editingItemId === item.id ? ( <> setEditItemName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleRenameItem(item.id); if (e.key === 'Escape') setEditingItemId(null) }} className="flex-1 px-2 py-1 border border-slate-200 rounded-lg text-sm" /> ) : ( <> {item.name} )}
))} {/* Add item inline */} {addingItemToCatId === cat.id && (
setNewItemName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && newItemName.trim()) handleAddItem(cat.id); if (e.key === 'Escape') { setAddingItemToCatId(null); setNewItemName('') } }} placeholder={t('admin.packingTemplates.itemName')} className="flex-1 px-2 py-1 border border-slate-200 rounded-lg text-sm" />
)}
)}
) })} {/* Add category button */} {addingCategory ? (
setNewCatName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleAddCategory(); if (e.key === 'Escape') { setAddingCategory(false); setNewCatName('') } }} placeholder={t('admin.packingTemplates.categoryName')} className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" />
) : ( )}
)}
))}
)}
) }