import { Controller } from 'stimulus'; const helper = require("./../components/helper.js"); const record = require("./../components/record.js"); /* * This is an example Stimulus controller! * * Any element with a data-controller="checSchedule" attribute will cause * this controller to be executed. The name "hello" comes from the filename: * calendar_checkSchedule.js -> "checSchedule" * * Delete this file or adapt it for your use! */ export default class extends Controller { getClinicPhone() { // primary source: menu_controller.js tel() sets window.clinicPhone if (typeof window !== 'undefined' && window.clinicPhone) { return String(window.clinicPhone).trim(); } // fallback: read what tel() puts into the header button const el = document.getElementById('btn-callback-clinic'); const phone = el ? (el.textContent || '').trim() : ''; return phone || ''; } setTitleWithPhone(titleEl) { if (!titleEl) return; const phone = this.getClinicPhone(); const phoneText = phone ? phone : '______'; titleEl.textContent = `К этому специалисту возможна запись по тел.${phoneText} или через кнопку «записаться».`; // If phone not ready yet, retry briefly (tel() is async). if (!phone) { let tries = 0; const timer = setInterval(() => { tries++; const p = this.getClinicPhone(); if (p) { titleEl.textContent = `К этому специалисту возможна запись по тел.${p} или через кнопку «записаться».`; clearInterval(timer); } else if (tries >= 20) { clearInterval(timer); } }, 250); } } connect() { let run = function (intervalBlock, id, filialName) { this.show(intervalBlock, id, filialName) }.bind(this) // Функция для получения intervalBlock по текущему data-id const getIntervalBlock = () => { return document.getElementById(this.element.dataset.id); }; var intervalBlock = getIntervalBlock(); if (!intervalBlock) { return; // Если блок не найден, выходим } var wrap = intervalBlock.querySelector('.intervals-wrap'); wrap.classList.remove('grid-list'); wrap.classList.add('grid-none'); wrap.innerHTML = "Идет загрузка..."; var select = this.element.querySelector('.select-schedule'); if (select) { // Получаем значение department из URL параметра specialist_search[department] const urlParams = new URLSearchParams(window.location.search); const departmentParam = urlParams.get('specialist_search[department]') || urlParams.get('specialist_search%5Bdepartment%5D'); let optionFound = false; // Если есть параметр department в URL, выбираем соответствующую опцию if (departmentParam) { const departmentId = parseInt(departmentParam); for (let i = 0; i < select.options.length; i++) { const optionValue = select.options[i].value; // Формат значения: dcode:filial:department:onlineMode:infoclinica const parts = optionValue.split(':'); if (parts.length >= 3 && parseInt(parts[2]) === departmentId) { select.value = optionValue; optionFound = true; // Обновляем data-id родительского элемента в формате dcode:onlineMode:infoclinica if (parts.length >= 5) { this.element.dataset.id = `${parts[0]}:${parts[3]}:${parts[4]}`; // Обновляем ссылку на intervalBlock после изменения data-id intervalBlock = getIntervalBlock(); if (intervalBlock) { wrap = intervalBlock.querySelector('.intervals-wrap'); } } // Обновляем bootstrap-select если он используется // Используем setTimeout чтобы дать время bootstrap-select инициализироваться setTimeout(() => { if (typeof $ !== 'undefined' && $(select).data('selectpicker')) { $(select).selectpicker('refresh'); } }, 100); // Запускаем загрузку расписания для выбранной опции if (intervalBlock) { run(intervalBlock, optionValue, select.options[i].text); } break; } } } // Если параметра нет или опция не найдена, используем дефолтное поведение if (!optionFound) { run(intervalBlock, false, false); } const controllerElement = this.element; // Сохраняем ссылку на элемент контроллера select.addEventListener('change', function() { // Обновляем data-id родительского элемента при изменении селекта const optionValue = this.value; const parts = optionValue.split(':'); // Формат значения: dcode:filial:department:onlineMode:infoclinica // Формат data-id: dcode:onlineMode:infoclinica if (parts.length >= 5) { controllerElement.dataset.id = `${parts[0]}:${parts[3]}:${parts[4]}`; // Обновляем ссылку на intervalBlock после изменения data-id const newIntervalBlock = document.getElementById(controllerElement.dataset.id); if (newIntervalBlock) { intervalBlock = newIntervalBlock; wrap = intervalBlock.querySelector('.intervals-wrap'); } } wrap.classList.remove('grid-list'); wrap.classList.add('grid-none'); wrap.innerHTML = "Идет загрузка..."; run(intervalBlock, optionValue, select.options[select.selectedIndex].text) }); } else { // Если селекта нет, используем дефолтное поведение run(intervalBlock, false, false); } } show(intervalBlock, id, filialName) { // Внутри intervalBlock должны быть: // - .intervals-wrap (контейнер для интервалов/кнопки) // - .show-specialist-detail (кнопка "Все даты" с dataset для /api/interval) // Раньше при пустом расписании мы затирали intervalBlock.innerHTML, // из-за чего при смене клиники селектом пропадал btn и падало на btn.dataset. // Теперь никогда не удаляем intervalBlock целиком. let wrap = intervalBlock.querySelector('.intervals-wrap'); if (!wrap) { wrap = document.createElement('div'); wrap.className = 'intervals-wrap'; intervalBlock.prepend(wrap); } const btn = intervalBlock.querySelector('.show-specialist-detail'); // Кешируем dataset на самом intervalBlock, чтобы можно было работать даже если btn временно скрыт // data-docName в HTML даёт dataset.docName (camelCase), читаем оба варианта if (btn) { intervalBlock.dataset.specialistid = btn.dataset.specialistid || intervalBlock.dataset.specialistid || ''; intervalBlock.dataset.departmentid = btn.dataset.departmentid || intervalBlock.dataset.departmentid || ''; intervalBlock.dataset.filialid = btn.dataset.filialid || intervalBlock.dataset.filialid || ''; intervalBlock.dataset.onlinemode = btn.dataset.onlinemode || intervalBlock.dataset.onlinemode || ''; intervalBlock.dataset.address = btn.dataset.address || intervalBlock.dataset.address || ''; intervalBlock.dataset.company = btn.dataset.company || intervalBlock.dataset.company || ''; intervalBlock.dataset.comment = btn.dataset.comment || intervalBlock.dataset.comment || ''; intervalBlock.dataset.docname = (btn.dataset.docName || btn.dataset.docname || intervalBlock.dataset.docname || '').toString(); } // Ищем элементы заголовка/даты строго внутри текущей карточки, // чтобы не зависеть от хрупких parentElement-цепочек. const timeList = intervalBlock.closest('.time-list'); const titleEl = timeList ? timeList.querySelector('.time-list__title') : null; // Если в заголовке ранее был текст для "записаться", возвращаем стандартный вид перед загрузкой расписания if (titleEl && !titleEl.querySelector('.cdate')) { titleEl.innerHTML = 'Удобное время для записи: загружается'; } const cdate = titleEl ? titleEl.querySelector('.cdate') : null; const ds = intervalBlock.dataset || {}; if (!ds.specialistid || !ds.departmentid || !ds.filialid) { // Нет данных для запроса расписания (защитимся от падения) return; } var data = { 'update' : true, 'doctor': ds.specialistid, 'department': ds.departmentid, 'filial': ds.filialid, 'startInterval': document.querySelector('.specialist-items').dataset.st, 'endInterval': document.querySelector('.specialist-items').dataset.en, 'onlineMode': ds.onlinemode }; if (id) { var params = id.split(":"); data.doctor = params[0]; data.filial = params[1]; data.department = params[2]; data.onlineMode = params[3]; if (btn) { btn.dataset.specialistid = params[0]; btn.dataset.filialid = params[1]; btn.dataset.departmentid = params[2]; btn.dataset.onlinemode = params[3]; } intervalBlock.dataset.specialistid = params[0]; intervalBlock.dataset.filialid = params[1]; intervalBlock.dataset.departmentid = params[2]; intervalBlock.dataset.onlinemode = params[3]; } if (filialName) { if (btn) { btn.dataset.address = filialName; } intervalBlock.dataset.address = filialName; } helper.sendRequest(data, "/api/interval", "GET").then((resolve) => { var isNotFree = true; // если ранее скрывали кнопку "Все даты" — возвращаем обратно при любом новом запросе if (btn) { btn.classList.remove('d-none'); } intervalBlock.classList.remove('space-between'); wrap.classList.add('grid-list'); wrap.classList.remove('grid-none'); wrap.innerHTML = ""; var i = 0; if (cdate && cdate.dataset && cdate.dataset.nearestDate) { delete cdate.dataset.nearestDate; } // Проверяем, есть ли данные в ответе if (!resolve.data.intervalsData || resolve.data.intervalsData.length === 0) { isNotFree = true; } else { resolve.data.intervalsData.forEach(function(el) { if (el.isFree == true) { var cDate = new Date(el.workDate).toLocaleString('ru', {year: 'numeric',month: 'numeric',day: 'numeric'}); var nearestDate = cdate && cdate.dataset ? cdate.dataset.nearestDate : undefined; if (i < 6) { isNotFree = false; for (let [key, item] of Object.entries(el.intervals)) { if (item.isFree == true && i < 6 && typeof nearestDate === 'undefined') { if (i == 0) { var spanCurrentDate = document.createElement('span'); spanCurrentDate.innerText = cDate; if (cdate) { cdate.innerText = cDate; cdate.dataset.nearestDate = item.nearestDate; } } var spanInterval = document.createElement('span'); spanInterval.classList = 'time available text-center'; spanInterval.innerText = item.startTime; spanInterval.dataset.workDate = item.workDate; spanInterval.dataset.schedident = item.schedident; spanInterval.dataset.time = item.time; spanInterval.dataset.onlinemode = item.onlineMode; spanInterval.dataset.rnum = item.rNum; spanInterval.dataset.specialistid = intervalBlock.dataset.specialistid || btn.dataset.specialistid; spanInterval.dataset.filialid = intervalBlock.dataset.filialid || btn.dataset.filialid; spanInterval.dataset.department = btn.dataset.departmentid; spanInterval.dataset.company = intervalBlock.dataset.company || btn.dataset.company || ''; spanInterval.dataset.comment = intervalBlock.dataset.comment || btn.dataset.comment || ''; spanInterval.dataset.address = intervalBlock.dataset.address || btn.dataset.address || ''; spanInterval.dataset.docname = (intervalBlock.dataset.docname || btn.dataset.docName || btn.dataset.docname || '').toString(); spanInterval.addEventListener('click', function (evn) { var popupWrap = document.getElementById('popup'); evn.target.dataset.onlinemode = item.onlineMode; if (record.renderFormRecord(resolve.data.userInfo, evn.target.dataset)) { if (resolve.data.userInfo) { evn.target.innerHTML = ''; record.sendReserve(popupWrap) } else { $(popupWrap).modal('show'); } } }) wrap.append(spanInterval); i++; } } } } }); } if (isNotFree) { // Если расписание пустое, показываем кнопку "Записаться" как при specialist.infoclinica == false intervalBlock.classList.add('space-between'); // прячем "Все даты", но не удаляем (нужно для переключения селекта) if (btn) { btn.classList.add('d-none'); } var button = document.createElement('button'); // Используем правильные имена атрибутов в camelCase для dataset // data-docName -> dataset.docName (не docname!) var docName = btn && btn.dataset.docName ? btn.dataset.docName : ''; var address = btn && btn.dataset.address ? btn.dataset.address : 'null'; var company = btn && btn.dataset.company ? btn.dataset.company : 'null'; var comment = btn && btn.dataset.comment ? btn.dataset.comment : ''; button.setAttribute('data-docName', docName); button.setAttribute('data-address', address); button.setAttribute('data-company', company); button.setAttribute('data-comment', comment); button.className = 'btn-show-specialist-detail'; button.type = 'button'; button.setAttribute('data-controller', 'uslugi'); button.innerText = 'Записаться'; // кладём кнопку в wrap, чтобы не ломать структуру блока wrap.classList.remove('grid-list'); wrap.classList.add('grid-none'); wrap.innerHTML = ''; wrap.appendChild(button); // И если мы показываем кнопку, то заголовок должен быть с телефоном/подсказкой this.setTitleWithPhone(titleEl); } }); } }