Выберите дату и время, чтобы увидеть ближайшее событие
--:--
Редактирование доступно только через кнопку-карандаш на карточке
—
Москва - Монтреал
—°
Остафьевская 21/1
Большое поле
—°
Малое поле
—°
Расписание для заливщиков Админ Богданова Марина Зайцев Арсений Кривенко Ксения
Сгенерировать расписание Редактировать расписание Копировать
Окно редактирования для заливщиков Поле Большое поле Малое поле
Время
Тип Аренда ФК Аренда ХК СМК ХК СМК ФК Массовое катание Турнир ФК Турнир ХК Утро
Комментарий
Добавить строку Скрыть окно
Редактирование карточки Копировать Удалить Отмена Сохранить
История изменений
Загрузка... Обновить Закрыть
':'На выбранное время событий не найдено'
}
function match(item,q){return!q||[item.time,item.title,item.rooms,item.note].join(" ").toLowerCase().includes(q)}
function sorted(zone,q){return getSelectedDay().items.filter(i=>i.zone===zone&&match(i,q)).sort((a,b)=>((a.order??firstMin(a.time)*10)-(b.order??firstMin(b.time)*10))||firstMin(a.time)-firstMin(b.time))}
function cls(c){return"mtl-card-"+(["blue","pink","purple","yellow","red","green"].includes(c)?c:"blue")}
function card(item,near){
const note=item.note?'
'+esc(item.note)+'
':"";
return '
'+esc(item.time)+'
'+esc(item.rooms)+'
'+(item.hiddenFullscreen?"":"")+' '
}
function renderTabs(){
document.getElementById("mtl-week-tabs").innerHTML=weekDates.map(date=>{
const d=ensureDay(date),dt=new Date(date+"T12:00:00"),weekend=[0,6].includes(dt.getDay()),shortDay=(d.day||"").slice(0,2);
return '
'+d.day+' '+shortDay+' '+String(dt.getDate())+' '
}).join("")
}
function getAdminName(){
const iceAdmin=document.getElementById("mtl-ice-admin-person");
if(iceAdmin&&iceAdmin.value)return iceAdmin.value;
const adminSelect=document.getElementById("mtl-admin-person");
if(adminSelect&&adminSelect.value)return adminSelect.value;
const d=getSelectedDay();
return d.admin||"Богданова Марина"
}
function inferIceType(item){
if(item&&item.iceType)return item.iceType;
const u=String((item&&item.title)||"").toUpperCase();
if(u.includes("УТРО"))return "Утро";
if(u.includes("МАССОВ"))return "Массовое катание";
if(u.includes("ТУРНИР")&&u.includes("ФК"))return "Турнир ФК";
if(u.includes("ТУРНИР"))return "Турнир ХК";
if(u.includes("СМК")&&u.includes("ФК"))return "СМК ФК";
if(u.includes("СМК"))return "СМК ХК";
if(u.includes("АРЕНДА")||u.includes("МИРОНОВ")||u.includes("ЖЕЛЕЗНОВ"))return u.includes("ФК")?"Аренда ФК":"Аренда ХК";
if(u.includes("ФК"))return "Аренда ФК";
return "Аренда ХК"
}
function goalCount(item){
const raw=item&&item.iceGoals;
const n=parseInt(raw,10);
return Number.isFinite(n)&&n>0?n:0
}
function inferIceGoals(item){
return String(goalCount(item))
}
function shouldClearGoals(item){
return !!(item&&item.iceClearGoals)||goalCount(item)<=0
}
function goalText(item){
const n=goalCount(item);
if(shouldClearGoals(item))return "Убрать все ворота.";
return "Оставить "+n+" ворот."
}
function iceNoteText(item){
return item&&item.iceNote?" · примечание: "+String(item.iceNote):""
}
function iceAction(item){
const type=inferIceType(item);
if(type==="Утро")return "НА УТРО — "+goalText(item)+iceNoteText(item);
return type+" · "+goalText(item)+iceNoteText(item)
}
function iceLines(zone){
const lines=[];let latest=null,hasMorning=false;
getSelectedDay().items.filter(i=>i.zone===zone).forEach(item=>intervals(item.time).forEach(iv=>{
const type=inferIceType(item);
const minute=type==="Утро"?iv.end:iv.start-15;
if(type==="Утро")hasMorning=true;
lines.push({minute:minute,text:minToTime(minute)+" — "+iceAction(item)});
if(latest===null||iv.end>latest)latest=iv.end
}));
lines.sort((a,b)=>a.minute-b.minute);
if(latest!==null&&!hasMorning){lines.push({minute:latest,text:minToTime(latest)+" — НА УТРО — Оставить 2 ворот."})}
return lines.map(x=>x.text)
}
function iceStorageKey(){
return ICE_STORAGE_KEY+"_"+selectedDate
}
function generatedIceText(){
return ["Заливки на "+fmt(selectedDate,true),"Админ – "+getAdminName(),"","Большое поле:",iceLines("big").join("\n"),"","Малое поле:",iceLines("small").join("\n")].join("\n")
}
function saveIceText(){
localStorage.setItem(iceStorageKey(),iceOutput.value)
}
function renderIce(){
if(iceOutput.dataset.editing==="1")return;
const saved=localStorage.getItem(iceStorageKey());
iceOutput.value=saved!==null?saved:generatedIceText();
iceOutput.readOnly=true;
iceOutput.classList.remove("mtl-editing");
iceOutput.dataset.editing="0";
showIceEditor(false);
const btn=document.getElementById("mtl-edit-ice-btn");
if(btn)btn.textContent="Редактировать расписание"
}
function syncAdminSelectors(){
const day=getSelectedDay();
const val=day.admin||"Богданова Марина";
const a=document.getElementById("mtl-admin-person");
const i=document.getElementById("mtl-ice-admin-person");
if(a)a.value=val;
if(i)i.value=val;
}
function showIceEditor(show){
iceEditor.classList.toggle("mtl-visible",!!show)
}
function setDensity(bigCount,smallCount){
const maxCount=Math.max(bigCount,smallCount);
app.classList.remove("mtl-density-compact","mtl-density-tight");
if(maxCount>=7)app.classList.add("mtl-density-compact");
if(maxCount>=9)app.classList.add("mtl-density-tight");
}
function buildIceEditorLine(){
const time=(document.getElementById("mtl-ice-time").value||"").trim();
const type=document.getElementById("mtl-ice-type").value;
const goals=parseInt(document.getElementById("mtl-ice-goals").value,10)||0;
const comment=(document.getElementById("mtl-ice-comment").value||"").trim();
if(!time)return null;
const g=goals>0?"Оставить "+goals+" ворот.":"Убрать все ворота.";
return time+" — "+type+" — "+g+(comment?" — примечание: "+comment:"")
}
function insertIceLine(zone,line){
let text=iceOutput.value||"";
const smallHeader="Малое поле:";
if(!text.includes(smallHeader))text=text+"\n\n"+smallHeader+"\n";
if(zone==="big"){
const idx=text.indexOf("\n\n"+smallHeader);
if(idx>=0){
const before=text.slice(0,idx).replace(/\s+$/,'');
const after=text.slice(idx);
text=before+"\n"+line+after;
}else{text=text.replace(/\s+$/,'')+"\n"+line}
}else{
const idx=text.indexOf(smallHeader);
if(idx>=0){
const start=idx+smallHeader.length;
text=text.slice(0,start)+"\n"+line+text.slice(start);
}else{text=text.replace(/\s+$/,'')+"\n\n"+smallHeader+"\n"+line}
}
iceOutput.value=text.replace(/\n{3,}/g,"\n\n").trim();
saveIceText();
}
function changeGoal(targetId,delta){
const input=document.getElementById(targetId);
if(!input)return;
const current=parseInt(input.value,10);
const next=Math.max(0,(Number.isFinite(current)?current:0)+delta);
input.value=String(next);
if(targetId==="mtl-modal-ice-goals"){
const clear=document.getElementById("mtl-modal-clear-goals");
if(clear)clear.checked=next<=0
}
input.dispatchEvent(new Event("input",{bubbles:true}))
}
function render(){
refreshWeekDates();
const day=getSelectedDay(),q=searchInput.value.trim().toLowerCase(),visible=day.items.filter(i=>match(i,q)),near=nearestIds(visible);
const bigItems=sorted("big",q),smallItems=sorted("small",q);
const displayTime=timePicker.value||new Date().toTimeString().slice(0,5);
document.getElementById("mtl-public-date").innerHTML='
'+fmtRuDate(selectedDate)+' '+esc(getSelectedDay().day.charAt(0)+getSelectedDay().day.slice(1).toLowerCase())+' '+esc(displayTime)+' ';
document.getElementById("mtl-public-day").textContent=day.day||"";
document.getElementById("mtl-admin-date").value=selectedDate;
document.getElementById("mtl-admin-day").value=day.day||"";const dayCurrent=document.getElementById("mtl-admin-day-current");if(dayCurrent)dayCurrent.textContent=fmt(selectedDate,true)+" · "+(day.day||"");
const bigIceTempInput=document.getElementById("mtl-big-ice-temp-input"),smallIceTempInput=document.getElementById("mtl-small-ice-temp-input");
if(bigIceTempInput&&document.activeElement!==bigIceTempInput)bigIceTempInput.value=getMeta().bigIceTemp;
if(smallIceTempInput&&document.activeElement!==smallIceTempInput)smallIceTempInput.value=getMeta().smallIceTemp;
updateWeatherCard();
syncVisibilitySettings();
syncAdminSelectors();
datePicker.value=selectedDate;
bigList.innerHTML=bigItems.map(i=>card(i,near)).join("")+'
+ ';
smallList.innerHTML=smallItems.map(i=>card(i,near)).join("")+'
+ ';
if(!visible.length){bigList.innerHTML='
Расписание на этот день пока не заполнено
+ ';smallList.innerHTML='
+ '}
document.getElementById("mtl-current-info").innerHTML=nearestStatus(visible);
setDensity(bigItems.length,smallItems.length);
renderTabs();renderIce()
}
function toastType(msg,type){
if(type)return type;
if(msg==="Успешно сохранено")return"success";
if(/ошиб|сервер недоступен|не удалось|конфликт|изменили с другого устройства/i.test(String(msg||"")))return"error";
return"info"
}
function toastIcon(type){return type==="success"?"✓":type==="error"?"⛔":"i"}
function toast(msg,type){
const box=document.getElementById("mtl-toast"),el=document.createElement("div"),kind=toastType(msg,type);
el.className="mtl-toast-item mtl-toast-"+kind;
el.innerHTML='
'+toastIcon(kind)+' '+esc(msg)+' ';
box.appendChild(el);
setTimeout(()=>el.style.opacity="0",2600);
setTimeout(()=>el.remove(),3100)
}
function setAdminTheme(mode){
const dark=mode==="dark";
app.classList.toggle("mtl-admin-dark",dark);
localStorage.setItem("mtl_admin_theme",dark?"dark":"light");
const btn=document.getElementById("mtl-admin-theme-btn");
if(btn)btn.textContent=dark?"Светлый режим":"Темный режим"
}
function toggleAdminTheme(){
setAdminTheme(app.classList.contains("mtl-admin-dark")?"light":"dark")
}
function openAdmin(){
const area=document.getElementById("mtl-admin-area");
if(area.classList.contains("mtl-visible")){area.classList.remove("mtl-visible");app.classList.remove("mtl-admin-panel-open");if(adminUnlocked)persistAdminLog("Выход из админ-панели");return}
if(!adminUnlocked){let saved=readSavedLogin();let user=saved?authenticateUser(saved.login,saved.password):null;if(!user){if(saved)clearSavedLogin();const login=(prompt("Логин сотрудника")||"").trim().toLowerCase();if(!login)return;const found=USERS.find(u=>u.login===login);if(!found){toast("Сотрудник не найден");return}const pass=prompt("Пароль для "+found.name);user=authenticateUser(login,pass);if(!user){clearSavedLogin();toast("Неверный пароль");return}if(confirm("Сохранить логин и пароль на этом устройстве?"))writeSavedLogin(login,pass)}currentUser=user;adminUnlocked=true;app.classList.add("mtl-admin-on");toast("Вход: "+user.name);persistAdminLog("Вход в админ-панель")}
area.classList.add("mtl-visible");app.classList.add("mtl-admin-panel-open");const a=document.getElementById("mtl-admin-person"),ia=document.getElementById("mtl-ice-admin-person");if(currentUser&¤tUser.role!=="admin"){if(a)a.value=currentUser.name;if(ia)ia.value=currentUser.name;getSelectedDay().admin=currentUser.name}render()
}
function openModal(cardId){
if(!adminUnlocked){toast("Для редактирования войдите в админ-панель");return}
const item=getSelectedDay().items.find(i=>i.id===cardId);
if(!item)return;
activeEditId=cardId;
document.getElementById("mtl-modal-title-label").textContent="Редактирование карточки";
document.getElementById("mtl-modal-zone").value=item.zone;
document.getElementById("mtl-modal-color").value=item.color||"blue";
document.getElementById("mtl-modal-ice-type").value=inferIceType(item);
document.getElementById("mtl-modal-ice-goals").value=inferIceGoals(item);
document.getElementById("mtl-modal-clear-goals").checked=!!item.iceClearGoals||goalCount(item)<=0;
document.getElementById("mtl-modal-title").value=item.title||"";
document.getElementById("mtl-modal-rooms").value=item.rooms||"";
document.getElementById("mtl-modal-time").value=item.time||"";
document.getElementById("mtl-modal-note").value=item.note||"";
document.getElementById("mtl-modal-ice-note").value=item.iceNote||"";
document.getElementById("mtl-modal-hide-fullscreen").checked=!!item.hiddenFullscreen;
modal.classList.add("mtl-visible")
}
function openAddModal(zone){
if(!adminUnlocked){toast("Для добавления войдите в админ-панель");return}
activeEditId=null;
document.getElementById("mtl-modal-title-label").textContent="Добавление карточки";
document.getElementById("mtl-modal-zone").value=zone||"big";
document.getElementById("mtl-modal-color").value="blue";
document.getElementById("mtl-modal-ice-type").value="Аренда ХК";
document.getElementById("mtl-modal-ice-goals").value="0";
document.getElementById("mtl-modal-clear-goals").checked=true;
document.getElementById("mtl-modal-title").value="";
document.getElementById("mtl-modal-rooms").value="";
document.getElementById("mtl-modal-time").value="";
document.getElementById("mtl-modal-note").value="";
document.getElementById("mtl-modal-ice-note").value="";
document.getElementById("mtl-modal-hide-fullscreen").checked=false;
modal.classList.add("mtl-visible")
}
function closeModal(){activeEditId=null;modal.classList.remove("mtl-visible")}
function setNow(){const d=new Date(),h=String(d.getHours()).padStart(2,"0"),m=String(d.getMinutes()).padStart(2,"0");timePicker.value=h+":"+m;useLiveTime=true;render()}
function tick(){
const d=new Date(),h=String(d.getHours()).padStart(2,"0"),m=String(d.getMinutes()).padStart(2,"0"),s=String(d.getSeconds()).padStart(2,"0"),hm=h+":"+m;
document.getElementById("mtl-live-time").textContent="Сейчас "+hm+":"+s;
const railTime=document.querySelector("#mtl-public-date .mtl-rail-time");
if(railTime&&useLiveTime)railTime.textContent=hm;
if(useLiveTime&&timePicker.value!==hm){timePicker.value=hm;render()}
}
function moveWeatherToRail(){const weather=document.getElementById("mtl-weather-card"),rail=document.querySelector("#mtl-schedule-app .mtl-day-rail"),dayText=document.getElementById("mtl-public-day");if(weather&&rail&&weather.parentNode!==rail)rail.insertBefore(weather,dayText)}
function goDay(delta){selectedDate=addDays(selectedDate,delta);useLiveTime=false;refreshWeekDates();render()}
function toggleMobileMenu(){app.classList.toggle("mtl-mobile-menu-open")}
function formatLogTime(v){const d=new Date(v);if(isNaN(d.getTime()))return String(v||"");return d.toLocaleDateString("ru-RU")+", "+d.toLocaleTimeString("ru-RU")}
function renderLogs(logs){const out=document.getElementById("mtl-logs-output");if(!out)return;if(!logs||!logs.length){out.textContent="Логов пока нет";return}out.textContent=logs.map(l=>formatLogTime(l.time)+" | "+(l.actor||"-")+" | "+(l.action||"-")).join("\n")}
function loadLogs(){const out=document.getElementById("mtl-logs-output");if(out)out.textContent="Загрузка...";fetch(googleUrl({logs:"1",token:GOOGLE_WRITE_TOKEN,ts:Date.now()}),{cache:"no-store"}).then(r=>r.json()).then(res=>{if(res&&res.ok&&Array.isArray(res.logs)&&res.logs.length){renderLogs(res.logs);return}renderLogs(scheduleLogsNewestFirst())}).catch(()=>renderLogs(scheduleLogsNewestFirst()))}
function openLogs(){document.getElementById("mtl-logs-user").textContent=currentUser?"Текущий сотрудник: "+currentUser.name:"";document.getElementById("mtl-logs-modal").classList.add("mtl-visible");loadLogs()}
function setPageScrollLock(locked){
document.documentElement.style.overflow=locked?"hidden":"";
document.body.style.overflow=locked?"hidden":"";
document.body.style.height=locked?"100vh":""
}
function toggleFull(){
const full=!app.classList.contains("mtl-fullscreen-mode");
app.classList.toggle("mtl-fullscreen-mode",full);
setPageScrollLock(full);
document.getElementById("mtl-fullscreen-btn").textContent=full?"Выйти из полного экрана":"Во весь экран";
if(full){
if(app.requestFullscreen)app.requestFullscreen().catch(()=>toast("Браузерный fullscreen недоступен, включен режим табло"));
}else if(document.fullscreenElement&&document.exitFullscreen){
document.exitFullscreen().catch(()=>{})
}
render()
}
let draggedCardId=null;
function listZone(list){return list&&list.id==="mtl-small-list"?"small":"big"}
function cardAfterPointer(list,y){
const cards=[...list.querySelectorAll(".mtl-card:not(.mtl-dragging)")];
let closest={offset:Number.NEGATIVE_INFINITY,element:null};
cards.forEach(card=>{
const box=card.getBoundingClientRect();
const offset=y-box.top-box.height/2;
if(offset<0&&offset>closest.offset)closest={offset:offset,element:card}
});
return closest.element
}
function clearDropState(){
document.querySelectorAll("#mtl-schedule-app .mtl-list.mtl-drop-active").forEach(x=>x.classList.remove("mtl-drop-active"));
document.querySelectorAll("#mtl-schedule-app .mtl-card.mtl-dragging").forEach(x=>x.classList.remove("mtl-dragging"))
}
function setupCardDrag(){
app.addEventListener("dragstart",e=>{
const card=e.target.closest(".mtl-card");
if(!card||!adminUnlocked||app.classList.contains("mtl-fullscreen-mode"))return;
if(e.target.closest("button")&&!e.target.closest(".mtl-drag")){e.preventDefault();return}
draggedCardId=card.dataset.cardId;
card.classList.add("mtl-dragging");
if(e.dataTransfer){
e.dataTransfer.effectAllowed="move";
e.dataTransfer.setData("text/plain",draggedCardId)
}
});
app.addEventListener("dragover",e=>{
if(!draggedCardId)return;
const list=e.target.closest(".mtl-list");
if(!list)return;
e.preventDefault();
list.classList.add("mtl-drop-active");
const dragging=app.querySelector('.mtl-card[data-card-id="'+draggedCardId+'"]');
const after=cardAfterPointer(list,e.clientY);
const add=list.querySelector(".mtl-add-card");
if(dragging)list.insertBefore(dragging,after||add||null)
});
app.addEventListener("drop",e=>{
if(!draggedCardId)return;
const list=e.target.closest(".mtl-list");
if(!list){clearDropState();draggedCardId=null;return}
e.preventDefault();
const cards=[...list.querySelectorAll(".mtl-card")];
const index=cards.findIndex(x=>x.dataset.cardId===draggedCardId);
const beforeId=cards[index+1]?cards[index+1].dataset.cardId:null;
const moved=moveScheduleItem(draggedCardId,listZone(list),beforeId);
clearDropState();
draggedCardId=null;
if(moved){render();toast("Порядок карточек сохранён")}
});
app.addEventListener("dragend",()=>{
clearDropState();
draggedCardId=null
})
}
document.getElementById("mtl-admin-open-btn").onclick=openAdmin;document.getElementById("mtl-logs-btn").onclick=openLogs;document.getElementById("mtl-logs-refresh-btn").onclick=loadLogs;document.getElementById("mtl-logs-close-btn").onclick=()=>document.getElementById("mtl-logs-modal").classList.remove("mtl-visible");const mobileMenuBtn=document.getElementById("mtl-mobile-menu-btn");if(mobileMenuBtn)mobileMenuBtn.onclick=toggleMobileMenu;document.getElementById("mtl-admin-prev-day").onclick=()=>goDay(-1);document.getElementById("mtl-admin-next-day").onclick=()=>goDay(1);
document.getElementById("mtl-admin-hide-btn").onclick=()=>{document.getElementById("mtl-admin-area").classList.remove("mtl-visible");app.classList.remove("mtl-admin-panel-open");if(adminUnlocked)persistAdminLog("Выход из админ-панели")};
document.getElementById("mtl-admin-theme-btn").onclick=toggleAdminTheme;
document.getElementById("mtl-fullscreen-btn").onclick=toggleFull;
document.getElementById("mtl-now-btn").onclick=setNow;
document.getElementById("mtl-refresh-btn").onclick=()=>{
if(iceOutput.dataset.editing==="1")saveIceText();
toast("Обновляем расписание...");
Promise.resolve(saveSchedule(false,"Обновление расписания"))
.then(()=>fetchRemoteSchedule(false))
.finally(()=>window.location.reload())
};
document.getElementById("mtl-sync-load-btn").onclick=()=>fetchRemoteSchedule(true);
document.getElementById("mtl-week-tabs").onclick=e=>{const t=e.target.closest("[data-date]");if(t){selectedDate=t.dataset.date;render()}};
datePicker.onchange=()=>{if(datePicker.value){selectedDate=datePicker.value;const d=getSelectedDay();d.date=selectedDate;d.day=makeDay(selectedDate).day;saveSchedule(false);render()}};
document.getElementById("mtl-admin-date").onchange=e=>{if(e.target.value)document.getElementById("mtl-admin-day").value=makeDay(e.target.value).day};
timePicker.onchange=()=>{useLiveTime=false;render()};
searchInput.oninput=render;
document.getElementById("mtl-admin-person").onchange=()=>{getSelectedDay().admin=document.getElementById("mtl-admin-person").value;saveSchedule(false);localStorage.removeItem(iceStorageKey());render()};
document.getElementById("mtl-ice-admin-person").onchange=()=>{getSelectedDay().admin=document.getElementById("mtl-ice-admin-person").value;saveSchedule(false);localStorage.removeItem(iceStorageKey());renderIce()};
document.getElementById("mtl-big-ice-temp-input").oninput=e=>{getMeta().bigIceTemp=e.target.value||"-5.0";updateWeatherCard()};
document.getElementById("mtl-big-ice-temp-input").onchange=e=>{getMeta().bigIceTemp=e.target.value||"-5.0";saveSchedule(true);updateWeatherCard()};
document.getElementById("mtl-small-ice-temp-input").oninput=e=>{getMeta().smallIceTemp=e.target.value||"-5.0";updateWeatherCard()};
document.getElementById("mtl-small-ice-temp-input").onchange=e=>{getMeta().smallIceTemp=e.target.value||"-5.0";saveSchedule(true);updateWeatherCard()};
document.getElementById("mtl-toggle-weather").onchange=e=>{getMeta().showWeather=e.target.checked;syncVisibilitySettings();saveSchedule(true)};
const clockToggle=document.getElementById("mtl-toggle-clock");
if(clockToggle)clockToggle.onchange=e=>{getMeta().showClock=e.target.checked;syncVisibilitySettings();saveSchedule(true)};
document.getElementById("mtl-toggle-ice-temps").onchange=e=>{getMeta().showIceTemps=e.target.checked;syncVisibilitySettings();saveSchedule(true)};
app.onclick=e=>{
const g=e.target.closest("[data-goal-action]");
if(g){changeGoal(g.dataset.goalTarget,g.dataset.goalAction==="inc"?1:-1);return}
const copyBtn=e.target.closest("[data-copy-id]");
if(copyBtn){
if(!adminUnlocked){toast("Для копирования войдите в админ-панель");return}
const copied=duplicateScheduleItem(copyBtn.dataset.copyId);
if(copied){localStorage.removeItem(iceStorageKey());render();toast("Карточка скопирована")}
return
}
const lockBtn=e.target.closest("[data-toggle-hide-id]");
if(lockBtn){
if(!adminUnlocked){toast("Для скрытия войдите в админ-панель");return}
const item=getSelectedDay().items.find(x=>x.id===lockBtn.dataset.toggleHideId);
if(item){item.hiddenFullscreen=!item.hiddenFullscreen;saveSchedule(true);render()}
return
}
const del=e.target.closest("[data-delete-id]");
if(del){
if(!adminUnlocked){toast("Для удаления войдите в админ-панель");return}
if(confirm("Удалить карточку?")){deleteScheduleItem(del.dataset.deleteId);localStorage.removeItem(iceStorageKey());render();toast("Карточка удалена")}
return
}
const add=e.target.closest("[data-add-zone]");
if(add){openAddModal(add.dataset.addZone);return}
const b=e.target.closest("[data-edit-id]");
if(b)openModal(b.dataset.editId)
};
document.getElementById("mtl-save-schedule-btn").onclick=()=>{const nd=document.getElementById("mtl-admin-date").value||selectedDate,old=getSelectedDay();if(nd!==selectedDate){scheduleStore.days[nd]=old;delete scheduleStore.days[selectedDate];selectedDate=nd}getSelectedDay().date=selectedDate;getSelectedDay().day=makeDay(selectedDate).day;document.getElementById("mtl-admin-day").value=getSelectedDay().day;getSelectedDay().admin=document.getElementById("mtl-admin-person").value;saveSchedule(true);localStorage.removeItem(iceStorageKey());render()};
document.getElementById("mtl-publish-btn").onclick=()=>{
saveSchedule(true);
if(iceOutput.dataset.editing==="1")saveIceText();
render();
};
document.getElementById("mtl-add-item-btn").onclick=()=>{
const item={id:id(),zone:document.getElementById("mtl-form-zone").value,time:document.getElementById("mtl-form-time").value.trim(),title:document.getElementById("mtl-form-title").value.trim(),rooms:document.getElementById("mtl-form-rooms").value.trim(),note:document.getElementById("mtl-form-note").value.trim(),color:document.getElementById("mtl-form-color").value,iceType:document.getElementById("mtl-form-ice-type").value,iceGoals:document.getElementById("mtl-form-ice-goals").value,iceClearGoals:parseInt(document.getElementById("mtl-form-ice-goals").value,10)<=0,iceNote:document.getElementById("mtl-form-ice-note").value.trim(),hiddenFullscreen:document.getElementById("mtl-form-hide-fullscreen").checked};
if(!item.time||!item.title||!item.rooms){toast("Заполните время, название и раздевалки");return}
saveScheduleItem(item);localStorage.removeItem(iceStorageKey());
["mtl-form-time","mtl-form-title","mtl-form-rooms","mtl-form-note","mtl-form-ice-note"].forEach(x=>document.getElementById(x).value="");
document.getElementById("mtl-form-ice-goals").value="0";
document.getElementById("mtl-form-hide-fullscreen").checked=false;
render();toast("Успешно сохранено")
};
document.getElementById("mtl-clear-btn").onclick=()=>{if(!confirm("Очистить расписание выбранного дня?"))return;clearSchedule();localStorage.removeItem(iceStorageKey());render();toast("Расписание очищено")};
const resetDemoBtn=document.getElementById("mtl-reset-btn");if(resetDemoBtn)resetDemoBtn.onclick=()=>{resetDemoSchedule();selectedDate="2026-05-18";render();toast("Демо-данные восстановлены")};
document.getElementById("mtl-generate-ice-btn").onclick=()=>{
getSelectedDay().admin=getAdminName();
saveSchedule(false);
iceOutput.dataset.editing="0";
iceOutput.readOnly=true;
iceOutput.classList.remove("mtl-editing");
iceOutput.value=generatedIceText();
saveIceText();
showIceEditor(false);
document.getElementById("mtl-edit-ice-btn").textContent="Редактировать расписание";
toast("Расписание для заливщиков сгенерировано")
};
document.getElementById("mtl-edit-ice-btn").onclick=()=>{
const btn=document.getElementById("mtl-edit-ice-btn");
if(iceOutput.readOnly){
iceOutput.readOnly=false;
iceOutput.dataset.editing="1";
iceOutput.classList.add("mtl-editing");
btn.textContent="Сохранить расписание";
showIceEditor(true);
iceOutput.focus();
toast("Можно редактировать расписание для заливщиков")
}else{
saveIceText();
iceOutput.readOnly=true;
iceOutput.dataset.editing="0";
iceOutput.classList.remove("mtl-editing");
showIceEditor(false);
btn.textContent="Редактировать расписание";
toast("Успешно сохранено")
}
};
document.getElementById("mtl-ice-add-line-btn").onclick=()=>{
const line=buildIceEditorLine();
if(!line){toast("Укажите время для строки заливщиков");return}
insertIceLine(document.getElementById("mtl-ice-zone").value,line);
document.getElementById("mtl-ice-time").value="";
document.getElementById("mtl-ice-comment").value="";
toast("Строка добавлена в расписание заливщиков")
};
document.getElementById("mtl-ice-hide-editor-btn").onclick=()=>showIceEditor(false);
function exportBackup(){
const data={version:"montreal-v6",exportedAt:new Date().toISOString(),scheduleStore:scheduleStore,iceManual:{}};
for(let i=0;i
URL.revokeObjectURL(a.href),1000);
toast("Файл резервной копии выгружен")
}
function importBackupFile(file){
const r=new FileReader();
r.onload=()=>{
try{
const data=JSON.parse(r.result);
if(data.scheduleStore&&data.scheduleStore.days){scheduleStore=data.scheduleStore;saveSchedule(false)}
if(data.iceManual){Object.keys(data.iceManual).forEach(k=>localStorage.setItem(k,data.iceManual[k]))}
render();toast("Файл резервной копии загружен")
}catch(e){toast("Не удалось загрузить файл")}
};
r.readAsText(file)
}
document.getElementById("mtl-export-backup-btn").onclick=exportBackup;
document.getElementById("mtl-import-backup-btn").onclick=()=>document.getElementById("mtl-import-backup-file").click();
document.getElementById("mtl-import-backup-file").onchange=e=>{const f=e.target.files&&e.target.files[0];if(f)importBackupFile(f);e.target.value=""};
document.getElementById("mtl-copy-ice-btn").onclick=()=>{iceOutput.select();if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(iceOutput.value).then(()=>toast("Расписание для заливщиков скопировано"));else{document.execCommand("copy");toast("Расписание для заливщиков скопировано")}};
document.getElementById("mtl-modal-save-btn").onclick=()=>{
const item={id:activeEditId||id(),zone:document.getElementById("mtl-modal-zone").value,time:document.getElementById("mtl-modal-time").value.trim(),title:document.getElementById("mtl-modal-title").value.trim(),rooms:document.getElementById("mtl-modal-rooms").value.trim(),note:document.getElementById("mtl-modal-note").value.trim(),color:document.getElementById("mtl-modal-color").value,iceType:document.getElementById("mtl-modal-ice-type").value,iceGoals:document.getElementById("mtl-modal-ice-goals").value,iceClearGoals:document.getElementById("mtl-modal-clear-goals").checked||parseInt(document.getElementById("mtl-modal-ice-goals").value,10)<=0,iceNote:document.getElementById("mtl-modal-ice-note").value.trim(),hiddenFullscreen:document.getElementById("mtl-modal-hide-fullscreen").checked};
if(!item.time||!item.title||!item.rooms){toast("Заполните время, название и раздевалки");return}
saveScheduleItem(item);localStorage.removeItem(iceStorageKey());closeModal();render();toast("Успешно сохранено")
};
document.getElementById("mtl-modal-copy-btn").onclick=()=>{if(!activeEditId){toast("Сначала сохраните новую карточку");return}const copied=duplicateScheduleItem(activeEditId);if(copied){localStorage.removeItem(iceStorageKey());closeModal();render();toast("Карточка скопирована")}};
document.getElementById("mtl-modal-delete-btn").onclick=()=>{if(!activeEditId){closeModal();return}deleteScheduleItem(activeEditId);localStorage.removeItem(iceStorageKey());closeModal();render();toast("Карточка удалена")};
document.getElementById("mtl-modal-clear-goals").onchange=()=>{if(document.getElementById("mtl-modal-clear-goals").checked)document.getElementById("mtl-modal-ice-goals").value="0"};
document.getElementById("mtl-modal-ice-goals").oninput=()=>{const n=parseInt(document.getElementById("mtl-modal-ice-goals").value,10)||0;document.getElementById("mtl-modal-clear-goals").checked=n<=0};
document.getElementById("mtl-modal-cancel-btn").onclick=closeModal;
modal.onclick=e=>{if(e.target===modal)closeModal()};
document.onkeydown=e=>{if(e.key==="Escape")closeModal()};
document.addEventListener("fullscreenchange",()=>{
if(!document.fullscreenElement&&app.classList.contains("mtl-fullscreen-mode")){
app.classList.remove("mtl-fullscreen-mode");
setPageScrollLock(false);
document.getElementById("mtl-fullscreen-btn").textContent="Во весь экран";
render()
}
});
setAdminTheme(localStorage.getItem("mtl_admin_theme")||"light");
moveWeatherToRail();
setupCardDrag();
loadSchedule();
datePicker.value=selectedDate;
setNow();
fetchWeather();
setInterval(fetchWeather,10*60*1000);
setInterval(tick,1000);
})();