import { useState, useEffect, useCallback, useRef } from 'react' import { useAlert } from '../context/AlertContext' import { useAuth } from '../context/AuthContext' import { motion, AnimatePresence } from 'framer-motion' import ConfirmModal from '../components/ConfirmModal' import FormField from '../components/FormField' import Forbidden from '../components/Forbidden' import useModalLock from '../hooks/useModalLock' import apiFetch from '../utils/api' const API_BASE = '/api/admin' const DEFAULT_CUSTOMER_FIELD_ORDER = ['street', 'city_postal', 'country', 'company_id', 'vat_id'] const CUSTOMER_FIELD_LABELS = { street: 'Ulice', city_postal: 'Město + PSČ', country: 'Země', company_id: 'IČO', vat_id: 'DIČ', } export default function OffersCustomers() { const alert = useAlert() const { hasPermission } = useAuth() const [loading, setLoading] = useState(true) const [customers, setCustomers] = useState([]) const [search, setSearch] = useState('') const [showModal, setShowModal] = useState(false) const [editingCustomer, setEditingCustomer] = useState(null) const [saving, setSaving] = useState(false) const [form, setForm] = useState({ name: '', street: '', city: '', postal_code: '', country: '', company_id: '', vat_id: '', }) const [customFields, setCustomFields] = useState([]) const customFieldKeyCounter = useRef(0) const [fieldOrder, setFieldOrder] = useState([...DEFAULT_CUSTOMER_FIELD_ORDER]) const [deleteConfirm, setDeleteConfirm] = useState({ show: false, customer: null }) const [deleting, setDeleting] = useState(false) useModalLock(showModal) // Build the full field order list including custom fields const getFullFieldOrder = useCallback(() => { const allBuiltIn = [...DEFAULT_CUSTOMER_FIELD_ORDER] const order = [...fieldOrder].filter(k => k !== 'name') for (const f of allBuiltIn) { if (!order.includes(f)) order.push(f) } for (let i = 0; i < customFields.length; i++) { const key = `custom_${i}` if (!order.includes(key)) order.push(key) } return order.filter(key => { if (key.startsWith('custom_')) { const idx = parseInt(key.split('_')[1]) return idx < customFields.length } return true }) }, [fieldOrder, customFields]) const moveField = (index, direction) => { const order = getFullFieldOrder() const newIndex = index + direction if (newIndex < 0 || newIndex >= order.length) return const updated = [...order] ;[updated[index], updated[newIndex]] = [updated[newIndex], updated[index]] setFieldOrder(updated) } const getFieldDisplayName = (key) => { if (CUSTOMER_FIELD_LABELS[key]) return CUSTOMER_FIELD_LABELS[key] if (key.startsWith('custom_')) { const idx = parseInt(key.split('_')[1]) const cf = customFields[idx] if (cf) return cf.name ? `${cf.name}: ${cf.value || '...'}` : cf.value || `Vlastní pole ${idx + 1}` } return key } const fetchData = useCallback(async () => { try { const response = await apiFetch(`${API_BASE}/customers.php`) if (response.status === 401) return const result = await response.json() if (result.success) { setCustomers(result.data.customers) } else { alert.error(result.error || 'Nepodařilo se načíst zákazníky') } } catch { alert.error('Chyba připojení') } finally { setLoading(false) } }, [alert]) useEffect(() => { fetchData() }, [fetchData]) const openCreateModal = () => { setEditingCustomer(null) setForm({ name: '', street: '', city: '', postal_code: '', country: '', company_id: '', vat_id: '' }) setCustomFields([]) setFieldOrder([...DEFAULT_CUSTOMER_FIELD_ORDER]) setShowModal(true) } const openEditModal = (customer) => { setEditingCustomer(customer) setForm({ name: customer.name || '', street: customer.street || '', city: customer.city || '', postal_code: customer.postal_code || '', country: customer.country || '', company_id: customer.company_id || '', vat_id: customer.vat_id || '', }) // Load custom fields const cf = Array.isArray(customer.custom_fields) && customer.custom_fields.length > 0 ? customer.custom_fields.map(f => ({ ...f, _key: `cf-${++customFieldKeyCounter.current}` })) : [] setCustomFields(cf) // Load field order if (Array.isArray(customer.customer_field_order) && customer.customer_field_order.length > 0) { setFieldOrder(customer.customer_field_order) } else { setFieldOrder([...DEFAULT_CUSTOMER_FIELD_ORDER]) } setShowModal(true) } const closeModal = () => { setShowModal(false) setEditingCustomer(null) } const handleSubmit = async () => { if (!form.name.trim()) { alert.error('Název zákazníka je povinný') return } setSaving(true) try { const url = editingCustomer ? `${API_BASE}/customers.php?id=${editingCustomer.id}` : `${API_BASE}/customers.php` const payload = { ...form, custom_fields: customFields.filter(f => f.name.trim() || f.value.trim()), customer_field_order: getFullFieldOrder(), } const response = await apiFetch(url, { method: editingCustomer ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) const result = await response.json() if (result.success) { closeModal() await new Promise(resolve => setTimeout(resolve, 300)) alert.success(result.message || (editingCustomer ? 'Zákazník byl aktualizován' : 'Zákazník byl vytvořen')) fetchData() } else { alert.error(result.error || 'Nepodařilo se uložit zákazníka') } } catch { alert.error('Chyba připojení') } finally { setSaving(false) } } const handleDelete = async () => { if (!deleteConfirm.customer) return setDeleting(true) try { const response = await apiFetch(`${API_BASE}/customers.php?id=${deleteConfirm.customer.id}`, { method: 'DELETE' }) const result = await response.json() if (result.success) { setDeleteConfirm({ show: false, customer: null }) alert.success(result.message || 'Zákazník byl smazán') fetchData() } else { alert.error(result.error || 'Nepodařilo se smazat zákazníka') } } catch { alert.error('Chyba připojení') } finally { setDeleting(false) } } if (!hasPermission('offers.view')) return const filteredCustomers = search ? customers.filter(c => (c.name || '').toLowerCase().includes(search.toLowerCase()) || (c.company_id || '').includes(search) || (c.city || '').toLowerCase().includes(search.toLowerCase()) ) : customers if (loading) { return (
{[0, 1, 2, 3, 4].map(i => (
))}
) } const fullFieldOrder = getFullFieldOrder() return (

Zákazníci

Správa zákazníků pro nabídky

{hasPermission('offers.create') && ( )}
setSearch(e.target.value)} className="admin-form-input" placeholder="Hledat zákazníky..." />
{filteredCustomers.length === 0 ? (

{search ? 'Žádní zákazníci odpovídající hledání.' : 'Zatím nejsou žádní zákazníci.'}

{!search && hasPermission('offers.create') && ( )}
) : (
{filteredCustomers.map((customer) => ( ))}
Název Město IČO DIČ Nabídky Akce
{customer.name}
{customer.street && (
{customer.street}
)}
{customer.city || '—'} {customer.company_id || '—'} {customer.vat_id || '—'} {customer.quotation_count || 0}
{hasPermission('offers.edit') && ( )} {hasPermission('offers.delete') && ( )}
)}
{/* Create/Edit Modal */} {showModal && (

{editingCustomer ? 'Upravit zákazníka' : 'Nový zákazník'}

setForm(prev => ({ ...prev, name: e.target.value }))} className="admin-form-input" placeholder="Název firmy / jméno" /> setForm(prev => ({ ...prev, street: e.target.value }))} className="admin-form-input" />
setForm(prev => ({ ...prev, city: e.target.value }))} className="admin-form-input" /> setForm(prev => ({ ...prev, postal_code: e.target.value }))} className="admin-form-input" />
setForm(prev => ({ ...prev, country: e.target.value }))} className="admin-form-input" />
setForm(prev => ({ ...prev, company_id: e.target.value }))} className="admin-form-input" /> setForm(prev => ({ ...prev, vat_id: e.target.value }))} className="admin-form-input" />
{/* Dynamic custom fields */}
{customFields.map((field, idx) => (
{ const updated = [...customFields] updated[idx] = { ...updated[idx], name: e.target.value } setCustomFields(updated) }} className="admin-form-input" placeholder="Např. Kontakt" />
{ const updated = [...customFields] updated[idx] = { ...updated[idx], value: e.target.value } setCustomFields(updated) }} className="admin-form-input" style={{ flex: 1 }} />
))}
{/* Field order for PDF */}
Určuje pořadí řádků v adresním bloku zákazníka na PDF nabídce.
{fullFieldOrder.map((key, index) => (
{getFieldDisplayName(key)}
))}
)} {/* Delete Confirm Modal */} setDeleteConfirm({ show: false, customer: null })} onConfirm={handleDelete} title="Smazat zákazníka" message={`Opravdu chcete smazat zákazníka "${deleteConfirm.customer?.name}"? Tato akce je nevratná.`} confirmText="Smazat" cancelText="Zrušit" type="danger" loading={deleting} />
) }