Replace browser prompt()-based editing with proper Bootstrap 5 modal dialogs for testimonials, services, FAQs, and leads. This provides better UX with form validation, structured input fields, and i18n support (ES/RU) instead of raw prompt dialogs. - Add testimonialModal, serviceModal, faqModal, leadModal to admin.html - Add show*/save* methods in admin.js for each entity type - Wire leads.html 'Add lead' button to leadModal - Add modal JS modules (FAQModal, LeadModal, ServiceModal) - Add unit and e2e tests for modals and API client
116 lines
4.8 KiB
JavaScript
116 lines
4.8 KiB
JavaScript
export class FAQModal {
|
||
constructor(app) {
|
||
this.app = app
|
||
this.id = 'faqModal'
|
||
this.element = null
|
||
this.bsModal = null
|
||
this._editId = null
|
||
}
|
||
|
||
open(faq = null) {
|
||
if (!this.element) this._build()
|
||
this._editId = faq ? faq.id : null
|
||
const title = this.element.querySelector('.modal-title')
|
||
const inputs = this.element.querySelectorAll('input[name], textarea[name], select[name]')
|
||
if (faq) {
|
||
title.innerHTML = '<i class="bi bi-pencil me-2"></i>Editar pregunta'
|
||
inputs.forEach(i => {
|
||
const key = i.getAttribute('name')
|
||
if (faq[key] !== undefined) i.value = faq[key]
|
||
if (i.type === 'checkbox') i.checked = faq[key] !== false
|
||
})
|
||
} else {
|
||
title.innerHTML = '<i class="bi bi-question-circle me-2"></i>Añadir pregunta'
|
||
inputs.forEach(i => { i.value = ''; if (i.type === 'checkbox') i.checked = true })
|
||
this.element.querySelector('select[name="category"]').value = 'general'
|
||
this.element.querySelector('input[name="order_num"]').value = ''
|
||
}
|
||
this.bsModal = new bootstrap.Modal(this.element)
|
||
this.bsModal.show()
|
||
}
|
||
|
||
close() {
|
||
if (this.bsModal) this.bsModal.hide()
|
||
}
|
||
|
||
async save() {
|
||
const data = this._collect()
|
||
if (!data.question_es || !data.question_es.trim()) {
|
||
this.app.showNotification('La pregunta es obligatoria', 'error')
|
||
return
|
||
}
|
||
if (!data.answer_es || !data.answer_es.trim()) {
|
||
this.app.showNotification('La respuesta es obligatoria', 'error')
|
||
return
|
||
}
|
||
const res = this._editId
|
||
? await API.updateFAQ(this._editId, data)
|
||
: await API.createFAQ(data)
|
||
if (res.success) {
|
||
this.close()
|
||
this.app.showNotification(this._editId ? 'FAQ actualizado' : 'FAQ creado', 'success')
|
||
this.app.refreshSection('faq')
|
||
} else {
|
||
this.app.showNotification(res.error || 'Error al guardar', 'error')
|
||
}
|
||
}
|
||
|
||
_collect() {
|
||
const d = {}
|
||
this.element.querySelectorAll('input[name], textarea[name], select[name]').forEach(i => {
|
||
const k = i.getAttribute('name')
|
||
d[k] = i.type === 'checkbox' ? i.checked : i.value
|
||
})
|
||
d.order_num = parseInt(d.order_num) || 0
|
||
d.is_active = d.is_active !== undefined ? d.is_active : true
|
||
return d
|
||
}
|
||
|
||
_build() {
|
||
const el = document.createElement('div')
|
||
el.id = this.id
|
||
el.className = 'modal fade'
|
||
el.innerHTML = `
|
||
<div class="modal-dialog modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><i class="bi bi-question-circle me-2"></i>Añadir pregunta</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-3"><label class="form-label">Pregunta (ES)</label>
|
||
<input type="text" class="form-control" name="question_es" placeholder="¿Puedo comprar terreno...?"></div>
|
||
<div class="mb-3"><label class="form-label">Pregunta (RU)</label>
|
||
<input type="text" class="form-control" name="question_ru" placeholder="Могу ли я купить землю...?"></div>
|
||
<div class="mb-3"><label class="form-label">Respuesta (ES)</label>
|
||
<textarea class="form-control" rows="3" name="answer_es" placeholder="Respuesta detallada..."></textarea></div>
|
||
<div class="mb-3"><label class="form-label">Respuesta (RU)</label>
|
||
<textarea class="form-control" rows="3" name="answer_ru" placeholder="Подробный ответ..."></textarea></div>
|
||
<div class="mb-3"><label class="form-label">Categoría</label>
|
||
<select class="form-select" name="category">
|
||
<option value="general">General</option>
|
||
<option value="legal">Legal</option>
|
||
<option value="buying">Compra</option>
|
||
<option value="taxes">Impuestos</option>
|
||
<option value="property">Propiedad</option>
|
||
</select></div>
|
||
<div class="mb-3"><label class="form-label">Orden</label>
|
||
<input type="number" class="form-control" name="order_num" placeholder="0"></div>
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" name="is_active" id="faqActive" checked>
|
||
<label class="form-check-label" for="faqActive">Activo</label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||
<button type="button" class="btn btn-primary" id="faqSaveBtn">
|
||
<i class="bi bi-check-lg me-2"></i>Guardar pregunta</button>
|
||
</div>
|
||
</div>
|
||
</div>`
|
||
document.body.appendChild(el)
|
||
el.querySelector('#faqSaveBtn').addEventListener('click', () => this.save())
|
||
this.element = el
|
||
}
|
||
}
|