`;
});
document.getElementById('jobs-list').innerHTML = h;
setupDragDrop('jobs-list', 'reorderJobs', 'data-name');
}
function addStaff() {
var o = {name:document.getElementById('sn').value, email:document.getElementById('se').value, company:document.getElementById('sc').value, role:document.getElementById('sr').value, contact:document.getElementById('sk').value, pin:document.getElementById('sp').value};
callBackend('saveStaffMember', o, BID).then(res => {
if(res.success) { sync(); ['sn','se','sc','sr','sk','sp'].forEach(i => document.getElementById(i).value = ''); }
else { alert("Failed to add staff member. " + (res.msg || "")); }
});
}
function saveJob() {
var o = {name:document.getElementById('j-name').value, address:document.getElementById('j-addr').value, lat:document.getElementById('j-lat').value, lng:document.getElementById('j-lng').value};
callBackend('saveNewJob', o, BID).then(res => {
if(res.success) { sync(); ['j-name','j-addr','j-coords-display'].forEach(i => document.getElementById(i).value = ''); }
else { alert("Failed to save site. " + (res.msg || "")); }
});
}
function navClick(t) {
document.querySelectorAll('.nav-item').forEach(x => x.classList.remove('active'));
document.querySelectorAll('.drawer').forEach(x => x.style.display = 'none');
if (t === 'map') document.getElementById('m-btn').classList.add('active');
else if (t === 'jobs') { document.getElementById('jobs-drawer').style.display = 'block'; document.getElementById('j-btn').classList.add('active'); }
else if (t === 'staff') { document.getElementById('staff-drawer').style.display = 'block'; document.getElementById('s-btn').classList.add('active'); }
else if (t === 'leaderboard') { document.getElementById('leaderboard-drawer').style.display = 'block'; document.getElementById('lb-btn').classList.add('active'); loadLeaderboard(); }
else if (t === 'today') { loadLogs('today', document.getElementById('t-btn')); }
else if (t === 'stats') { document.getElementById('stats-drawer').style.display = 'block'; document.getElementById('stats-btn').classList.add('active'); loadStats(); }
}
function loadLeaderboard() {
callBackend('getLeaderboardData', {}, BID).then(data => {
leaderboardData = data;
const sel = document.getElementById('week-selector'); sel.innerHTML = '';
Object.keys(data).sort().reverse().forEach(w => sel.innerHTML += ``);
renderLeaderboard();
});
}
function renderLeaderboard() {
const week = document.getElementById('week-selector').value;
let h = '';
if(leaderboardData[week]) leaderboardData[week].forEach((p, i) => {
let rC = (i===0)?'rank-1':(i===1)?'rank-2':(i===2)?'rank-3':'';
h += `
${i+1}
${p.name}
${p.score} pts
`;
});
document.getElementById('lb-content').innerHTML = h;
}
function loadLogs(day, el) {
document.getElementById('log-panel').style.display = 'block';
el.classList.add('active');
var actionStr = (day === 'today') ? 'getTodayLogs' : 'getYesterdayLogs';
callBackend(actionStr, {}, BID).then(logs => {
var h = '
TIME
STAFF
ACTION
';
logs.forEach(r => h += `
${new Date(r[0]).toLocaleTimeString()}
${r[1]}
${r[2]}
`);
document.getElementById('log-content').innerHTML = h + '
';
});
}
function loadStats() {
callBackend('getDashboard', {}, BID).then(s => {
document.getElementById('stat-active-jobs').innerText = s.activeJobs;
document.getElementById('stat-onsite').innerText = s.onSite;
});
}
function setupDragDrop(lI, sF, aN) {
const l = document.getElementById(lI);
l.addEventListener('dragover', e => { e.preventDefault(); const d = document.querySelector('.dragging'); if (d) l.appendChild(d); });
l.addEventListener('dragend', () => {
const i = Array.from(l.querySelectorAll('.item-card')).map(el => el.getAttribute(aN));
callBackend(sF, {namesArray: i}, BID);
});
}
function drag(e) { e.target.classList.add('dragging'); }
function enablePinning() { isPinning = true; document.getElementById('map').classList.add('crosshair-mode'); }
function disablePinning() { isPinning = false; document.getElementById('map').classList.remove('crosshair-mode'); }
function setCoords(la, ln, ad) { document.getElementById('j-lat').value = la; document.getElementById('j-lng').value = ln; document.getElementById('j-addr').value = ad; document.getElementById('j-coords-display').value = parseFloat(la).toFixed(5) + "," + parseFloat(ln).toFixed(5); }
function generatePin() { document.getElementById('sp').value = Math.floor(100000 + Math.random() * 900000); }
function downloadCSV() {
callBackend('getCsvData', {}, BID).then(d => {
var b = new Blob([d.map(e => e.join(",")).join("\n")], {type: 'text/csv'});
var a = document.createElement("a"); a.href = URL.createObjectURL(b); a.download = "Logs.csv"; a.click();
});
}
function goBackToOperator() {
window.location.href = "Operator.html";
}
SITERoll ADMIN
SITEROLL v3
00
PERSONNEL
SITES
SHOW NAMES
ADD PERSONNEL
X
ROSTER
ESTABLISH SITE
X
DIRECTORY
DASHBOARD STATS
X
Active Jobs: 0
On Site Today: 0
LEADERBOARD
X
SCORING RULES:
• 1 pt for every minute on site.
• +10 pts Bonus: Sign in before 7:00 AM.
• +10 pts Bonus: Sign out after 3:30 PM.
• -20 pts Penalty: Sign in after 7:00 AM.
• -20 pts Penalty: Sign out before 3:30 PM. * Failure to sign out forfeits all points for that session.