fix(coupons): remove duplicate business cols from coupons in PromoterDashboard and CouponsView

This commit is contained in:
2026-02-26 15:36:32 -05:00
parent 23f8438456
commit 10cb37c866
6 changed files with 15 additions and 28 deletions

View File

@ -34,7 +34,7 @@ export const couponsService = {
/** Create a new coupon */
async createCoupon(coupon: Omit<Coupon, 'id' | 'created_at' | 'updated_at'>): Promise<Coupon> {
// Prevent sending nested business properties over insert
const { business, ...payload } = coupon as any
const { business, business_name, business_address, business_phone, created_at, updated_at, id, ...payload } = coupon as any
const { data, error } = await supabase.from('coupons').insert([payload]).select().single()
if (error) throw new Error(error.message)
return data as Coupon
@ -42,7 +42,7 @@ export const couponsService = {
/** Update an existing coupon */
async updateCoupon(id: string, coupon: Partial<Coupon>): Promise<Coupon> {
const { business, ...payload } = coupon as any
const { business, business_name, business_address, business_phone, created_at, updated_at, id: _id, ...payload } = coupon as any
const { data, error } = await supabase.from('coupons').update(payload).eq('id', id).select().single()
if (error) throw new Error(error.message)
return data as Coupon

View File

@ -72,9 +72,6 @@ export interface Coupon {
business_id?: string | null
title: string
description?: string | null
business_name?: string | null
business_address?: string | null
business_phone?: string | null
image_url?: string | null
social_media?: string | null
terms?: string | null

View File

@ -24,7 +24,7 @@ onMounted(() => {
const filteredCoupons = computed(() => {
return couponStore.coupons.filter(c => {
const matchesSearch = c.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
c.business_name?.toLowerCase().includes(searchQuery.value.toLowerCase())
c.business?.name?.toLowerCase().includes(searchQuery.value.toLowerCase())
const matchesCategory = selectedCategory.value === 'Todas' || c.category === selectedCategory.value
return matchesSearch && matchesCategory
})
@ -43,7 +43,7 @@ function openCoupon(coupon: Coupon) {
analyticsService.logEvent({
event_name: 'promo_view',
item_id: coupon.title,
properties: { coupon_id: coupon.id, business: coupon.business_name }
properties: { coupon_id: coupon.id, business: coupon.business?.name }
})
}
@ -51,13 +51,13 @@ function handleDirections() {
if (!selectedCoupon.value) return
analyticsService.logEvent({
event_name: 'promo_click',
item_id: 'directions_' + selectedCoupon.value.business_name,
item_id: 'directions_' + selectedCoupon.value.business?.name,
properties: {
coupon_id: selectedCoupon.value.id,
action: 'get_directions'
}
})
window.open(`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(selectedCoupon.value.business_address || selectedCoupon.value.business_name || '')}`, '_blank')
window.open(`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(selectedCoupon.value.business?.address || selectedCoupon.value.business?.name || '')}`, '_blank')
}
function getCategoryIcon(category?: string | null) {
@ -142,7 +142,7 @@ function getCategoryIcon(category?: string | null) {
</div>
<div class="offer-content">
<h3 class="offer-title">{{ coupon.business_name || 'Restaurante' }}</h3>
<h3 class="offer-title">{{ coupon.business?.name || 'Restaurante' }}</h3>
<p class="offer-benefit">{{ coupon.title }}</p>
</div>
</div>
@ -186,7 +186,7 @@ function getCategoryIcon(category?: string | null) {
</div>
<div class="modal-info-section">
<h2 class="modal-business-title">{{ selectedCoupon.business_name }}</h2>
<h2 class="modal-business-title">{{ selectedCoupon.business?.name }}</h2>
<div class="benefit-highlight-box">
<span class="material-icons icon-label">local_offer</span>

View File

@ -1150,7 +1150,7 @@ function clearNavigation() {
<!-- Info -->
<div class="sheet-info">
<p class="sheet-biz-name">{{ currentPromo.business_name || 'SIBU' }}</p>
<p class="sheet-biz-name">{{ currentPromo.business?.name || 'SIBU' }}</p>
<h3 class="sheet-promo-title">{{ currentPromo.title }}</h3>
<p class="sheet-promo-desc">{{ currentPromo.description }}</p>
<button class="sheet-cta" @click="router.push('/business/' + currentPromo.business_id)">
@ -1204,7 +1204,7 @@ function clearNavigation() {
</div>
<div class="promo-body-modal">
<h2 class="promo-title-modal">{{ selectedPromo.title }}</h2>
<div class="promo-biz">{{ selectedPromo.business_name }}</div>
<div class="promo-biz">{{ selectedPromo.business?.name }}</div>
<p>{{ selectedPromo.description }}</p>
</div>
<div class="promo-actions-modal">

View File

@ -163,7 +163,7 @@ const getFullUrl = (path: string) => getImageUrl(path)
<div class="coupon-main">
<div class="coupon-details">
<h3>{{ userCoupon.coupon?.title || 'Cupón' }}</h3>
<p class="biz-name">{{ userCoupon.coupon?.business_name || 'Comercio' }}</p>
<p class="biz-name">{{ userCoupon.coupon?.business?.name || 'Comercio' }}</p>
<div class="code-row">
<span class="code">{{ userCoupon.redemption_code }}</span>
<span :class="['status-tag', userCoupon.status]">{{ getStatusLabel(userCoupon.status) }}</span>

View File

@ -36,9 +36,6 @@ const currentCoupon = ref<Partial<Coupon>>({
title: '',
business_id: null,
description: '',
business_name: '',
business_address: '',
business_phone: '',
image_url: '',
social_media: '',
terms: '',
@ -83,7 +80,7 @@ function checkHash() {
const filteredCoupons = computed(() => {
return coupons.value.filter(c => {
const matchesSearch = c.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
(c.business_name?.toLowerCase().includes(searchQuery.value.toLowerCase()))
(c.business?.name?.toLowerCase().includes(searchQuery.value.toLowerCase()))
const matchesCategory = categoryFilter.value === 'Todas' || c.category === categoryFilter.value
return matchesSearch && matchesCategory
})
@ -247,9 +244,6 @@ function openCreateModal() {
title: '',
business_id: null,
description: '',
business_name: '',
business_address: '',
business_phone: '',
image_url: '',
social_media: '',
terms: '',
@ -266,9 +260,6 @@ function openCreateModal() {
function handleBusinessChange() {
const selectedBiz = businesses.value.find(b => b.id === currentCoupon.value.business_id)
if (selectedBiz) {
currentCoupon.value.business_name = selectedBiz.name
currentCoupon.value.business_address = selectedBiz.address
currentCoupon.value.business_phone = selectedBiz.phone
currentCoupon.value.image_url = selectedBiz.image_url
currentCoupon.value.social_media = selectedBiz.social_media
currentCoupon.value.category = selectedBiz.category
@ -297,8 +288,7 @@ async function saveCoupon() {
if (!isEditing.value) delete data.id
const fieldsToClean = [
'description', 'business_name', 'business_address',
'business_phone', 'image_url', 'social_media', 'terms',
'description', 'image_url', 'social_media', 'terms',
'discount_percentage', 'discount_amount', 'valid_from', 'valid_until'
]
@ -459,7 +449,7 @@ async function toggleCouponStatus(coupon: Coupon) {
<strong>{{ coupon.title }}</strong>
<div class="business-tag">
<span class="material-icons">store</span>
{{ coupon.business_name || 'Comercio Local' }}
{{ coupon.business?.name || 'Comercio Local' }}
</div>
</div>
</div>
@ -638,7 +628,7 @@ async function toggleCouponStatus(coupon: Coupon) {
<div class="form-row">
<div class="form-group">
<label>Nombre del Local</label>
<input v-model="currentCoupon.business_name" type="text">
<input :value="businesses.find(b => b.id === currentCoupon.business_id)?.name || ''" type="text" readonly disabled class="readonly-input">
</div>
<div class="form-group">
<label>Categoría</label>