Files
cabinet/assets/controllers/checkSchedule_controller.js
T
2026-05-28 12:09:28 +03:00

346 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
});
}
}