feat(admin): new AdminBusinessEditor with live mobile preview + fix admin logout bug on file picker timeout

This commit is contained in:
2026-03-04 14:15:59 -05:00
parent 0a6a0e8f7e
commit d17955383a
4 changed files with 663 additions and 220 deletions

View File

@ -24,11 +24,7 @@ const categories = ['Todas', 'Restaurante', 'Turismo', 'Bebidas', 'Comercio']
// Modals
const showModal = ref(false)
const showBusinessModal = ref(false)
const isEditing = ref(false)
const isEditingBusiness = ref(false)
const businessImageFile = ref<File | null>(null)
const businessImagePreview = ref<string | null>(null)
// Current data
@ -47,24 +43,6 @@ const currentCoupon = ref<Partial<Coupon>>({
is_active: true
})
const currentBusiness = ref<Partial<Business>>({
name: '',
address: '',
phone: '',
image_url: '',
social_media: '',
category: 'Restaurante',
area: 'Boquete',
description: '',
website: '',
// Template fields
schedule: '',
whatsapp: '',
instagram: '',
facebook: '',
gallery_images: []
})
const userName = localStorage.getItem('user_name') || 'Promotor'
onMounted(async () => {
@ -174,82 +152,15 @@ async function deleteShuttle(id: string) {
}
// Business Methods
import { useRouter } from 'vue-router'
const localRouter = useRouter()
function openCreateBusinessModal() {
isEditingBusiness.value = false
currentBusiness.value = {
name: '',
address: '',
phone: '',
image_url: '',
social_media: '',
category: 'Restaurante',
area: 'Boquete',
description: '',
website: '',
schedule: '',
whatsapp: '',
instagram: '',
facebook: '',
gallery_images: []
}
showBusinessModal.value = true
businessImageFile.value = null
businessImagePreview.value = null
}
function handleBusinessImage(event: any) {
const file = event.target.files[0]
if (file) {
businessImageFile.value = file
businessImagePreview.value = URL.createObjectURL(file)
}
localRouter.push('/admin/business-edit/new')
}
function openEditBusinessModal(biz: Business) {
isEditingBusiness.value = true
currentBusiness.value = { ...biz }
showBusinessModal.value = true
businessImageFile.value = null
businessImagePreview.value = getImageUrl(biz.image_url)
}
async function saveBusiness() {
try {
const formData = new FormData()
formData.append('name', currentBusiness.value.name || '')
formData.append('category', currentBusiness.value.category || 'Restaurante')
formData.append('address', currentBusiness.value.address || '')
formData.append('phone', currentBusiness.value.phone || '')
formData.append('social_media', currentBusiness.value.social_media || '')
formData.append('area', currentBusiness.value.area || 'Boquete')
formData.append('description', currentBusiness.value.description || '')
formData.append('website', currentBusiness.value.website || '')
// Template fields
formData.append('schedule', currentBusiness.value.schedule || '')
formData.append('whatsapp', currentBusiness.value.whatsapp || '')
formData.append('instagram', currentBusiness.value.instagram || '')
formData.append('facebook', currentBusiness.value.facebook || '')
// gallery_images handled separately via JSON
if (currentBusiness.value.gallery_images?.length) {
formData.append('gallery_images', JSON.stringify(currentBusiness.value.gallery_images))
}
if (businessImageFile.value) {
formData.append('image', businessImageFile.value)
}
if (isEditingBusiness.value && currentBusiness.value.id) {
await businessService.updateBusiness(currentBusiness.value.id, formData)
} else {
await businessService.createBusiness(formData)
}
showBusinessModal.value = false
await loadBusinesses()
} catch (e) {
console.error('Error saving business:', e)
alert('Error al guardar el negocio')
}
localRouter.push(`/admin/business-edit/${biz.id}`)
}
async function deleteBusiness(id: string) {
@ -704,125 +615,6 @@ async function toggleCouponStatus(coupon: Coupon) {
</div>
</div>
<!-- Business Modal -->
<div v-if="showBusinessModal" class="modal-overlay" @click.self="showBusinessModal = false">
<div class="modal-content">
<div class="modal-header">
<h2>{{ isEditingBusiness ? 'Editar Negocio' : 'Registrar Negocio' }}</h2>
<button class="close-btn" @click="showBusinessModal = false"><span class="material-icons">close</span></button>
</div>
<form @submit.prevent="saveBusiness" class="coupon-form">
<!-- Info básica -->
<div class="form-section-label">📋 Información Básica</div>
<div class="form-group">
<label>Nombre del Negocio *</label>
<input v-model="currentBusiness.name" type="text" required placeholder="Ej: Restaurante La Casona">
</div>
<div class="form-row">
<div class="form-group">
<label>Categoría</label>
<select v-model="currentBusiness.category">
<option value="Restaurante">Restaurante</option>
<option value="Hotel">Hotel</option>
<option value="Café">Café</option>
<option value="Bebidas">Bar / Bebidas</option>
<option value="Comercio">Comercio</option>
<option value="Turismo">Turismo</option>
</select>
</div>
<div class="form-group">
<label>Área / Región</label>
<select v-model="currentBusiness.area">
<option value="Boquete">Boquete</option>
<option value="Alto Boquete">Alto Boquete</option>
<option value="Dolega">Dolega</option>
<option value="David">David</option>
<option value="Caldera">Caldera</option>
<option value="Chiriquí">Chiriquí</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Teléfono</label>
<input v-model="currentBusiness.phone" type="text" placeholder="+507 6000-0000">
</div>
<div class="form-group">
<label>🕐 Horario de Atención</label>
<input v-model="currentBusiness.schedule" type="text" placeholder="Ej: Lun-Sáb 8am-10pm">
</div>
</div>
<div class="form-group">
<label>Dirección Física</label>
<input v-model="currentBusiness.address" type="text" placeholder="Ej: Calle Principal #123, Frente al Parque">
</div>
<!-- Imagen principal -->
<div class="form-section-label">🖼 Imagen de Portada</div>
<div class="form-group">
<label>Foto Principal (Logo o Fachada)</label>
<div class="file-upload-wrapper">
<input type="file" @change="handleBusinessImage" accept="image/*" class="file-input">
<div class="file-preview" v-if="businessImagePreview">
<img :src="businessImagePreview" alt="Vista previa">
</div>
</div>
</div>
<!-- Descripción -->
<div class="form-section-label">📝 Descripción</div>
<div class="form-group">
<label>Sobre el Negocio (Texto de marketing)</label>
<textarea v-model="currentBusiness.description" placeholder="Describe la experiencia del lugar, su especialidad y ambiente para atraer clientes..." rows="4"></textarea>
</div>
<!-- Redes Sociales y Contacto -->
<div class="form-section-label">🌐 Contacto y Redes Sociales</div>
<div class="form-row">
<div class="form-group">
<label>💬 WhatsApp</label>
<input v-model="currentBusiness.whatsapp" type="text" placeholder="50760000000 (con código de país)">
</div>
<div class="form-group">
<label>📸 Instagram</label>
<input v-model="currentBusiness.instagram" type="text" placeholder="@usuario">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>👍 Facebook</label>
<input v-model="currentBusiness.facebook" type="text" placeholder="/nombre-de-pagina">
</div>
<div class="form-group">
<label>🌐 Página Web</label>
<input v-model="currentBusiness.website" type="url" placeholder="https://www.ejemplo.com">
</div>
</div>
<!-- Galería -->
<div class="form-section-label">📸 Galería de Imágenes (Carrusel)</div>
<div class="form-group">
<label>URLs de fotos adicionales (una por línea)</label>
<textarea
:value="currentBusiness.gallery_images?.join('\n') || ''"
@input="(e: any) => currentBusiness.gallery_images = e.target.value.split('\n').map((s: string) => s.trim()).filter((s: string) => s.length > 0)"
placeholder="https://url-foto1.jpg&#10;https://url-foto2.jpg&#10;https://url-foto3.jpg"
rows="3"
></textarea>
<small class="form-hint">Agrega URLs de imágenes para el carrusel (menú, ambiente, experiencias). Una URL por línea.</small>
</div>
<div class="form-actions">
<button type="submit" class="submit-btn">
<span class="material-icons">{{ isEditingBusiness ? 'save' : 'store' }}</span>
{{ isEditingBusiness ? 'Guardar Cambios' : 'Publicar Negocio' }}
</button>
</div>
</form>
</div>
</div>
</div>
</template>