feat: business template/mold redesign
- Supabase: Add schedule, whatsapp, instagram, facebook, gallery_images columns to businesses table - types/index.ts: Add 5 new fields to Business interface - businessService.ts: Update all SELECT queries to include new fields, add _parseFormData helper, handle gallery_images as JSON array - BusinessDetailsView.vue: Full redesign matching the approved mockup: * Hero 300px with centered name + yellow category badge * Horizontal scrollable quick-info pills (area, schedule, phone, web) * Image carousel (main image + gallery_images) with arrows + dots * About section with yellow left-accent bar * 2x2 social grid (WhatsApp, Instagram, Facebook, Maps) with brand colors * Coupon section preserved * Sticky CTA bar: Ver en el Mapa + Llamar buttons - PromoterDashboard.vue: Updated business modal with grouped sections (Info Basica / Portada / Descripcion / Redes / Galeria) + new fields
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -56,7 +56,13 @@ const currentBusiness = ref<Partial<Business>>({
|
||||
category: 'Restaurante',
|
||||
area: 'Boquete',
|
||||
description: '',
|
||||
website: ''
|
||||
website: '',
|
||||
// Template fields
|
||||
schedule: '',
|
||||
whatsapp: '',
|
||||
instagram: '',
|
||||
facebook: '',
|
||||
gallery_images: []
|
||||
})
|
||||
|
||||
const userName = localStorage.getItem('user_name') || 'Promotor'
|
||||
@ -179,7 +185,12 @@ function openCreateBusinessModal() {
|
||||
category: 'Restaurante',
|
||||
area: 'Boquete',
|
||||
description: '',
|
||||
website: ''
|
||||
website: '',
|
||||
schedule: '',
|
||||
whatsapp: '',
|
||||
instagram: '',
|
||||
facebook: '',
|
||||
gallery_images: []
|
||||
}
|
||||
showBusinessModal.value = true
|
||||
businessImageFile.value = null
|
||||
@ -214,7 +225,16 @@ async function saveBusiness() {
|
||||
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)
|
||||
}
|
||||
@ -692,39 +712,57 @@ async function toggleCouponStatus(coupon: Coupon) {
|
||||
<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>
|
||||
<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="Area Turistica">Área Turística</option>
|
||||
<option value="Hotel">Hotel</option>
|
||||
<option value="Café">Café</option>
|
||||
<option value="Bebidas">Bar / Bebidas</option>
|
||||
<option value="Viajes de Turismo">Viajes de Turismo</option>
|
||||
<option value="Comercio">Comercio</option>
|
||||
<option value="Turismo">Turismo</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Teléfono</label>
|
||||
<input v-model="currentBusiness.phone" type="text">
|
||||
</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>Imagen del Negocio (Logo o Fachada)</label>
|
||||
<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">
|
||||
@ -732,20 +770,55 @@ async function toggleCouponStatus(coupon: Coupon) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Descripción ── -->
|
||||
<div class="form-section-label">📝 Descripción</div>
|
||||
<div class="form-group">
|
||||
<label>Descripción / Historia del Negocio</label>
|
||||
<textarea v-model="currentBusiness.description" placeholder="Cuéntanos un poco sobre el negocio para darle un toque premium..." rows="3"></textarea>
|
||||
<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>Página Web (Opcional)</label>
|
||||
<input v-model="currentBusiness.website" type="url" placeholder="https://www.ejemplo.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Redes Sociales</label>
|
||||
<input v-model="currentBusiness.social_media" type="text" placeholder="Ej: @pizzeria_centro">
|
||||
<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 https://url-foto2.jpg 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">Guardar Negocio</button>
|
||||
<button type="submit" class="submit-btn">
|
||||
<span class="material-icons">{{ isEditingBusiness ? 'save' : 'store' }}</span>
|
||||
{{ isEditingBusiness ? 'Guardar Cambios' : 'Publicar Negocio' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -1182,6 +1255,35 @@ async function toggleCouponStatus(coupon: Coupon) {
|
||||
|
||||
.submit-btn:hover { opacity: 0.9; }
|
||||
|
||||
/* Form section labels (organize modal into visual sections) */
|
||||
.form-section-label {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--active-color);
|
||||
padding: 8px 0 2px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Submit btn icon alignment */
|
||||
.submit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.submit-btn .material-icons { font-size: 1.1rem; }
|
||||
|
||||
.spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user