Продолжая использовать наш сайт, вы даете согласие на согласие на обработку ваших персональных данных и использование cookie-файлов в соответствии с Политикой конфиденциальности
OK
Выберите дату и время, чтобы увидеть ближайшее событие
--:--
Редактирование доступно только через кнопку-карандаш на карточке
БОЛЬШОЕ ПОЛЕ
МАЛОЕ ПОЛЕ
Москва - Монтреал
—°
Остафьевская 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.title)+note+'
'+esc(item.rooms)+'
' } 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 '' }).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&&currentUser.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="Загрузка...";getGoogle({logs:"1",token:GOOGLE_WRITE_TOKEN,ts:Date.now()}).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;iURL.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); })();