-
+
-
-
${company}
-
${name}
-
- ${addr}, ${pop}
-
-
-
-
+
+
+
`;
@@ -536,6 +530,34 @@
lucide.createIcons();
}
+ async function markClientNotFound(serviceId, currentTimes) {
+ const btn = event.currentTarget;
+ const originalHtml = btn.innerHTML;
+ btn.innerHTML = `
Enviando...`;
+ btn.disabled = true;
+
+ try {
+ const res = await fetch(`${API_URL}/services/not-found/${serviceId}`, {
+ method: 'POST',
+ headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
+ });
+
+ const data = await res.json();
+ if (data.ok) {
+ showToast(`Aviso enviado. Intentos: ${data.called_times}`);
+ refreshData(); // Recargamos para actualizar el numerito
+ } else {
+ alert("Error al registrar la llamada.");
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ }
+ } catch (e) {
+ alert("Error de conexión");
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ }
+ }
+
// ==========================================
// LÓGICA DEL MOTOR DE AGENDAMIENTO INTELIGENTE
// ==========================================
@@ -546,17 +568,8 @@
return `${y}-${m}-${d}`;
}
- function timeToMins(t) {
- let [h, m] = t.split(':').map(Number);
- return h * 60 + m;
- }
-
- function minsToTime(m) {
- let h = Math.floor(m / 60).toString().padStart(2, '0');
- let min = (m % 60).toString().padStart(2, '0');
- return `${h}:${min}`;
- }
-
+ function timeToMins(t) { let [h, m] = t.split(':').map(Number); return h * 60 + m; }
+ function minsToTime(m) { let h = Math.floor(m / 60).toString().padStart(2, '0'); let min = (m % 60).toString().padStart(2, '0'); return `${h}:${min}`; }
function addOneHour(timeStr) {
if(!timeStr) return "";
let [h, m] = timeStr.split(':').map(Number);
@@ -565,7 +578,6 @@
let newM = totalMins % 60;
return `${String(newH).padStart(2,'0')}:${String(newM).padStart(2,'0')}`;
}
-
function formatDate(dateStr) {
if (!dateStr) return "";
try {
@@ -582,19 +594,16 @@
container.innerHTML = "";
let today = new Date();
- for (let i = 0; i < 14; i++) { // 14 días a futuro
+ for (let i = 0; i < 14; i++) {
let d = new Date(today);
d.setDate(today.getDate() + i);
-
- if (d.getDay() === 0) continue; // Saltar domingos
+ if (d.getDay() === 0) continue;
const isoDate = toISODate(d);
const dayName = d.toLocaleDateString('es-ES', { weekday: 'short' }).replace('.', '').substring(0,3);
const dayNum = d.getDate();
- // Seleccionar hoy por defecto
if (!pickerSelectedDate) pickerSelectedDate = isoDate;
-
const isSelected = isoDate === pickerSelectedDate;
container.innerHTML += `
@@ -608,15 +617,15 @@
function selectPickerDate(isoDate) {
pickerSelectedDate = isoDate;
- pickerSelectedTime = ""; // Resetear hora al cambiar de día
- buildDayCarousel(); // Repintar para actualizar estilos
+ pickerSelectedTime = "";
+ buildDayCarousel();
renderTimeSlots();
checkSaveButton();
}
function selectPickerTime(timeStr) {
pickerSelectedTime = timeStr;
- renderTimeSlots(); // Repintar para estilos
+ renderTimeSlots();
checkSaveButton();
}
@@ -648,7 +657,6 @@
function isOverlapping(startMins, endMins, occupiedRanges) {
for(let r of occupiedRanges) {
- // Hay solapamiento si mi inicio es antes de que él acabe, Y mi fin es después de que él empiece
if(startMins < r.end && endMins > r.start) return true;
}
return false;
@@ -663,12 +671,10 @@
grid.innerHTML = "";
let slotsGenerated = 0;
- // Función interna para recorrer tramos
const genSlotsForPeriod = (startStr, endStr) => {
let startMins = timeToMins(startStr);
let endMins = timeToMins(endStr);
- // Saltos de 30 minutos (igual que en buscar.html)
for (let m = startMins; m + duration <= endMins; m += 30) {
if (!isOverlapping(m, m + duration, occupied)) {
const timeStr = minsToTime(m);
@@ -683,20 +689,18 @@
}
};
- // Generar Mañana y Tarde
genSlotsForPeriod(bizHours.m_start, bizHours.m_end);
genSlotsForPeriod(bizHours.a_start, bizHours.a_end);
if (slotsGenerated === 0) {
msg.classList.remove('hidden');
grid.classList.add('hidden');
- pickerSelectedTime = ""; // Reset forzado
+ pickerSelectedTime = "";
} else {
msg.classList.add('hidden');
grid.classList.remove('hidden');
}
- // Si la hora que estaba seleccionada ya no sale, la borramos
if (pickerSelectedTime && !grid.innerHTML.includes(`'${pickerSelectedTime}'`)) {
pickerSelectedTime = "";
}
@@ -704,9 +708,6 @@
checkSaveButton();
}
- // ==========================================
- // APERTURA DE MODAL Y GUARDADO (MANUAL)
- // ==========================================
function openActionModal(id) {
const s = localServices.find(x => x.id === id);
if (!s) return;
@@ -721,17 +722,16 @@
const matchPhone = rawPhone.toString().match(/[6789]\d{8}/);
document.getElementById('detPhoneRaw').value = matchPhone ? matchPhone[0] : "";
- // INICIALIZAR EL AGENDADOR
document.getElementById('durationInput').value = "60";
- pickerSelectedDate = ""; // Reset
- pickerSelectedTime = ""; // Reset
- buildDayCarousel(); // Esto selecciona 'hoy' automáticamente y pinta los huecos
+ pickerSelectedDate = "";
+ pickerSelectedTime = "";
+ buildDayCarousel();
checkSaveButton();
const modal = document.getElementById('actionModal');
const content = document.getElementById('modalContent');
modal.classList.remove('hidden');
- void modal.offsetWidth; // Reflow
+ void modal.offsetWidth;
modal.classList.remove('opacity-0');
content.classList.remove('translate-y-full');
}
@@ -739,10 +739,8 @@
function closeModal(modalId, contentId) {
const modal = document.getElementById(modalId);
const content = document.getElementById(contentId);
-
modal.classList.add('opacity-0');
content.classList.add('translate-y-full');
-
setTimeout(() => { modal.classList.add('hidden'); }, 300);
}