346 lines
18 KiB
JavaScript
346 lines
18 KiB
JavaScript
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 = 'Удобное время для записи: <span class="cdate">загружается</span>';
|
||
}
|
||
|
||
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 = '<img width="20" src="/images/eclipse.gif">';
|
||
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);
|
||
}
|
||
|
||
});
|
||
}
|
||
}
|