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 RichEditor from '../components/RichEditor'
import useModalLock from '../hooks/useModalLock'
import apiFetch from '../utils/api'
const API_BASE = '/api/admin'
export default function OffersTemplates() {
const { hasPermission } = useAuth()
const [activeTab, setActiveTab] = useState('items')
if (!hasPermission('offers.settings')) return
return (
Šablony
Šablony položek a rozsahu projektu
{activeTab === 'items' ?
:
}
)
}
// --- Item Templates Tab ---
function ItemTemplatesTab() {
const alert = useAlert()
const [loading, setLoading] = useState(true)
const [templates, setTemplates] = useState([])
const [showModal, setShowModal] = useState(false)
const [editingTemplate, setEditingTemplate] = useState(null)
const [saving, setSaving] = useState(false)
const [form, setForm] = useState({ name: '', description: '', default_price: 0, category: '' })
const [deleteConfirm, setDeleteConfirm] = useState({ show: false, template: null })
const [deleting, setDeleting] = useState(false)
useModalLock(showModal)
const fetchData = useCallback(async () => {
try {
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=items`)
if (response.status === 401) return
const result = await response.json()
if (result.success) {
setTemplates(result.data.templates)
}
} catch {
alert.error('Nepodařilo se načíst šablony')
} finally {
setLoading(false)
}
}, [alert])
useEffect(() => { fetchData() }, [fetchData])
const openCreate = () => {
setEditingTemplate(null)
setForm({ name: '', description: '', default_price: 0, category: '' })
setShowModal(true)
}
const openEdit = (t) => {
setEditingTemplate(t)
setForm({ name: t.name || '', description: t.description || '', default_price: t.default_price || 0, category: t.category || '' })
setShowModal(true)
}
const handleSubmit = async () => {
if (!form.name.trim()) {
alert.error('Název šablony je povinný')
return
}
setSaving(true)
try {
const body = editingTemplate ? { ...form, id: editingTemplate.id } : form
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=item`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
const result = await response.json()
if (result.success) {
setShowModal(false)
await new Promise(r => setTimeout(r, 300))
alert.success(result.message)
fetchData()
} else {
alert.error(result.error)
}
} catch {
alert.error('Chyba připojení')
} finally {
setSaving(false)
}
}
const handleDelete = async () => {
if (!deleteConfirm.template) return
setDeleting(true)
try {
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=item&id=${deleteConfirm.template.id}`, { method: 'DELETE' })
const result = await response.json()
if (result.success) {
setDeleteConfirm({ show: false, template: null })
alert.success(result.message)
fetchData()
} else {
alert.error(result.error)
}
} catch {
alert.error('Chyba připojení')
} finally {
setDeleting(false)
}
}
if (loading) {
return (
{[0, 1, 2, 3, 4].map(i => (
))}
)
}
return (
<>
Šablony položek ({templates.length})
{templates.length === 0 ? (
Zatím žádné šablony položek.
) : (
| Název |
Popis |
Cena |
Kategorie |
Akce |
{templates.map((t) => (
| {t.name} |
{t.description || '—'} |
{Number(t.default_price).toFixed(2)} |
{t.category || '—'} |
|
))}
)}
{/* Item Template Modal */}
{showModal && (
setShowModal(false)} />
{editingTemplate ? 'Upravit šablonu' : 'Nová šablona položky'}
)}
setDeleteConfirm({ show: false, template: null })}
onConfirm={handleDelete}
title="Smazat šablonu"
message={`Opravdu chcete smazat šablonu "${deleteConfirm.template?.name}"?`}
confirmText="Smazat"
cancelText="Zrušit"
type="danger"
loading={deleting}
/>
>
)
}
// --- Scope Templates Tab ---
function ScopeTemplatesTab() {
const alert = useAlert()
const [loading, setLoading] = useState(true)
const [templates, setTemplates] = useState([])
const [showModal, setShowModal] = useState(false)
const [editingTemplate, setEditingTemplate] = useState(null)
const [saving, setSaving] = useState(false)
const [form, setForm] = useState({ name: '', sections: [] })
const sectionKeyCounter = useRef(0)
const [deleteConfirm, setDeleteConfirm] = useState({ show: false, template: null })
const [deleting, setDeleting] = useState(false)
useModalLock(showModal)
const fetchData = useCallback(async () => {
try {
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=scopes`)
if (response.status === 401) return
const result = await response.json()
if (result.success) {
setTemplates(result.data.templates)
}
} catch {
alert.error('Nepodařilo se načíst šablony')
} finally {
setLoading(false)
}
}, [alert])
useEffect(() => { fetchData() }, [fetchData])
const openCreate = () => {
setEditingTemplate(null)
setForm({ name: '', sections: [{ _key: `sc-${++sectionKeyCounter.current}`, title: '', title_cz: '', content: '' }] })
setShowModal(true)
}
const openEdit = async (t) => {
try {
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=scope_detail&id=${t.id}`)
const result = await response.json()
if (result.success) {
setEditingTemplate(result.data)
setForm({
name: result.data.name || '',
sections: result.data.sections?.length
? result.data.sections.map(s => ({ _key: `sc-${++sectionKeyCounter.current}`, title: s.title || '', title_cz: s.title_cz || '', content: s.content || '' }))
: [{ _key: `sc-${++sectionKeyCounter.current}`, title: '', title_cz: '', content: '' }]
})
setShowModal(true)
}
} catch {
alert.error('Nepodařilo se načíst detail šablony')
}
}
const addSection = () => {
setForm(prev => ({ ...prev, sections: [...prev.sections, { _key: `sc-${++sectionKeyCounter.current}`, title: '', title_cz: '', content: '' }] }))
}
const removeSection = (index) => {
setForm(prev => ({
...prev,
sections: prev.sections.filter((_, i) => i !== index)
}))
}
const updateSection = (index, field, value) => {
setForm(prev => ({
...prev,
sections: prev.sections.map((s, i) => i === index ? { ...s, [field]: value } : s)
}))
}
const moveSection = (index, direction) => {
setForm(prev => {
const newSections = [...prev.sections]
const targetIndex = index + direction
if (targetIndex < 0 || targetIndex >= newSections.length) return prev
;[newSections[index], newSections[targetIndex]] = [newSections[targetIndex], newSections[index]]
return { ...prev, sections: newSections }
})
}
const handleSubmit = async () => {
if (!form.name.trim()) {
alert.error('Název šablony je povinný')
return
}
setSaving(true)
try {
const body = editingTemplate ? { ...form, id: editingTemplate.id } : form
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=scope`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
const result = await response.json()
if (result.success) {
setShowModal(false)
await new Promise(r => setTimeout(r, 300))
alert.success(result.message)
fetchData()
} else {
alert.error(result.error)
}
} catch {
alert.error('Chyba připojení')
} finally {
setSaving(false)
}
}
const handleDelete = async () => {
if (!deleteConfirm.template) return
setDeleting(true)
try {
const response = await apiFetch(`${API_BASE}/offers-templates.php?action=scope&id=${deleteConfirm.template.id}`, { method: 'DELETE' })
const result = await response.json()
if (result.success) {
setDeleteConfirm({ show: false, template: null })
alert.success(result.message)
fetchData()
} else {
alert.error(result.error)
}
} catch {
alert.error('Chyba připojení')
} finally {
setDeleting(false)
}
}
if (loading) {
return (
{[0, 1, 2, 3, 4].map(i => (
))}
)
}
return (
<>
Šablony rozsahu ({templates.length})
{templates.length === 0 ? (
Zatím žádné šablony rozsahu.
) : (
| Název |
Akce |
{templates.map((t) => (
| {t.name} |
|
))}
)}
{/* Scope Template Modal (large) */}
{showModal && (
setShowModal(false)} />
{editingTemplate ? 'Upravit šablonu rozsahu' : 'Nová šablona rozsahu'}
setForm(p => ({ ...p, name: e.target.value }))} className="admin-form-input" />
{form.sections.map((section, index) => (
{index + 1}.
{section.title || section.title_cz || `Sekce ${index + 1}`}
{form.sections.length > 1 && (
)}
))}
)}
setDeleteConfirm({ show: false, template: null })}
onConfirm={handleDelete}
title="Smazat šablonu"
message={`Opravdu chcete smazat šablonu "${deleteConfirm.template?.name}"?`}
confirmText="Smazat"
cancelText="Zrušit"
type="danger"
loading={deleting}
/>
>
)
}