Fargo-Moorhead’s Most Trusted House Cleaning Service | Dust Busters Cleaning

`; } function openPrintReport(){ const html = buildPrintableHTML(); const w = window.open("", "_blank"); if (!w){ alert("Pop-up blocked. Allow pop-ups to export PDF."); return; } w.document.open(); w.document.write(html); w.document.close(); w.focus(); } function setHandlers(root){ $("#btn-save").addEventListener("click", async ()=>{ if (!state.settings.weekStart){ alert("Select Week Start (Monday) first."); return; } saveState(); // local backup first const result = await backendSave(state.settings.weekStart, state); if (result && result.success){ alert("Saved to Google Sheets ✅"); } else { alert("Saved locally, but Google save failed ⚠️"); } }); $("#btn-load").addEventListener("click", async ()=>{ if (!state.settings.weekStart){ alert("Select Week Start (Monday) first."); return; } const result = await backendLoad(state.settings.weekStart); if (result && result.success && result.payload){ state = result.payload; saveState(); buildUI(root); setHandlers(root); updateUI(); alert("Loaded from Google Sheets ✅"); } else { alert("Could not load from Google. Using local data."); } }); $("#btn-reset").addEventListener("click", ()=>{ if (!confirm("Reset the entire week back to blank?")) return; state = deepClone(DEFAULT_STATE); saveState(); buildUI(root); setHandlers(root); updateUI(); }); $("#btn-export-print").addEventListener("click", ()=> openPrintReport()); $("#btn-export-csv").addEventListener("click", ()=>{ const week = (state.settings.weekStart || "week").replaceAll("-",""); downloadFile(`payroll_${week}.csv`, buildCSV(), "text/csv;charset=utf-8"); }); $("#btn-export-json").addEventListener("click", ()=>{ const week = (state.settings.weekStart || "week").replaceAll("-",""); downloadFile(`payroll_backup_${week}.json`, JSON.stringify(state, null, 2), "application/json"); }); const bind = (sel, fn) => { $(sel).addEventListener("input", ()=>{ fn(); saveState(); scheduleUpdate(); }); $(sel).addEventListener("change", ()=>{ fn(); saveState(); scheduleUpdate(); }); }; bind("#set-week-start", ()=>{ state.settings.weekStart = $("#set-week-start").value || ""; updateTabs(); }); bind("#set-hold", ()=>{ state.settings.squareHold = parseFloat($("#set-hold").value) || 0; }); bind("#set-cap", ()=>{ state.settings.capPct = parseFloat($("#set-cap").value) || 0.5; }); bind("#set-loaded", ()=>{ state.settings.loadedCost = num($("#set-loaded").value) || 0; }); bind("#set-joanne", ()=>{ state.settings.office.joanne = num($("#set-joanne").value); }); bind("#set-dawn", ()=>{ state.settings.office.dawn = num($("#set-dawn").value); }); bind("#set-robert", ()=>{ state.settings.office.robert = num($("#set-robert").value); }); bind("#set-karly", ()=>{ state.settings.office.karly = num($("#set-karly").value); }); bind("#set-moses-perjob", ()=>{ state.settings.mosesPerJob = num($("#set-moses-perjob").value); }); bind("#set-moses-jobs", ()=>{ state.settings.mosesJobs = Math.max(0, Math.floor(num($("#set-moses-jobs").value))); }); root.addEventListener("click", (e)=>{ const tab = e.target.closest("button[data-action='setDay']"); if (tab){ const day = tab.getAttribute("data-day"); if (DAYS.includes(day)){ state.activeDay = day; saveState(); refreshActiveDay(); } return; } const btn = e.target.closest("button[data-action]"); if (!btn) return; const action = btn.getAttribute("data-action"); if (action === "setDay") return; const teamEl = btn.closest(".team"); if (!teamEl) return; const day = teamEl.getAttribute("data-day"); const team = teamEl.getAttribute("data-team"); if (action === "addRow"){ state.data[day][team].push({label:"", price:"", hours:""}); refreshBody(day, team); saveState(); scheduleUpdate(); } if (action === "clearTeam"){ if (!confirm(`Clear all rows for ${team} on ${day}?`)) return; state.data[day][team] = [{label:"", price:"", hours:""}]; refreshBody(day, team); saveState(); scheduleUpdate(); } if (action === "saveNow"){ saveState(); } if (action === "removeRow"){ const tr = btn.closest("tr"); const idx = parseInt(tr.getAttribute("data-idx"), 10); state.data[day][team].splice(idx, 1); if (state.data[day][team].length === 0) state.data[day][team].push({label:"", price:"", hours:""}); refreshBody(day, team); saveState(); scheduleUpdate(); } }); root.addEventListener("input", (e)=>{ const inp = e.target.closest("input.cellInput"); if (!inp) return; const tr = inp.closest("tr"); if (!tr) return; const day = tr.getAttribute("data-day"); const team = tr.getAttribute("data-team"); const idx = parseInt(tr.getAttribute("data-idx"), 10); const field = inp.getAttribute("data-field"); if (!state.data?.[day]?.[team]?.[idx]) return; if (field === "price"){ inp.value = formatMoneyFromDigits(inp.value); } state.data[day][team][idx][field] = inp.value; saveState(); scheduleUpdate(); }); } function mount(){ const root = document.getElementById("db-payroll"); if (!root) return false; buildUI(root); setHandlers(root); updateUI(); return true; } if (!mount()){ let tries = 0; const iv = setInterval(()=>{ tries++; if (mount() || tries > 30) clearInterval(iv); }, 250); } })();