⚑ Reportar anuncio

Tu denuncia se revisará. No retiramos automáticamente un anuncio solo por recibir un reporte.

🛡️ Moderación de anuncios

Solo usuarios autorizados pueden consultar y resolver reportes. Resolver un reporte no modifica automáticamente el anuncio.

Todos
Destacados
🗂️ Mostrando 0 avisos | Categoría: Negocios y comercios 0 AVISOS EN TRASPASOS, MOBILIARIO Y NEGOCIOS EN MÉRIDA Y YUCATÁN

Traspasos, Mobiliario y Negocios en Mérida y Yucatán

AVISOS YUCATÁN

CLASIFICADOS • JUEVES 24 DE OCTUBRE DE 2024 • MÉRIDA, YUC.

El punto de encuentro para traspasos de locales, mobiliario comercial, estanterías y equipo para restaurantes en Mérida.

Invierte con certeza y adquiere herramientas para arrancar o expandir tu negocio en las principales zonas de Yucatán.

Por ahora no hay avisos activos en esta sección.

Sé el primero en anunciar tu producto o servicio en Traspasos, Mobiliario y Negocios en Mérida y Yucatán.

¿Dudas? ¡Escríbenos por WhatsApp!
1
');doc.close(); const st=doc.createElement('style');st.textContent='@page{size:A4;margin:14mm}body{font:14px/1.55 Arial,sans-serif;color:#14243e;margin:26px;max-width:760px}header{display:flex;justify-content:space-between;align-items:center;border-bottom:3px solid #caa757;padding-bottom:12px}h1{font-size:25px;margin:20px 0 6px;overflow-wrap:anywhere}h2{font-size:13px;margin:18px 0 4px}p{overflow-wrap:anywhere;white-space:pre-wrap}.folio{font-weight:bold;color:#8c651d}.photos{display:flex;gap:10px;flex-wrap:wrap}.photos img{max-width:31%;max-height:210px;object-fit:contain;border:1px solid #ddd;border-radius:9px}.qr{width:148px;height:148px;object-fit:contain}footer{display:flex;align-items:center;gap:18px;border-top:1px solid #ddd;padding-top:16px;margin-top:20px}.controls{margin:16px 0;background:#0b111e;color:white;border:0;border-radius:7px;padding:10px 18px;cursor:pointer}@media print{.controls{display:none}body{margin:0}}';doc.head.appendChild(st); const header=doc.createElement('header');header.append(ayPrintNode('strong','AVISOS YUCATÁN','font-size:20px;letter-spacing:1px'),ayPrintNode('span',ayFolio(ad.id),'font-weight:bold;color:#8c651d'));doc.body.appendChild(header); doc.body.appendChild(ayPrintNode('h1',ad.title));doc.body.appendChild(ayPrintNode('p',ad.short_description)); if(ad.full_description){doc.body.appendChild(ayPrintNode('h2','MÁS INFORMACIÓN'));doc.body.appendChild(ayPrintNode('p',ad.full_description));} if(ad.municipality){doc.body.appendChild(ayPrintNode('p','Ubicación: '+ad.municipality+(ad.state_code?', '+ad.state_code:'')));} const photos=ayPrintNode('div',null,'');photos.className='photos';doc.body.appendChild(photos); const footer=doc.createElement('footer');const qr=doc.createElement('img');qr.className='qr';qr.src=ayQrUrl(ad.id);qr.alt='Código QR de Avisos Yucatán';footer.append(qr,ayPrintNode('div','Escanea para entrar a avisosyucatan.com\nBusca o consulta el folio '+ayFolio(ad.id),'white-space:pre-line'));doc.body.appendChild(footer); const printBtn=ayPrintNode('button','Imprimir aviso');printBtn.className='controls';printBtn.type='button';printBtn.onclick=()=>popup.print();doc.body.appendChild(printBtn); try { const r=await fetch('/avisos-v2-fotos.php?recurso=listar&ad_id='+encodeURIComponent(ad.id),{credentials:'same-origin',cache:'no-store'}); const data=await r.json(); if(r.ok&&data.ok===true&&Array.isArray(data.fotos)){ data.fotos.slice(0,3).forEach(f=>{if(typeof f?.url!=='string'||!/^\/?avisos-v2-fotos\.php\?recurso=ver&id=\d+$/.test(f.url))return;const img=doc.createElement('img');img.src=new URL(f.url,window.location.href).href;img.alt='Foto del aviso';photos.appendChild(img);}); } }catch(_){/* Un aviso sin fotos imprimibles sigue siendo imprimible */} if(photos.children.length===0)photos.remove(); popup.focus(); } function feedback(text){const t=getTarget();if(t)t.replaceChildren(para(text));} function editableField(label,value,name,maximum,container,multiline) { const wrap=el('label',undefined,'flex flex-col gap-1 text-xs font-bold text-gray-700');wrap.appendChild(el('span',label)); const node=document.createElement(multiline?'textarea':'input');node.name=name;node.value=value||'';node.maxLength=maximum; node.className='w-full border border-gray-300 rounded-lg p-2 text-sm text-gray-900'; if(multiline)node.rows=3; wrap.appendChild(node);container.appendChild(wrap);return node; } async function edit(ad) { if (busy) return; if (!['active','paused'].includes(ad.status)) {alert('Solo se pueden editar avisos activos o pausados.');return;} // Reutilizar el formulario de publicación, sin crear un segundo aviso. const modal=document.getElementById('createModal'); if(!modal){alert('No se encontró el formulario de avisos.');return;} // Cerrar únicamente el diálogo de administración, SIN navegar en el historial. // switchMainView('home') llama history.back() y cierra el formulario de edición. const dashboard=document.getElementById('dashboardViewSection'); if(dashboard){ if(dashboard.open) dashboard.close(); dashboard.classList.add('hidden'); } document.getElementById('homeViewSection')?.classList.remove('hidden'); // Sustituimos el hash de Mis avisos sin disparar hashchange/popstate. if(location.hash==='#mis-avisos'){ history.replaceState(history.state,'',location.pathname+location.search); } window.ayV2AuthPassOnce=true; openCreateModalNew(); // Identidad del plan existente: la edición no supone una nueva compra. window.ayv47EditingPlan = {id:Number(ad.id),tier:ad.tier}; window.ayv80RefreshPlanUI?.(); const get=id=>document.getElementById(id); const fill=(id,val)=>{const e=get(id);if(e){e.value=val??'';e.dispatchEvent(new Event('input',{bubbles:true}));}}; fill('newBold',ad.title);fill('newDesc',ad.short_description);fill('newDetails',ad.full_description); fill('ayV2Municipality',ad.municipality);fill('newPhone',ad.contact_whatsapp); fill('ayV2State',ad.state_code||'YUC'); if (ad.user_name) fill('newName', ad.user_name); const pAmtPre = ad.price_amount ?? ad.precio; if (pAmtPre !== null && pAmtPre !== undefined && Number(pAmtPre) > 0) { fill('newPrice', String(pAmtPre)); } let categoryId=Number(ad.category_id); try { const response=await fetch('/avisos-v2-nucleo.php?recurso=categorias',{credentials:'same-origin',cache:'no-store'}); const data=await response.json(); const cat=Array.isArray(data.categorias)?data.categorias.find(c=>Number(c.id)===categoryId):null; const normalize=v=>String(v||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLowerCase().trim(); const sel=get('newCat'); if(cat&&sel){const match=[...sel.options].find(o=>normalize(o.value)===normalize(cat.name)||normalize(o.textContent.replace(/^\d+\.\s*/,''))===normalize(cat.name));if(match)sel.value=match.value;} }catch(_){/* Si el catálogo no responde, se conserva la categoría existente. */} const prevent=['ayV2PrevalidateButton']; const actionButtons=[...modal.querySelectorAll('button')].filter(b=>b.getAttribute('onclick')?.includes('publishNewAd(')); const original=new Map(); for(const b of [...actionButtons,...prevent.map(get).filter(Boolean)]){original.set(b,{display:b.style.display,disabled:b.disabled});b.style.display='none';b.disabled=true;} const hidden=[]; for(const id of ['ayV34ContactOptions']){const el=get(id);if(el){hidden.push([el,el.style.display]);el.style.display='none';}} const email=get('newEmail');const emailDisabled=email?.disabled;if(email)email.disabled=true; const panel=document.createElement('div');panel.id='ayV47EditActions';panel.style.cssText='position:sticky;bottom:0;z-index:20;background:#fff;border-top:2px solid #eab308;padding:12px;display:grid;gap:8px;border-radius:12px'; const title=document.createElement('strong');title.textContent='Editando '+('AY-'+String(ad.id).padStart(8,'0'))+' · Se conservarán el folio y las fotografías';title.style.cssText='font-size:13px;color:#16324f'; const warning=document.createElement('small');warning.textContent='Los métodos de contacto y las fotos actuales se conservarán. Solo cambiaremos los datos editables.';warning.style.cssText='color:#64748b'; const save=document.createElement('button');save.type='button';save.textContent='Guardar cambios del aviso';save.style.cssText='background:#166534;color:white;border:0;border-radius:8px;padding:12px;font-weight:800;cursor:pointer'; const cancel=document.createElement('button');cancel.type='button';cancel.textContent='Cancelar edición';cancel.style.cssText='background:#e2e8f0;color:#0f172a;border:0;border-radius:8px;padding:9px;font-weight:700;cursor:pointer'; panel.append(title,warning); if(['highlight_text','highlight_photos'].includes(ad.tier)) { const addPhotos=document.createElement('button'); addPhotos.type='button'; addPhotos.textContent='+ Agregar fotografías al destacado (máximo '+(ad.tier==='highlight_photos'?15:10)+')'; addPhotos.style.cssText='background:#0f766e;color:white;border:0;border-radius:8px;padding:11px;font-weight:800;cursor:pointer'; if(ad.status!=='active' || Number(ad.vencido)===1){ addPhotos.disabled=true; addPhotos.textContent='Para agregar fotografías, el destacado debe estar activo y vigente'; addPhotos.style.opacity='.6'; }else { addPhotos.addEventListener('click',()=>ayv83AddPaidPhotos(ad)); } const note=document.createElement('small'); note.textContent='Puedes agregar fotografías disponibles en tu plan. Para sustituir o borrar fotografías actuales se requiere habilitar esa operación en el módulo de fotos.'; note.style.cssText='color:#64748b;line-height:1.35'; panel.append(addPhotos,note); const gallery=await ayvManageGallery(ad);panel.append(gallery); addPhotos.addEventListener('click',()=>{setTimeout(()=>{ if(gallery.isConnected)gallery.refresh(); },2500);}); } panel.append(save,cancel); const host=get('ayV2PrevalidateButton')?.parentElement; if(!host){alert('No se encontró dónde guardar los cambios.');return;} host.prepend(panel); let ended=false; const cleanup=()=>{ if(ended)return;ended=true;panel.remove(); window.ayv47EditingPlan=null; window.ayv80RefreshPlanUI?.(); for(const [b,v] of original){b.style.display=v.display;b.disabled=v.disabled;} for(const [el,display] of hidden)el.style.display=display; if(email)email.disabled=emailDisabled; window.ayV47ExitEditMode=null; }; window.ayV47ExitEditMode=cleanup; cancel.addEventListener('click',()=>{cleanup();closeCreateModalNew();switchMainView('dashboard');}); save.addEventListener('click',async()=>{ if(busy)return; const sel=get('newCat'); let nextCategory=categoryId; if(sel?.value){ try {const r=await fetch('/avisos-v2-nucleo.php?recurso=categorias',{credentials:'same-origin',cache:'no-store'});const j=await r.json();const normalize=v=>String(v||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLowerCase().trim();const c=j.categorias?.find(c=>normalize(c.name)===normalize(sel.value));if(c)nextCategory=Number(c.id);else if(sel.value!==''&&!Number.isInteger(nextCategory))throw Error('Categoría no válida');}catch(e){alert('No se pudo validar el catálogo. '+e.message);return;} } busy=true;save.disabled=true;save.textContent='Guardando...'; const priceVal = get('newPrice')?.value ? Number(get('newPrice').value.replace(/[^0-9.]/g,'')) : null; const userNameVal = get('newName')?.value?.trim() || null; try {await call('editar',{id:Number(ad.id),title:get('newBold').value,short_description:get('newDesc').value,full_description:get('newDetails').value,municipality:get('ayV2Municipality').value,contact_whatsapp:get('newPhone').value,state_code:get('ayV2State').value,category_id:nextCategory,price_amount:priceVal,user_name:userNameVal}); cleanup();closeCreateModalNew();switchMainView('dashboard');await load(); }catch(e){alert('No se guardaron los cambios: '+e.message)}finally{busy=false;save.disabled=false;save.textContent='Guardar cambios del aviso';} }); get('modalCenterScrollCol')?.scrollTo({top:0,behavior:'auto'}); } async function startCheckout(ad,tier,skipConfirm=false,existingPaymentWindow=null){ if(busy)return false; const price=tier==='highlight_text'?'$20.00 MXN por 15 días':'$50.00 MXN por 30 días'; if(!skipConfirm && !window.confirm('¿Destacar el aviso #'+ad.id+' por '+price+'? Se abrirá Stripe en otra ventana.'))return false; // window.open ocurre antes de await, directamente desde el clic (antibloqueo popup). const paymentWindow=(existingPaymentWindow && !existingPaymentWindow.closed) ? existingPaymentWindow : window.open('','ayv2_pago_'+String(ad.id),'popup=yes,width=650,height=790,scrollbars=yes,resizable=yes'); if(!paymentWindow){window.alert('Tu navegador bloqueó la ventana emergente. Habilita ventanas emergentes para avisosyucatan.com e inténtalo de nuevo. No se hizo ningún cobro.');return false;} try {paymentWindow.document.title='Preparando pago seguro';paymentWindow.document.body.textContent='Preparando la pasarela segura de Stripe…';}catch(_){/* Si el navegador impide escribir, continuar. */} busy=true; try { const response=await fetch('/ayv2-stripe.php?recurso=estado',{credentials:'same-origin',cache:'no-store'}); const info=await response.json(); if(!response.ok||info.stripe_habilitado!==true)throw Error('Stripe aún no está configurado para pagos reales. No se realizará ningún cobro.'); const precioEsperado=tier==='highlight_text'?20:50; if(Number(info?.precios_mxn?.[tier])!==precioEsperado)throw Error('Se está actualizando la promoción. Vuelve a intentar en unos minutos. No se realizará ningún cobro.'); const pay=await fetch('/ayv2-stripe.php?recurso=checkout',{ method:'POST',credentials:'same-origin',cache:'no-store',headers:{'Content-Type':'application/json','X-CSRF-Token':info.csrf_token}, body:JSON.stringify({id:Number(ad.id),tier,website:''}) }); const data=await pay.json(); if(!pay.ok||data.ok!==true)throw Error(({sube_fotos_antes_de_pagar:'Este destacado requiere fotografías cargadas en el anuncio.',aviso_no_disponible_para_destacar:'Este anuncio no está disponible para destacar.',ya_hay_un_pago_en_proceso:'Ya hay un pago pendiente para este aviso.',pago_pendiente_requiere_conciliacion:'La sesión anterior de Stripe ya no está abierta o requiere confirmación. Tu aviso sigue oculto; no se ha iniciado un segundo cobro. Contacta a soporte para conciliar el pago.',hay_un_pago_de_otro_plan_en_proceso:'Ya hay una solicitud de pago de otro tipo para este aviso.',pago_aun_preparandose:'Stripe todavía está preparando el pago anterior. Intenta abrirlo de nuevo en unos segundos.',demasiados_intentos_pago:'Demasiadas solicitudes de pago. Espera unos minutos.'})[data.error]||'No se pudo iniciar el pago. No se hizo ningún cobro.'); const url=new URL(data.checkout_url); if(url.protocol!=='https:'||url.hostname!=='checkout.stripe.com')throw Error('Enlace de pago no válido.'); if(paymentWindow.closed) throw Error('Cerraste la ventana de pago antes de abrir Stripe. El aviso sigue pendiente.'); paymentWindow.location.replace(url.href); paymentWindow.focus(); return true; }catch(e){try{if(!paymentWindow.closed)paymentWindow.close();}catch(_){}window.alert(e.message);return false;}finally{busy=false;} } window.ayV2CheckoutNewAd = (id,tier,popup)=>startCheckout({id:Number(id)},tier,true,popup); function renewalAvailability(ad) { const expiry=(typeof ad.expires_at==='string')?Date.parse(ad.expires_at.replace(' ','T')+'Z'):NaN; if(!Number.isFinite(expiry))return {allowed:false,note:'No se pudo identificar la vigencia.'}; const seconds=Math.round((expiry-Date.now())/1000); if(seconds>172800){ const when=new Date(expiry-172800000).toLocaleDateString('es-MX',{year:'numeric',month:'short',day:'numeric'}); return {allowed:false,note:'Renovar disponible desde '+when+' (48 horas antes de vencer).'}; } if(seconds<-(30*86400))return {allowed:false,note:'Este aviso venció hace más de 30 días. Publica uno nuevo.'}; return {allowed:true,note:''}; } function buildAdVisualCard(ad) { const tier = String(ad.tier || 'free'); const card = document.createElement('div'); card.className = 'ay-dashboard-ad-card ' + (tier === 'highlight_photos' ? 'ad-carousel' : tier === 'highlight_text' ? 'ad-shaded' : 'ad-free'); // 1. Contenedor de fotografía / banner const imgWrap = document.createElement('div'); imgWrap.className = 'ay-card-img-wrap'; const img = document.createElement('img'); img.className = 'ay-card-img'; img.loading = 'lazy'; img.decoding = 'async'; img.alt = String(ad.title || 'Aviso'); img.src = '/ayv2-publicidad-1-v031.png'; imgWrap.appendChild(img); card.appendChild(imgWrap); // Consulta de la fotografía principal del aviso fetch('/avisos-v2-fotos.php?recurso=listar&ad_id=' + encodeURIComponent(ad.id), { credentials: 'same-origin', cache: 'no-store' }) .then(r => r.ok ? r.json() : null) .then(data => { if (data && data.ok === true && Array.isArray(data.fotos) && data.fotos.length > 0) { const url = data.fotos[0]?.url; if (typeof url === 'string') { img.src = url; } } }).catch(() => {}); // 2. Cuerpo de la tarjeta con estilo Marketplace const body = document.createElement('div'); body.className = 'ay-card-body'; // 3. Título y descripción integrados const p = document.createElement('p'); p.className = 'ay-card-text'; const strong = document.createElement('strong'); strong.className = 'ay-card-title'; strong.textContent = String(ad.title || 'Aviso'); p.appendChild(strong); p.appendChild(document.createTextNode(' ' + String(ad.short_description || ''))); body.appendChild(p); // 4. Precio destacado con centavos en superíndice ($ 4,500 00) o "A tratar" let hasNumericPrice = false; let priceInt = ''; let cents = '00'; const pAmt = ad.price_amount ?? ad.precio ?? ad.price; if (pAmt !== null && pAmt !== undefined && Number(pAmt) > 0) { hasNumericPrice = true; const parts = Number(pAmt).toFixed(2).split('.'); priceInt = Number(parts[0]).toLocaleString('es-MX'); cents = parts[1]; } else { const txt = (ad.title + ' ' + (ad.short_description || '') + ' ' + (ad.full_description || '')); const match = txt.match(/\$\s*([0-9]{1,3}(?:,[0-9]{3})+|[0-9]{3,7})/); if (match) { const cleanNum = Number(match[1].replace(/,/g, '')); if (cleanNum > 0) { hasNumericPrice = true; priceInt = cleanNum.toLocaleString('es-MX'); cents = '00'; } } } const priceBox = document.createElement('div'); priceBox.className = 'ay-card-price'; if (hasNumericPrice) { priceBox.innerHTML = '$ ' + priceInt + '' + cents + ' MXN'; } else { priceBox.innerHTML = 'A tratar'; } body.appendChild(priceBox); // 5. Usuario que subió el aviso let userName = String(ad.user_name || '').trim(); if (!userName || userName.toLowerCase() === 'usuario') { if (ad.contact_email && typeof ad.contact_email === 'string') { userName = ad.contact_email.split('@')[0]; } else { const loggedUser = document.getElementById('ayAliUserName')?.textContent?.trim(); if (loggedUser && (!window.ayIsAdmin || !ayDashboardFilterAll)) { userName = loggedUser; } } } if (!userName) userName = 'Usuario'; userName = userName.charAt(0).toUpperCase() + userName.slice(1); const userBox = document.createElement('div'); userBox.className = 'ay-card-user'; userBox.title = 'Publicado por ' + userName; userBox.innerHTML = 'Por ' + userName + ''; body.appendChild(userBox); // 6. Pie de tarjeta: Insignia Trato directo + WHATSAPP const footer = document.createElement('div'); footer.className = 'ay-card-footer'; const tratoBadge = document.createElement('span'); tratoBadge.className = 'ay-badge-trato'; tratoBadge.textContent = 'Trato directo'; footer.appendChild(tratoBadge); if (ad.has_whatsapp == 1 || ad.contact_whatsapp == 1) { const waBadge = document.createElement('span'); waBadge.className = 'ay-badge-wa'; waBadge.innerHTML = ' WHATSAPP'; footer.appendChild(waBadge); } body.appendChild(footer); card.appendChild(body); const slugTitle = String(ad.title || 'aviso') .normalize('NFD').replace(/[\u0300-\u036f]/g, '') .toLowerCase().replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '').slice(0, 85) || 'aviso'; const viewBtn = document.createElement('a'); viewBtn.href = '/aviso/' + slugTitle + '-' + ad.id; viewBtn.target = '_blank'; viewBtn.className = 'ay-card-view-btn'; viewBtn.style.cssText = 'display:block;text-align:center;text-decoration:none;margin-top:6px;background:#0b111e;color:#fff;font-size:11.5px;font-weight:bold;padding:6px 10px;border-radius:6px;transition:background 0.15s;'; viewBtn.innerHTML = 'Ver aviso en el portal ↗'; card.appendChild(viewBtn); return card; } function adCard(ad) { const box = el('article', undefined, 'ay-user-ad-item rounded-2xl border border-gray-200/90 bg-white p-4 sm:p-5 shadow-xs transition-shadow hover:shadow-sm space-y-4 mb-4'); // 1. Encabezado con estado, folio y vigencia const heading = el('div', undefined, 'flex items-center justify-between pb-3 border-b border-gray-100 flex-wrap gap-2'); const headLeft = el('div', undefined, 'flex items-center gap-2 flex-wrap'); const isVencido = ad.vencido == 1; const statusText = isVencido ? 'Vencido' : (statuses[ad.status] || ad.status); const statusBg = isVencido ? 'bg-red-50 text-red-700 border-red-200' : ad.status === 'active' ? 'bg-emerald-50 text-emerald-700 border-emerald-200' : ad.status === 'paused' ? 'bg-amber-50 text-amber-700 border-amber-200' : 'bg-blue-50 text-blue-700 border-blue-200'; const statusDot = isVencido ? '🔴' : ad.status === 'active' ? '🟢' : '🟡'; const stBadge = el('span', statusDot + ' ' + statusText, 'text-xs font-bold px-2.5 py-0.5 rounded-full border ' + statusBg); headLeft.appendChild(stBadge); const folioSpan = el('span', 'Folio ' + ayFolio(ad.id), 'text-xs font-extrabold text-gray-800'); headLeft.appendChild(folioSpan); const tierBadge = el('span', ad.tier === 'highlight_photos' ? '📸 Destacado Fotos' : ad.tier === 'highlight_text' ? '👑 Destacado Texto' : 'Gratis', 'text-xs font-semibold ' + (ad.tier === 'highlight_text' ? 'text-amber-700' : ad.tier === 'highlight_photos' ? 'text-orange-700' : 'text-gray-500') ); headLeft.appendChild(tierBadge); heading.appendChild(headLeft); const vigencia = el('span', ad.expires_at ? ('Vigencia: hasta ' + ad.expires_at + ' UTC') : 'Sin fecha de vencimiento', 'text-[11.5px] text-gray-500 font-medium'); heading.appendChild(vigencia); box.appendChild(heading); // 2. Contenedor de 2 columnas: Vista previa idéntica a la pantalla principal (izquierda) + Herramientas (derecha) const bodyLayout = el('div', undefined, 'grid grid-cols-1 md:grid-cols-[250px_1fr] gap-5 items-start'); // Columna izquierda: El aviso idéntico a la pantalla principal const previewWrapper = el('div', undefined, 'flex flex-col items-center w-full max-w-[250px] mx-auto md:mx-0'); previewWrapper.appendChild(buildAdVisualCard(ad)); bodyLayout.appendChild(previewWrapper); // Columna derecha: Suite de administración del aviso const rightCol = el('div', undefined, 'flex flex-col justify-between space-y-4 min-w-0'); // Herramientas de folio: QR, imprimir, compartir const folioTools = el('div', undefined, 'ayv24-folio-tools p-3.5 bg-slate-50/90 rounded-2xl border border-slate-200/90 flex flex-col gap-2.5 shadow-2xs'); // Fila superior: QR y Folio oficial completo const folioTop = el('div', undefined, 'flex items-center gap-3 min-w-0'); const qrImg = el('img'); qrImg.src = ayQrUrl(ad.id); qrImg.alt = 'QR para visitar Avisos Yucatán'; qrImg.loading = 'lazy'; qrImg.width = 46; qrImg.height = 46; qrImg.className = 'rounded-xl bg-white p-1 border border-slate-200 shrink-0 shadow-2xs'; const folioLabel = el('div', undefined, 'ayv24-folio-info min-w-0 flex-1'); const folioTag = el('span', 'Folio oficial', 'text-[10px] uppercase font-bold tracking-wider text-slate-400 block leading-tight mb-0.5'); const folioCode = el('strong', ayFolio(ad.id), 'block text-sm sm:text-base font-black text-slate-900 tracking-wide whitespace-nowrap leading-tight'); folioLabel.append(folioTag, folioCode); folioTop.append(qrImg, folioLabel); // Fila inferior: Botones Imprimir y Compartir alineados const folioButtons = el('div', undefined, 'flex items-center gap-2 pt-2 border-t border-slate-200/70 flex-wrap'); const printButton = el('button', '⎙ Imprimir', 'ayv24-print-btn'); printButton.type = 'button'; printButton.style.cssText = 'background:#ffffff!important;color:#16324f!important;border:1px solid #cbd5e1!important;border-radius:9px!important;padding:7px 13px!important;font-size:11.5px!important;font-weight:700!important;cursor:pointer;display:inline-flex;align-items:center;gap:5px;box-shadow:0 1px 3px rgba(15,23,42,0.06);white-space:nowrap;transition:all 0.15s ease;'; printButton.onclick = () => ayPrintAd(ad); folioButtons.appendChild(printButton); if (ad.status === 'active' && ad.vencido != 1) folioButtons.appendChild(ayShareMenu(ad)); folioTools.append(folioTop, folioButtons); rightCol.appendChild(folioTools); // Botones de acción administrativa const actionsWrapper = el('div', undefined, 'space-y-2'); actionsWrapper.appendChild(el('p', 'Acciones y administración:', 'text-[11px] font-bold text-gray-500 uppercase tracking-wider')); const actions = el('div', undefined, 'ayv24-actions flex flex-wrap gap-2 pt-1'); const allowed = ad.tier === 'free'; if (allowed && ad.status === 'draft') { const publishGroup = el('div', undefined, 'ayv24-plan-buttons'); const free = btn('Gratis · 7 días', () => mutation('publicar_borrador_gratis', ad.id), '#0b111e'); const textPlan = btn('Texto · $20', () => startCheckout(ad, 'highlight_text', true), '#d39b20'); const photoPlan = btn('Fotos · $50', () => startCheckout(ad, 'highlight_photos', true), '#f07c29'); for (const b of [free, textPlan, photoPlan]) { b.className = 'ayv24-plan-btn'; publishGroup.appendChild(b); } actionsWrapper.appendChild(el('p', 'Publicar este borrador sin volver a capturar los datos:', 'text-[11px] text-slate-500 mt-2')); actionsWrapper.appendChild(publishGroup); actionsWrapper.appendChild(el('p', 'Los destacados solo aparecerán después de que Stripe confirme el pago. Para fotos debes tener fotografías guardadas.', 'text-[11px] text-slate-500 mt-1')); } if (['active', 'paused'].includes(ad.status)) { actions.appendChild(btn('✏️ Editar', () => edit(ad), '#16324f')); if (ad.status === 'active' && ad.vencido != 1) actions.appendChild(btn('⏸️ Pausar', () => mutation('pausar', ad.id), '#92400e')); if (ad.status === 'paused' && (allowed || (ad.expires_at && Date.parse(ad.expires_at.replace(' ', 'T') + 'Z') > Date.now()))) actions.appendChild(btn('▶️ Reactivar', () => mutation('reactivar', ad.id), '#166534')); } if (allowed && ['active', 'expired'].includes(ad.status)) { const eligibility = renewalAvailability(ad); const renew = btn('🔄 Renovar gratis · 7 días', () => mutation('renovar', ad.id), '#0f766e'); renew.disabled = !eligibility.allowed; if (!eligibility.allowed) { renew.style.opacity = '0.55'; renew.style.cursor = 'not-allowed'; renew.title = eligibility.note; } actions.appendChild(renew); if (!eligibility.allowed) actionsWrapper.appendChild(el('p', eligibility.note, 'text-xs text-slate-600 mt-1')); } if (allowed && ad.status === 'active' && ad.vencido != 1) { if (window.ayIsAdmin) { actions.appendChild(btn('👑 Destacar texto (Admin · Gratis)', () => mutation('destacar_admin', ad.id, { tier: 'highlight_text' }), '#98670e')); actions.appendChild(btn('📸 Destacar fotos (Admin · Gratis)', () => mutation('destacar_admin', ad.id, { tier: 'highlight_photos' }), '#ea580c')); } else { actions.appendChild(btn('👑 Destacar texto · $20.00 MXN', () => startCheckout(ad, 'highlight_text'), '#98670e')); const photos = btn('📸 Destacado con fotos · $50.00 MXN', () => startCheckout(ad, 'highlight_photos'), '#ea580c'); actions.appendChild(photos); } } if (['highlight_text', 'highlight_photos'].includes(ad.tier) && ad.status === 'active' && ad.vencido != 1) { const cap = ad.tier === 'highlight_text' ? 10 : 15; actions.appendChild(btn('+ Agregar fotos (máximo ' + cap + ')', () => ayv83AddPaidPhotos(ad), '#0f766e')); actionsWrapper.appendChild(el('p', 'Puedes completar las fotos de tu paquete una vez confirmado el pago. Se guardan en WebP.', 'text-[11px] text-slate-600 mt-2')); } actions.appendChild(btn('🗑️ Eliminar', () => mutation('eliminar', ad.id), '#991b1b')); if (!allowed && ad.status === 'paused') actionsWrapper.appendChild(el('p', 'La pausa no detiene la vigencia del plan pagado.', 'text-xs text-amber-800 mt-1')); actionsWrapper.appendChild(actions); rightCol.appendChild(actionsWrapper); const editBox = el('div'); editBox.id = 'ayv2-edit-' + ad.id; rightCol.appendChild(editBox); bodyLayout.appendChild(rightCol); box.appendChild(bodyLayout); return box; } let ayDashboardFilterAll = false; let ayDashboardSearchQuery = ''; async function mutation(action,id,extraData){ if(busy)return; const ask={ pausar:'¿Pausar este aviso? Si es pagado, su fecha de vencimiento no cambia.', reactivar:'¿Reactivar este aviso?', renovar:'¿Renovar gratis este aviso por 7 días desde ahora? Si está vencido volverá a mostrarse en la portada.', publicar_borrador_gratis:'¿Publicar ESTE borrador gratis durante 7 días? Si tiene un pago pendiente, será rechazado por seguridad.', eliminar:'¿Eliminar este aviso de forma definitiva? Dejará de aparecer en la plataforma.', destacar_admin:'¿Destacar este aviso de forma gratuita como administrador?' }; if(!window.confirm(ask[action]||'¿Confirmas este cambio?'))return; busy=true;try{await call(action,{id:Number(id),...(extraData||{})});await load(null,ayDashboardFilterAll,ayDashboardSearchQuery);}catch(e){window.alert(e.message);}finally{busy=false;} } async function load(cursor, showAll, searchQuery) { const target=getTarget(); if(!target) return; if(showAll !== undefined) ayDashboardFilterAll = !!showAll; if(searchQuery !== undefined) ayDashboardSearchQuery = searchQuery || ''; try { if(!cursor) feedback('Cargando tus anuncios…'); const result=await call('estado');csrf=result.csrf_token||''; if(result.es_admin) window.ayIsAdmin = true; if(result.sesion_iniciada!==true){ const t = getTarget(); if (t) { t.replaceChildren(); const box = el('div', undefined, 'text-center py-8 px-4 space-y-4 bg-slate-50 rounded-xl border border-slate-200'); box.appendChild(el('p', 'Inicia sesión con tu correo o Google para ver y administrar tus anuncios.', 'text-sm text-gray-800 font-medium')); const loginBtn = btn('🔑 Iniciar sesión ahora', () => { const dash = document.getElementById('dashboardViewSection'); if (dash && dash.open) { try { dash.close(); } catch(_) { dash.removeAttribute('open'); } dash.classList.add('hidden'); } window.ayV44Login?.(); }, '#172b41'); loginBtn.style.cssText = 'padding:9px 18px;font-size:13px;border-radius:8px;font-weight:bold;cursor:pointer;'; box.appendChild(loginBtn); t.appendChild(box); } else { feedback('Inicia sesión con tu correo o Google para ver y administrar tus anuncios. Después vuelve a Mis Avisos.'); } return; } let path=ENDPOINT+'?recurso=listar'+(cursor?'&antes_de='+encodeURIComponent(cursor):''); if(window.ayIsAdmin && ayDashboardFilterAll) { path += '&todos=1'; if(ayDashboardSearchQuery) path += '&q=' + encodeURIComponent(ayDashboardSearchQuery); } const response=await fetch(path,{credentials:'same-origin',cache:'no-store'}); let data=await response.json();if(!response.ok||data.ok!==true)throw Error(errorMessage(data.error)); window.ayAllMyAds = data.avisos || []; window.ayAliNextCursor = data.siguiente_cursor || null; if(!cursor) target.replaceChildren(); if(window.ayIsAdmin && !cursor) { const adminTabs = document.createElement('div'); adminTabs.className = 'bg-white rounded-xl border border-slate-200 shadow-sm p-2.5 sm:p-3 mb-4'; const inner = document.createElement('div'); inner.className = 'flex flex-col sm:flex-row sm:items-center justify-between gap-2.5'; const leftGroup = document.createElement('div'); leftGroup.className = 'flex items-center gap-2 flex-wrap'; const badge = document.createElement('span'); badge.className = 'inline-flex items-center gap-1 px-2.5 py-1 rounded-lg text-xs font-bold bg-[#172b41] text-white shadow-sm shrink-0'; badge.innerHTML = '🛡️ Admin'; const tabsGroup = document.createElement('div'); tabsGroup.className = 'inline-flex p-0.5 bg-slate-100 rounded-lg border border-slate-200 shrink-0'; const myAdsBtn = document.createElement('button'); myAdsBtn.type = 'button'; myAdsBtn.textContent = 'Mis Avisos'; myAdsBtn.className = !ayDashboardFilterAll ? 'px-3 py-1.5 text-xs font-bold rounded-md bg-[#172b41] text-white shadow-sm transition-all' : 'px-3 py-1.5 text-xs font-medium rounded-md text-slate-600 hover:text-slate-900 hover:bg-white/60 transition-all'; myAdsBtn.onclick = () => load(null, false, ''); const allAdsBtn = document.createElement('button'); allAdsBtn.type = 'button'; allAdsBtn.textContent = '🌐 Todos los Avisos'; allAdsBtn.className = ayDashboardFilterAll ? 'px-3 py-1.5 text-xs font-bold rounded-md bg-amber-600 text-white shadow-sm transition-all' : 'px-3 py-1.5 text-xs font-medium rounded-md text-slate-600 hover:text-slate-900 hover:bg-white/60 transition-all'; allAdsBtn.onclick = () => load(null, true, ayDashboardSearchQuery); tabsGroup.append(myAdsBtn, allAdsBtn); leftGroup.append(badge, tabsGroup); const searchBox = document.createElement('div'); searchBox.className = 'flex items-center gap-1.5 w-full sm:w-auto'; const inputWrap = document.createElement('div'); inputWrap.className = 'relative flex-1 sm:w-52'; const searchIcon = document.createElement('span'); searchIcon.className = 'absolute inset-y-0 left-0 flex items-center pl-2.5 pointer-events-none text-slate-400 text-xs'; searchIcon.textContent = '🔍'; const sInput = document.createElement('input'); sInput.type = 'text'; sInput.placeholder = 'Buscar ID o palabra...'; sInput.value = ayDashboardSearchQuery; sInput.className = 'w-full pl-7 pr-3 py-1.5 text-xs bg-slate-50 hover:bg-white focus:bg-white border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500/20 focus:border-amber-500 transition-all text-slate-800 placeholder-slate-400'; sInput.onkeydown = (e) => { if (e.key === 'Enter') { e.preventDefault(); load(null, true, sInput.value.trim()); } }; inputWrap.append(searchIcon, sInput); const sBtn = document.createElement('button'); sBtn.type = 'button'; sBtn.textContent = 'Buscar'; sBtn.className = 'px-3.5 py-1.5 bg-[#172b41] hover:bg-slate-800 active:scale-95 text-white font-bold text-xs rounded-lg transition-all shadow-sm shrink-0'; sBtn.onclick = () => load(null, true, sInput.value.trim()); searchBox.append(inputWrap, sBtn); if (ayDashboardSearchQuery) { const clearBtn = document.createElement('button'); clearBtn.type = 'button'; clearBtn.title = 'Limpiar búsqueda'; clearBtn.className = 'px-2 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold text-xs rounded-lg transition-all border border-slate-200 shrink-0'; clearBtn.textContent = '✕'; clearBtn.onclick = () => { sInput.value = ''; load(null, true, ''); }; searchBox.append(clearBtn); } inner.append(leftGroup, searchBox); adminTabs.appendChild(inner); target.appendChild(adminTabs); } if(!data.avisos.length&&!cursor){ if(window.ayIsAdmin){ const emptyBox = el('div', undefined, 'py-4 space-y-3'); emptyBox.appendChild(el('p', ayDashboardFilterAll ? 'No se encontraron avisos con ese criterio de búsqueda.' : 'Todavía no tienes anuncios personales publicados con esta cuenta.', 'text-sm leading-relaxed text-gray-700')); if (!ayDashboardFilterAll) { const showAllNotice = btn('🌐 Ver todos los avisos del sitio para moderar', () => load(null, true, ''), '#b37d08'); showAllNotice.style.cssText = 'padding:8px 16px;font-size:12px;border-radius:8px;font-weight:bold;display:inline-block;cursor:pointer;'; emptyBox.appendChild(showAllNotice); } target.appendChild(emptyBox); } else { feedback('Todavía no tienes anuncios publicados con esta cuenta.'); } return; } for(const ad of data.avisos) target.appendChild(adCard(ad)); if(data.siguiente_cursor){const more=btn('Cargar más anuncios',()=>{more.remove();load(data.siguiente_cursor,ayDashboardFilterAll,ayDashboardSearchQuery)},'#0b111e');target.appendChild(more);} }catch(e){feedback('No se pudo cargar Mis Avisos: '+e.message);} } window.ayV2LoadMyAds=load; window.ayV2EditAdDirectly=edit; window.ayV2CallMisAvisos=call; window.ayV2MutationDirectly=mutation; const stripeReturn=new URLSearchParams(window.location.search).get('stripe'); if (stripeReturn==='procesando'||stripeReturn==='cancelado'){ const url=new URL(window.location.href);url.searchParams.delete('stripe'); window.history.replaceState({},'',url.pathname+url.search+url.hash); if(window.opener&&!window.opener.closed){ try{window.opener.postMessage({type:'ayv87-stripe-return',result:stripeReturn},window.location.origin);}catch(_){} // Return from Stripe only, not proof of a successful payment. document.body.style.display='none'; setTimeout(()=>{try{window.close();}catch(_){}},650); } else { setTimeout(()=>window.alert(stripeReturn==='procesando' ? 'Stripe regresó al sitio. Revisa Mis avisos: la publicación se activa solo cuando el pago está confirmado.' : 'El pago no se completó. Consulta Mis avisos para verificar el estado.'),300); } } })();

Vista previa de tu aviso

¡TU AVISO EN VIVO!
i

Así se verá tu publicación en el sitio. Esta vista previa no guarda ni publica el aviso; las fotografías se subirán cuando lo publiques.

Fotografía seleccionada del aviso 1 de 1

Título y descripción en la página principal

Más información

Categoría

Ubicación

Contacto