(function($){
'use strict';
if(window.AC_NONCE_FRESH){
if(typeof AC!=='undefined'){
AC.nonce=window.AC_NONCE_FRESH.nonce;
AC.ajax_url=window.AC_NONCE_FRESH.ajax_url;
}}
let pollTimer=null;
let lastMsgAt='';
let uploadedId=0;
const UI_LANG=String((window.AC&&AC.ui_lang)||'es').toLowerCase()==='en' ? 'en':'es';
const UI_LOCALE=UI_LANG==='en' ? 'en-US':'es-ES';
document.documentElement.setAttribute('lang', UI_LANG);
function t(es, en){
return UI_LANG==='en' ? en:es;
}
function escape_html(value){
return String(value===undefined||value===null ? '':value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function build_tab_url(tabKey, fallbackTab, args){
const safeTab=String(tabKey||'').replace(/[^a-z0-9_]/gi, '');
const key='tab_' + safeTab;
let base=(AC.portal_urls&&AC.portal_urls[key])||'';
if(!base){
base=(AC.portal_urls&&AC.portal_urls.mi_area_cliente)||((AC.home_url||'').replace(/\/+$/, '') + '/mi-area-cliente/');
if(fallbackTab){
base +=(base.indexOf('?') >=0 ? '&':'?') + 'tab=' + encodeURIComponent(fallbackTab);
}}
if(!args||typeof args!=='object') return base;
const pairs=[];
Object.keys(args).forEach(function(name){
const value=args[name];
if(value===undefined||value===null||value==='') return;
pairs.push(encodeURIComponent(name) + '=' + encodeURIComponent(String(value)));
});
if(!pairs.length) return base;
return base + (base.indexOf('?') >=0 ? '&':'?') + pairs.join('&');
}
function translate_static_ui(){
if(UI_LANG!=='en') return;
const map={
'Albera Clientes': 'Albera Clients',
'Notificaciones': 'Notifications',
'Cerrar sesión': 'Log out',
'Principal': 'Main',
'Inicio': 'Home',
'Mensajes': 'Messages',
'Presupuestos': 'Quotes',
'Proyectos': 'Projects',
'Solicitar': 'Request',
'Gestión': 'Management',
'Entregas': 'Deliveries',
'Facturas': 'Invoices',
'Incidencias': 'Issues',
'Feedback': 'Feedback',
'Cuenta': 'Account',
'Preferencias': 'Preferences',
'Mi historial': 'My history',
'Salir': 'Exit',
'Marcar todas como leídas': 'Mark all as read',
'Sin notificaciones.': 'No notifications.',
'Mensajes sin leer': 'Unread messages',
'Conversaciones activas': 'Active conversations',
'Presupuestos pendientes': 'Pending quotes',
'Proyectos activos': 'Active projects',
'Última conversación': 'Latest conversation',
'Responder →': 'Reply →',
'Presupuesto pendiente de aceptar': 'Quote pending approval',
'Aceptar': 'Accept',
'Rechazar': 'Reject',
'Progreso actual': 'Current progress',
'PM asignado': 'Assigned PM',
'Entrega prevista': 'Estimated delivery',
'Por definir': 'To be defined',
'Ver mis proyectos': 'View my projects',
'Contactar con el equipo': 'Contact the team',
'Asunto *': 'Subject *',
'Tipo de consulta': 'Query type',
'Mensaje *': 'Message *',
'Enviar →': 'Send →',
'Conversaciones': 'Conversations',
'Sin conversaciones todavía.': 'No conversations yet.',
'Escribe tu primera consulta y el equipo de Albera te responderá en breve.': 'Write your first request and the Albera team will reply shortly.',
'Inicia tu primera conversación': 'Start your first conversation',
'Cuéntanos tu consulta y te responderemos desde este mismo panel.': 'Tell us your request and we will reply from this same panel.',
'Inicia la conversación enviando un mensaje.': 'Start the conversation by sending a message.',
'Enviar': 'Send',
'Esta conversación está cerrada.': 'This conversation is closed.',
'Selecciona una conversación.': 'Select a conversation.',
'+ Solicitar nuevo presupuesto': '+ Request new quote',
'Presupuesto': 'Quote',
'Traducción': 'Translation',
'Traducción jurada': 'Sworn translation',
'Revisión': 'Revision',
'Proyectos en curso': 'Ongoing projects',
'Hablar con mi PM': 'Talk to my PM',
'Todavía no tienes proyectos en curso': 'You do not have any ongoing projects yet',
'Consultar al equipo': 'Contact the team',
'Incidencias / Reclamación': 'Issues / Claim',
'Incidencias / Reclamaciones': 'Issues / Claims',
'+ Nueva incidencia': '+ New issue',
'Nueva incidencia': 'New issue',
'Tipo de incidencia *': 'Issue type *',
'Descripción de la incidencia *': 'Issue description *',
'Abrir incidencia': 'Open issue',
'Cancelar': 'Cancel',
'Sin incidencias registradas': 'No issues registered',
'Respuesta del equipo': 'Team response',
'Enviar feedback': 'Send feedback',
'Tipo de feedback': 'Feedback type',
'Valoración (1–5 estrellas)': 'Rating (1–5 stars)',
'Comentario *': 'Comment *',
'Historial de feedback': 'Feedback history',
'Mis facturas': 'My invoices',
'Preferencias de traducción': 'Translation preferences',
'Estas preferencias se aplican a todos tus proyectos': 'These preferences apply to all your projects',
'Guardar preferencias': 'Save preferences',
'Mi historial': 'My history',
'Solicitud de revisión': 'Review request',
'Problema de calidad': 'Quality issue',
'Problema con la entrega': 'Delivery issue',
};
const map_attr={
'¿En qué podemos ayudarte?': 'How can we help you?',
'Describe tu consulta…': 'Describe your request…',
'Escribe tu mensaje…': 'Write your message…',
'¿Qué necesitas resolver?': 'What do you need to solve?',
'Describe el problema con el máximo detalle posible: qué ha ocurrido, en qué encargo, qué esperabas y qué has recibido…':
'Describe the issue in as much detail as possible: what happened, in which job, what you expected, and what you received…',
'Comparte tu experiencia, sugerencias o cualquier incidencia…':
'Share your experience, suggestions, or any issue…',
'Ej: Español latinoamericano, Inglés británico…':
'Example: Latin American Spanish, British English…',
'Términos específicos que debemos usar o evitar en tus traducciones…':
'Specific terms we should use or avoid in your translations…',
'Original → Traducción preferida (uno por línea)':
'Original → Preferred translation (one per line)',
'Cualquier indicación especial para el PM o los traductores…':
'Any special instructions for the PM or translators…',
};
$('.ac-wrap')
.find('*')
.contents()
.filter(function(){ return this.nodeType===3; })
.each(function(){
const original=this.nodeValue;
const trimmed=original.trim();
if(!trimmed||!Object.prototype.hasOwnProperty.call(map, trimmed)) return;
this.nodeValue=original.replace(trimmed, map[trimmed]);
});
$('[title]').each(function(){
const value=$(this).attr('title');
if(!value) return;
if(Object.prototype.hasOwnProperty.call(map, value)){
$(this).attr('title', map[value]);
}});
$('[placeholder]').each(function(){
const value=$(this).attr('placeholder');
if(!value) return;
if(Object.prototype.hasOwnProperty.call(map_attr, value)){
$(this).attr('placeholder', map_attr[value]);
}});
}
function serialize_form($form){
const data={};
$form.serializeArray().forEach(({ name, value })=> {
if(Object.prototype.hasOwnProperty.call(data, name)){
if(!Array.isArray(data[name])) data[name]=[data[name]];
data[name].push(value);
return;
}
data[name]=value;
});
return data;
}
function ac_post(action, data, $msg, onOk){
const $btn=$msg ? $msg.closest('form').find('[type=submit]'):null;
if($btn) $btn.prop('disabled', true);
if($msg) $msg.text('…').removeClass('ac-form-msg--success ac-form-msg--error');
$.post(AC.ajax_url, $.extend({ action, nonce: AC.nonce }, data), function(res){
if(res.success){
if($msg) $msg.text(res.data.message||'✓').addClass('ac-form-msg--success');
if(onOk) onOk(res.data);
}else{
const m=res.data?.message||'Error inesperado.';
if($msg) $msg.text(m).addClass('ac-form-msg--error');
else alert(m);
}}).fail(function(xhr){
var msg=t('Error de conexión.', 'Connection error.');
try { var d=JSON.parse(xhr.responseText); if(d&&d.data&&d.data.message) msg=d.data.message; } catch(e){}
if(xhr.status===0)   msg='Sin respuesta del servidor. Comprueba tu conexión.';
if(xhr.status===403) msg='Sesión expirada. Recarga la página e inténtalo de nuevo.';
if(xhr.status===413) msg='El archivo es demasiado grande para el servidor.';
if(xhr.status===500) msg='Error interno del servidor. Contacta con el equipo de Albera.';
if($msg) $msg.text(msg).addClass('ac-form-msg--error');
}).always(function(){
if($btn) $btn.prop('disabled', false);
});
}
function render_msg(msg){
const es_pm=msg.es_pm;
const cls=es_pm ? 'ac-msg--pm':'ac-msg--client';
const pm_tag=es_pm ? '<span class="ac-msg-pm-tag">Equipo Albera</span>':'';
const avatar=`<div class="ac-msg-avatar"><img src="https://www.gravatar.com/avatar/?d=mp&s=28" style="border-radius:50%;width:28px;height:28px"></div>`;
let adjuntos='';
if(msg.adjuntos&&msg.adjuntos.length){
adjuntos='<div class="ac-msg-adjuntos">' +
msg.adjuntos.map(a=> `<a href="${a.url}" target="_blank" class="ac-adj-link">📎 ${a.nombre_original}</a>`).join('') +
'</div>';
}
const body=`
<div class="ac-msg-body">
<div class="ac-msg-meta">
<strong>${msg.autor}</strong>
<span>${msg.fecha}</span>
${pm_tag}
</div>
<div class="ac-msg-text">${msg.cuerpo}</div>
${adjuntos}
</div>`;
return `<div class="ac-msg ${cls}" data-id="${msg.id}">${es_pm ? avatar+body:body+avatar}</div>`;
}
function scroll_bottom(){
const $msgs=$('#ac-messages');
if($msgs.length) $msgs.scrollTop($msgs[0].scrollHeight);
}
function init_chat(){
const $msgs=$('#ac-messages');
if(!$msgs.length) return;
lastMsgAt=$msgs.data('last')||'1970-01-01 00:00:00';
scroll_bottom();
start_poll();
}
function start_poll(){
if(pollTimer) clearInterval(pollTimer);
pollTimer=setInterval(poll_messages, AC.poll_interval);
}
function poll_messages(){
const $msgs=$('#ac-messages');
if(!$msgs.length) return;
const conv_id=$msgs.data('conv');
if(!conv_id) return;
$.post(AC.ajax_url, { action: 'ac_get_mensajes', nonce: AC.nonce, conversacion_id: conv_id, since_at: lastMsgAt }, function(res){
if(!res.success||!res.data.mensajes.length) return;
res.data.mensajes.forEach(msg=> {
if(!$(`#ac-messages [data-id="${msg.id}"]`).length){
$msgs.append(render_msg(msg));
lastMsgAt=msg.created_at;
}});
scroll_bottom();
if(res.data.unread!==undefined){
update_tab_badge(res.data.unread);
}});
}
function update_tab_badge(n){
const $nav=$('.ac-nav-item[data-tab="conversaciones"], .ac-nav-item[href*="tab=conversaciones"], .ac-nav-item[href*="cliente-conversaciones"]').first();
const $navBadge=$nav.find('.ac-nav-badge');
if($nav.length){
if(n > 0){
if($navBadge.length) $navBadge.text(n);
else $nav.append(`<span class="ac-nav-badge">${n}</span>`);
}else{
$navBadge.remove();
}
return;
}
const $tab=$('.ac-tab[href*="conversaciones"], .ac-tab[href*="cliente-conversaciones"]').first();
const $tabBadge=$tab.find('.ac-tab-badge');
if(!$tab.length) return;
if(n > 0){
if($tabBadge.length) $tabBadge.text(n);
else $tab.append(`<span class="ac-tab-badge">${n}</span>`);
}else{
$tabBadge.remove();
}}
$(document).on('change', '#ac-ui-lang', function(){
const $select=$(this);
const lang=String($select.val()||'').toLowerCase();
if(!lang||(lang!=='es'&&lang!=='en')) return;
$select.prop('disabled', true);
$.post(AC.ajax_url, {
action: 'ac_set_idioma_portal',
nonce: AC.nonce,
idioma: lang
}, function(res){
if(res&&res.success){
window.location.reload();
return;
}
alert((res&&res.data&&res.data.message) ? res.data.message:t('No se pudo actualizar el idioma.', 'Could not update the language.'));
$select.prop('disabled', false);
}).fail(function(){
alert(t('Error de conexión al cambiar el idioma.', 'Connection error while updating language.'));
$select.prop('disabled', false);
});
});
$(document).on('submit', '#ac-reply-form', function(e){
e.preventDefault();
const $form=$(this);
const $msg=$form.find('.ac-form-msg');
const cuerpo=$form.find('[name=cuerpo]').val().trim();
if(!cuerpo) return;
const data={
conversacion_id: $form.find('[name=conversacion_id]').val(),
cuerpo,
attachment_id: uploadedId||'',
};
ac_post('ac_enviar_mensaje', data, $msg, function(res){
$form.find('[name=cuerpo]').val('');
$('#ac-messages').append(render_msg(res.mensaje));
lastMsgAt=res.mensaje.created_at;
scroll_bottom();
uploadedId=0;
$('#ac-upload-preview').hide();
$('#ac-attachment-id').val('');
});
});
$(document).on('change', '#ac-file-input', function(){
const file=this.files[0];
if(!file) return;
const formData=new FormData();
formData.append('action', 'ac_upload_adjunto');
formData.append('nonce', AC.nonce);
formData.append('file', file);
$.ajax({ url: AC.ajax_url, type: 'POST', data: formData, processData: false, contentType: false,
success(res){
if(res.success){
uploadedId=res.data.attachment_id;
$('#ac-attachment-id').val(uploadedId);
$('#ac-upload-name').text(res.data.nombre);
$('#ac-upload-preview').show();
}else{ alert(res.data.message||'Error al subir el archivo.'); }},
error(xhr){
var msg='Error al subir el archivo.';
if(xhr.status===413) msg='El archivo es demasiado grande.';
if(xhr.status===0)   msg='Sin respuesta del servidor.';
alert(msg);
}});
$(this).val('');
});
$(document).on('click', '#ac-upload-remove', function(){
uploadedId=0;
$('#ac-attachment-id').val('');
$('#ac-upload-preview').hide();
});
$(document).on('submit', '#ac-nueva-conv-form', function(e){
e.preventDefault();
const $form=$(this);
const $msg=$form.find('.ac-form-msg');
ac_post('ac_nueva_conversacion', serialize_form($form), $msg, function(res){
setTimeout(function(){
window.location.href=build_tab_url('conversaciones', 'conversaciones', { conv: res.conversacion_id });
}, 800);
});
});
$(document).on('submit', '#ac-presupuesto-form', function(e){
e.preventDefault();
const $form=$(this);
const $msg=$form.find('.ac-form-msg');
ac_post('ac_solicitar_presupuesto', serialize_form($form), $msg, function(res){
setTimeout(function(){
const target=(AC.portal_urls&&AC.portal_urls.solicitar_presupuesto)
|| ((AC.home_url||'').replace(/\/+$/, '') + '/solicitar-presupuesto/');
window.location.href=target + '?id=' + res.presupuesto_id;
}, 900);
});
});
$(document).on('click', '.ac-aceptar-pres', function(){
if(!confirm(t('¿Aceptar este presupuesto?', 'Accept this quote?'))) return;
const id=$(this).data('id');
const $btn=$(this).prop('disabled', true);
ac_post('ac_aceptar_presupuesto', { id }, null, function(){
location.reload();
});
});
$(document).on('click', '.ac-rechazar-pres', function(){
if(!confirm(t('¿Rechazar este presupuesto?', 'Reject this quote?'))) return;
const id=$(this).data('id');
ac_post('ac_rechazar_presupuesto', { id }, null, function(){
location.reload();
});
});
$(document).on('submit', '#ac-feedback-form', function(e){
e.preventDefault();
const $form=$(this);
const $msg=$form.find('.ac-form-msg');
ac_post('ac_enviar_feedback', serialize_form($form), $msg, function(){
setTimeout(function(){ location.reload(); }, 900);
});
});
$(document).on('submit', '.ac-pm-feedback-form', function(e){
e.preventDefault();
const $form=$(this);
const $btn=$form.find('[type=submit]');
const $msg=$form.find('.ac-pm-form-msg');
const data=serialize_form($form);
const explicitId=parseInt(
String(
data.feedback_id
|| data.id
|| $form.find('input[name="feedback_id"]').val()
|| $form.find('input[name="id"]').val()
|| $form.attr('data-feedback-id')
|| ''
),
10
)||0;
if(explicitId > 0){
data.id=explicitId;
data.feedback_id=explicitId;
data.incidencia_id=explicitId;
}
$btn.prop('disabled', true);
$msg.text('Guardando...').removeClass('is-success is-error');
$.post(AC.ajax_url, $.extend({ action: 'ac_pm_responder_feedback', nonce: AC.nonce }, data), function(res){
if(res.success){
$msg.text(res.data?.message||'Respuesta guardada.').addClass('is-success');
setTimeout(function(){ location.reload(); }, 700);
return;
}
$msg.text(res.data?.message||'Error inesperado.').addClass('is-error');
}).fail(function(xhr){
var msg=t('Error de conexión.', 'Connection error.');
try {
var d=JSON.parse(xhr.responseText);
if(d&&d.data&&d.data.message) msg=d.data.message;
} catch (err){}
if(xhr.status===0)   msg='Sin respuesta del servidor.';
if(xhr.status===403) msg='Sesión expirada. Recarga la página e inténtalo de nuevo.';
if(xhr.status===500) msg='Error interno del servidor.';
$msg.text(msg).addClass('is-error');
}).always(function(){
$btn.prop('disabled', false);
});
});
$(document).on('click', '[data-ac-service]', function(e){
e.preventDefault();
const $btn=$(this);
const service=String($btn.data('acService')||'').trim();
if(!service) return;
const $scope=$btn.closest('form').length ? $btn.closest('form'):$(document);
const $select=$scope.find('select[name="servicio"]').first();
if(!$select.length) return;
$select.val(service).trigger('change');
$('[data-ac-service]').removeClass('is-active');
$btn.addClass('is-active');
});
function submit_pm_project($form){
const $btn=$form.find('[type=submit]');
const $msg=$form.find('.ac-pm-form-msg');
const data=serialize_form($form);
$btn.prop('disabled', true);
$msg.text('Guardando...').removeClass('is-success is-error');
$.post(AC.ajax_url, $.extend({ action: 'ac_pm_guardar_proyecto', nonce: AC.nonce }, data), function(res){
if(res.success){
$msg.text(res.data?.message||'Proyecto guardado.').addClass('is-success');
setTimeout(function(){ location.reload(); }, 700);
return;
}
$msg.text(res.data?.message||'No se pudo guardar el proyecto.').addClass('is-error');
}).fail(function(xhr){
var msg=t('Error de conexión.', 'Connection error.');
try {
var d=JSON.parse(xhr.responseText);
if(d&&d.data&&d.data.message) msg=d.data.message;
} catch (err){}
if(xhr.status===0)   msg='Sin respuesta del servidor.';
if(xhr.status===403) msg='No tienes permiso para gestionar este proyecto.';
if(xhr.status===500) msg='Error interno del servidor.';
$msg.text(msg).addClass('is-error');
}).always(function(){
$btn.prop('disabled', false);
});
}
$(document).on('submit', '#ac-pm-proyecto-form', function(e){
e.preventDefault();
submit_pm_project($(this));
});
$(document).on('submit', '.ac-pm-project-update-form, .ac-pm-proyecto-update-form', function(e){
e.preventDefault();
submit_pm_project($(this));
});
$(document).on('mouseenter', '.ac-star', function(){
const val=parseInt($(this).data('val'));
$('#ac-stars .ac-star').each(function(){
$(this).toggleClass('hover', parseInt($(this).data('val')) <=val);
});
}).on('mouseleave', '#ac-stars', function(){
const selected=parseInt($('#ac-valoracion-input').val())||0;
$('#ac-stars .ac-star').each(function(){
$(this).removeClass('hover');
$(this).toggleClass('active', parseInt($(this).data('val')) <=selected);
});
}).on('click', '.ac-star', function(){
const val=parseInt($(this).data('val'));
$('#ac-valoracion-input').val(val);
$('#ac-stars .ac-star').each(function(){
$(this).toggleClass('active', parseInt($(this).data('val')) <=val);
});
});
$(document).on('click', '#ac-notif-toggle', function(e){
e.stopPropagation();
$('#ac-notif-panel').toggle();
});
$(document).on('click', function(e){
if(!$(e.target).closest('#ac-notif-panel,#ac-notif-toggle').length){
$('#ac-notif-panel').hide();
}});
$(document).on('click', '#ac-mark-all-read', function(){
ac_post('ac_marcar_leidas', {}, null, function(){
$('.ac-notif-item--unread').removeClass('ac-notif-item--unread');
const $badge=$('.ac-bell-badge');
$badge.text('0').hide();
});
});
$(document).on('click', '.ac-notif-item', function(){
const id=$(this).data('id');
if(id) ac_post('ac_marcar_leidas', { id }, null);
});
function poll_notifs(){
$.post(AC.ajax_url, { action: 'ac_get_notificaciones', nonce: AC.nonce }, function(res){
if(!res||!res.success) return;
const $badge=$('.ac-bell-badge');
if(res.data.unread > 0){
if($badge.length) $badge.text(res.data.unread).show();
else $('#ac-notif-toggle').append(`<span class="ac-bell-badge">${res.data.unread}</span>`);
}else{
$badge.hide();
}});
}
setInterval(poll_notifs, 30000);
$(function(){
translate_static_ui();
init_chat();
});
function render_entregas(entregas){
const $el=$('#ac-entregas-list');
if(!entregas||!entregas.length){
$el.html(`<p style="color:#9ca3af;text-align:center;padding:2rem">${t('No hay entregas todavía.', 'There are no deliveries yet.')}</p>`);
return;
}
const estados=UI_LANG==='en'
? { ES:'Spanish',EN:'English',FR:'French',DE:'German',IT:'Italian',PT:'Portuguese' }
: { ES:'Español',EN:'Inglés',FR:'Francés',DE:'Alemán',IT:'Italiano',PT:'Portugués' };
let html='';
entregas.forEach(function(e){
const idiomas=(e.idioma_origen&&e.idioma_destino)
? (estados[e.idioma_origen]||e.idioma_origen) + ' → ' + (estados[e.idioma_destino]||e.idioma_destino)
: t('Entrega disponible', 'Delivery available');
const fecha=new Date(e.fecha_entrega).toLocaleDateString(UI_LOCALE, {day:'2-digit',month:'short',year:'numeric'});
const confirmada=parseInt(e.confirmada)===1;
html +=`
<div class="ac-card" style="margin-bottom:.75rem;padding:1rem 1.25rem">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:1rem">
<div>
<div style="font-weight:700;color:#0d1f35;margin-bottom:3px">
📦 ${e.nombre_archivo||(UI_LANG==='en' ? 'Delivery #':'Entrega #') + e.id}
</div>
<div style="font-size:.8rem;color:#9ca3af">
${idiomas} · v${e.version} · ${fecha}
</div>
${e.notas ? `<p style="font-size:.85rem;color:#374151;margin-top:.4rem">${e.notas}</p>`:''}
</div>
<div style="text-align:right;flex-shrink:0">
${e.descarga_url ? `<a href="${e.descarga_url}" class="ac-btn ac-btn--sm" style="display:inline-block;margin-bottom:.4rem">${t('Descargar', 'Download')}</a><br>`:''}
${confirmada
? `<span style="color:#22C55E;font-size:.8rem;font-weight:700">✅ ${t('Confirmada', 'Confirmed')}</span>`
: `<button class="ac-btn ac-btn--sm ac-confirmar-entrega" data-id="${e.id}">${t('Confirmar recepción', 'Confirm receipt')}</button>`
}
</div>
</div>
</div>`;
});
$el.html(html);
}
if($('#ac-entregas-list').length){
$.post(AC.ajax_url, {action:'ac_get_entregas',nonce:AC.nonce}, function(r){if(r.success) render_entregas(r.data);});
}
$(document).on('click', '.ac-confirmar-entrega', function(){
const id=$(this).data('id');
if(!confirm(t('¿Confirmar la recepción de esta entrega?', 'Confirm the receipt of this delivery?'))) return;
ac_post('ac_confirmar_entrega', { id }, null, function(){
$.post(AC.ajax_url, {action:'ac_get_entregas',nonce:AC.nonce}, function(r){if(r.success) render_entregas(r.data);});
});
});
function render_facturas(facturas){
const $el=$('#ac-facturas-list');
if(!facturas||!facturas.length){
$el.html(`<p style="color:#9ca3af;text-align:center;padding:2rem">${t('No hay facturas todavía.', 'There are no invoices yet.')}</p>`);
return;
}
const estados=UI_LANG==='en'
? {borrador:'Draft',emitida:'Issued',pagada:'Paid',vencida:'Overdue ⚠',cancelada:'Cancelled'}
: {borrador:'Borrador',emitida:'Emitida',pagada:'Pagada',vencida:'Vencida ⚠',cancelada:'Cancelada'};
let html='';
facturas.forEach(function(f){
const est=estados[f.estado]||f.estado;
const emitida=f.emitida ? new Date(f.emitida).toLocaleDateString(UI_LOCALE):'-';
const vence=f.vencimiento ? new Date(f.vencimiento).toLocaleDateString(UI_LOCALE):'-';
const estadoClass='ac-badge--' + String(f.estado||'borrador');
html +=`
<article class="ac-card ac-invoice-card">
<div class="ac-invoice-card__head">
<div>
<div class="ac-invoice-card__ref">${escape_html(f.ref||('FAC-' + f.id))}</div>
<h3 class="ac-invoice-card__title">${escape_html(f.concepto||t('Factura de servicio', 'Service invoice'))}</h3>
<p class="ac-invoice-card__meta">
${t('Emitida', 'Issued')}: ${emitida} · ${t('Vencimiento', 'Due date')}: ${vence}
</p>
</div>
<div class="ac-invoice-card__aside">
<div class="ac-invoice-card__amount">
€${parseFloat(f.total||0).toLocaleString(UI_LOCALE,{minimumFractionDigits:2,maximumFractionDigits:2})}
</div>
<span class="ac-badge ${estadoClass}">${escape_html(est)}</span>
<a href="${AC.ajax_url}?action=ac_pdf_factura&id=${encodeURIComponent(f.id)}&nonce=${encodeURIComponent(AC.nonce)}"
target="_blank" rel="noopener" class="ac-invoice-card__pdf">
🖨 ${t('Descargar PDF', 'Download PDF')}
</a>
</div>
</div>
</article>`;
});
$el.html(html);
}
if($('#ac-facturas-list').length){
$.post(AC.ajax_url, {action:'ac_get_facturas',nonce:AC.nonce}, function(r){if(r.success) render_facturas(r.data);});
}
if($('#ac-form-preferencias').length){
$.post(AC.ajax_url, {action:'ac_get_preferencias',nonce:AC.nonce}, function(r){
const data=r.success ? r.data:null;
if(!data) return;
['idioma_origen','idioma_destino','variante_regional','estilo',
'terminologia','glosario','notas_adicionales'].forEach(function(k){
const $el=$('[name="' + k + '"]');
if($el.length&&data[k]) $el.val(data[k]);
});
});
}
$(document).on('submit', '#ac-form-preferencias', function(e){
e.preventDefault();
const $msg=$('#ac-pref-msg');
const data={};
$(this).serializeArray().forEach(function(f){ data[f.name]=f.value; });
ac_post('ac_update_preferencias', data, $msg, function(){
$msg.text(t('✅ Preferencias guardadas correctamente.', '✅ Preferences saved successfully.')).css('color','#22c55e');
setTimeout(function(){ $msg.text(''); }, 3000);
});
});
function render_historial(data){
const $el=$('#ac-historial-panel');
if(!data){ $el.html(`<p style="color:#ef4444">${t('Error al cargar.', 'Error loading data.')}</p>`); return; }
$el.html(`
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:1rem;margin-bottom:1.5rem">
${[
[t('Total presupuestos', 'Total quotes'), data.presupuestos_total, '#D4A853'],
[t('Encargos aceptados', 'Accepted jobs'), data.presupuestos_aceptados, '#22C55E'],
[t('Facturación total', 'Total invoiced'), '€'+parseFloat(data.importe_total).toLocaleString(UI_LOCALE,{minimumFractionDigits:2}), '#6366F1'],
[t('Total pagado', 'Total paid'), '€'+parseFloat(data.importe_pagado).toLocaleString(UI_LOCALE,{minimumFractionDigits:2}), '#0D9488'],
].map(([label,val,color])=> `
<div style="background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:1rem;border-top:3px solid ${color}">
<div style="font-size:1.5rem;font-weight:900;color:#111">${val}</div>
<div style="font-size:.7rem;font-weight:700;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;margin-top:4px">${label}</div>
</div>`).join('')
}
</div>
${data.pares_linguisticos&&Object.keys(data.pares_linguisticos).length ? `
<div class="ac-card" style="padding:1rem 1.25rem">
<h3 style="font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;color:#9ca3af;margin-bottom:.75rem">${t('Pares lingüísticos utilizados', 'Language pairs used')}</h3>
${Object.entries(data.pares_linguisticos).map(([par,n])=> `
<div style="display:flex;justify-content:space-between;padding:.4rem 0;border-bottom:1px solid #f5f0e8;font-size:.9rem">
<span style="font-family:monospace;font-weight:700;color:#c9842a">${par}</span>
<span style="font-size:.8rem;color:#9ca3af">${n} ${t('encargo', 'job')}${n>1?'s':''}</span>
</div>`).join('')}
</div>`:''}
`);
}
if($('#ac-historial-panel').length){
$.post(AC.ajax_url, {action:'ac_get_historial',nonce:AC.nonce}, function(r){if(r.success) render_historial(r.data);});
}
$(document).on('click', '#ac-btn-nueva-incidencia', function(){
$('#ac-form-incidencia-wrap').slideDown(200);
$(this).hide();
$('html, body').animate({ scrollTop: $('#ac-form-incidencia-wrap').offset().top - 80 }, 300);
});
$(document).on('click', '#ac-btn-cancelar-incidencia', function(){
$('#ac-form-incidencia-wrap').slideUp(200);
$('#ac-btn-nueva-incidencia').show();
$('#ac-incidencia-form')[0].reset();
});
$(document).on('submit', '#ac-incidencia-form', function(e){
e.preventDefault();
var $form=$(this);
var $msg=$form.find('.ac-form-msg');
var $btn=$form.find('[type=submit]');
$btn.prop('disabled', true).text(t('Enviando…', 'Sending…'));
$msg.text('').removeClass('ac-form-msg--success ac-form-msg--error');
var data=$form.serializeArray().reduce(function(obj, item){
obj[item.name]=item.value;
return obj;
}, {});
$.post(AC.ajax_url, {
action:    'ac_enviar_feedback',
nonce:     AC.nonce,
tipo:      data.tipo||'incidencia',
comentario: data.comentario||'',
valoracion: 0
}, function(res){
if(res.success){
$msg.text(t('✅ Incidencia abierta. El equipo te responderá pronto.', '✅ Issue opened. The team will reply soon.'))
.addClass('ac-form-msg--success');
$form[0].reset();
setTimeout(function(){
location.reload();
}, 2000);
}else{
var m=res.data&&res.data.message ? res.data.message:t('Error al enviar.', 'Error while sending.');
$msg.text('⚠ ' + m).addClass('ac-form-msg--error');
$btn.prop('disabled', false).text(t('Abrir incidencia', 'Open issue'));
}}).fail(function(){
$msg.text('⚠ ' + t('Error de conexión. Inténtalo de nuevo.', 'Connection error. Please try again.')).addClass('ac-form-msg--error');
$btn.prop('disabled', false).text(t('Abrir incidencia', 'Open issue'));
});
});
})(jQuery);