import { useState } from 'react' import { useAlert } from '../context/AlertContext' import { useAuth } from '../context/AuthContext' import { useParams, useNavigate, Link } from 'react-router-dom' import { motion, AnimatePresence } from 'framer-motion' import ConfirmModal from '../components/ConfirmModal' import FormField from '../components/FormField' import Forbidden from '../components/Forbidden' import AdminDatePicker from '../components/AdminDatePicker' import OfferItemsSection from '../components/OfferItemsSection' import OfferScopeSection from '../components/OfferScopeSection' import OfferCustomerPicker from '../components/OfferCustomerPicker' import useModalLock from '../hooks/useModalLock' import useOfferForm from '../hooks/useOfferForm' import apiFetch from '../utils/api' const API_BASE = '/api/admin' export default function OfferDetail() { const { id } = useParams() const isEdit = Boolean(id) const alert = useAlert() const { hasPermission } = useAuth() const navigate = useNavigate() const { loading, saving, errors, setErrors, form, updateForm, items, setItems, sections, customers, itemTemplates, scopeTemplates, orderInfo, offerStatus, setOfferStatus, totals, draftSavedAtLabel, selectCustomer, clearCustomer, updateItem, addItem, removeItem, addItemFromTemplate, addSection, removeSection, updateSection, moveSection, loadScopeTemplate, handleSave } = useOfferForm({ id, isEdit, alert, navigate }) const [showItemTemplateMenu, setShowItemTemplateMenu] = useState(false) const [showScopeTemplateMenu, setShowScopeTemplateMenu] = useState(false) const [deleteConfirm, setDeleteConfirm] = useState(false) const [deleting, setDeleting] = useState(false) const [creatingOrder, setCreatingOrder] = useState(false) const [showOrderModal, setShowOrderModal] = useState(false) const [invalidateConfirm, setInvalidateConfirm] = useState(false) const [invalidatingOffer, setInvalidatingOffer] = useState(false) const [customerOrderNumber, setCustomerOrderNumber] = useState('') const [orderAttachment, setOrderAttachment] = useState(null) const [pdfLoading, setPdfLoading] = useState(false) useModalLock(showOrderModal) const isInvalidated = offerStatus === 'invalidated' const isExpiredNotInvalidated = isEdit && !isInvalidated && !orderInfo && form.valid_until && new Date(form.valid_until) < new Date(new Date().toDateString()) const handleCreateOrder = async () => { if (!customerOrderNumber.trim()) { alert.error('Číslo objednávky zákazníka je povinné') return } setCreatingOrder(true) try { const formData = new FormData() formData.append('quotationId', id) formData.append('customerOrderNumber', customerOrderNumber.trim()) if (orderAttachment) { formData.append('attachment', orderAttachment) } const response = await apiFetch(`${API_BASE}/orders.php`, { method: 'POST', body: formData }) const result = await response.json() if (result.success) { setShowOrderModal(false) alert.success(result.message || 'Objednávka byla vytvořena') navigate(`/orders/${result.data.order_id}`) } else { alert.error(result.error || 'Nepodařilo se vytvořit objednávku') } } catch { alert.error('Chyba připojení') } finally { setCreatingOrder(false) } } const handleInvalidateOffer = async () => { setInvalidatingOffer(true) try { const response = await apiFetch(`${API_BASE}/offers.php?action=invalidate&id=${id}`, { method: 'POST' }) const result = await response.json() if (result.success) { setInvalidateConfirm(false) setOfferStatus('invalidated') alert.success(result.message || 'Nabídka byla zneplatněna') } else { alert.error(result.error || 'Nepodařilo se zneplatnit nabídku') } } catch { alert.error('Chyba připojení') } finally { setInvalidatingOffer(false) } } const handleDelete = async () => { setDeleting(true) try { const response = await apiFetch(`${API_BASE}/offers.php?id=${id}`, { method: 'DELETE' }) const result = await response.json() if (result.success) { alert.success(result.message || 'Nabídka byla smazána') navigate('/offers') } else { alert.error(result.error || 'Nepodařilo se smazat nabídku') } } catch { alert.error('Chyba připojení') } finally { setDeleting(false) setDeleteConfirm(false) } } const handlePdf = async () => { if (!isEdit || pdfLoading) return setPdfLoading(true) try { const response = await apiFetch(`${API_BASE}/offers-pdf.php?id=${id}`) if (response.status === 401) return if (!response.ok) { alert.error('Nepodařilo se vygenerovat PDF') return } const html = await response.text() const w = window.open('', '_blank') if (w) { w.document.open() w.document.write(html) w.document.close() w.onload = () => w.print() } else { alert.error('Prohlížeč zablokoval vyskakovací okno') } } catch { alert.error('Chyba při generování PDF') } finally { setPdfLoading(false) } } const getRequiredPerm = () => { if (!isEdit) return 'offers.create' return isInvalidated ? 'offers.view' : 'offers.edit' } const requiredPerm = getRequiredPerm() if (!hasPermission(requiredPerm)) return if (loading) { return (
{[0, 1, 2, 3].map(i => (
))}
{[0, 1, 2].map(i => (
))}
{[0, 1, 2].map(i => (
))}
) } return (
{/* Header */}

{isEdit ? `Nabídka ${form.quotation_number}` : 'Nová nabídka'} {isInvalidated && ( Zneplatněna )}

{!isEdit && draftSavedAtLabel && (
Koncept uložen {draftSavedAtLabel}
)}
{isEdit && hasPermission('offers.export') && ( )} {isEdit && !isInvalidated && hasPermission('orders.create') && !orderInfo && ( )} {isEdit && orderInfo && ( Objednávka {orderInfo.order_number} )} {isExpiredNotInvalidated && hasPermission('offers.edit') && ( )} {!isInvalidated && ( )} {isEdit && hasPermission('offers.delete') && ( )}
{/* Quotation Form */}

Základní údaje

updateForm('project_code', e.target.value)} className="admin-form-input" placeholder="Volitelný kód projektu" readOnly={isInvalidated} />
{isInvalidated ? ( ) : ( { updateForm('created_at', val) setErrors(prev => ({ ...prev, created_at: undefined })) }} /> )} {isInvalidated ? ( ) : ( { updateForm('valid_until', val) setErrors(prev => ({ ...prev, valid_until: undefined })) }} /> )}
updateForm('vat_rate', parseFloat(e.target.value) || 0)} className="admin-form-input" step="0.1" style={{ flex: 1 }} readOnly={isInvalidated} />
updateForm('exchange_rate', e.target.value)} className="admin-form-input" placeholder="Volitelný" step="0.0001" readOnly={isInvalidated} />
{showOrderModal && (
!creatingOrder && setShowOrderModal(false)} />

Vytvořit objednávku

setCustomerOrderNumber(e.target.value)} onKeyDown={e => e.key === 'Enter' && !creatingOrder && handleCreateOrder()} className="admin-form-input" placeholder="Např. PO-2026-001" autoFocus /> {orderAttachment ? (
{orderAttachment.name} ({(orderAttachment.size / 1024).toFixed(0)} KB)
) : ( )} Max 10 MB
)} setInvalidateConfirm(false)} onConfirm={handleInvalidateOffer} title="Zneplatnit nabídku" message={`Opravdu chcete zneplatnit nabídku "${form.quotation_number}"? Nabídka bude pouze pro čtení a nepůjde upravovat.`} confirmText="Zneplatnit" cancelText="Zrušit" type="danger" loading={invalidatingOffer} /> setDeleteConfirm(false)} onConfirm={handleDelete} title="Smazat nabídku" message={`Opravdu chcete smazat nabídku "${form.quotation_number}"? Budou smazány i všechny položky a sekce. Tato akce je nevratná.`} confirmText="Smazat" cancelText="Zrušit" type="danger" loading={deleting} />
) }