@ -75,6 +75,9 @@
< h4 > Abstract Counts< / h4 >
< div id = "abstractCounts" class = "mb-4" > < / div >
< h4 > Zone Wise Attendance %< / h4 >
< div id = "zoneWisePercentage" class = "mb-4" > < / div >
< h4 > Zone Summary< / h4 >
< div class = "zone-summary-row" >
< div class = "zone-summary-card" >
@ -180,12 +183,15 @@
data.forEach(item => {
const facialRegistered = item.facialRegistered || 0;
const facialNotRegistered = item.facialNotRegistered || 0;
const totalCount = item.totalCount || 0;
const facialRegPct = totalCount > 0 ? ((facialRegistered / totalCount) * 100).toFixed(2) : '0.00';
html += `< div class = "col-md-3" > < div class = "card p-3 mb-2" >
< strong > ${item.emp_type}< / strong >
< div class = "mt-2" >
< div class = "small" > Total: < strong > ${item. totalCount}< / strong > < / div >
< div class = "small" > Total: < strong > ${totalCount}< / strong > < / div >
< div class = "small" > Facial Registered: < strong > ${facialRegistered}< / strong > < / div >
< div class = "small" > Facial Not Registered: < strong > ${facialNotRegistered}< / strong > < / div >
< div class = "small" > Facial Registered %: < strong > ${facialRegPct}%< / strong > < / div >
< div class = "small" > Present %: < strong > < span id = "presentPercent" > —< / span > < / strong > < / div >
< / div >
< / div > < / div > `;
@ -195,6 +201,50 @@
} catch (e) { target.innerHTML = `< div class = "alert alert-danger" > Error: ${e}< / div > `; }
}
async function loadZoneWisePercentage() {
const target = document.getElementById('zoneWisePercentage');
target.innerHTML = createLoader();
try {
const rawFrom = document.getElementById('fromDate').value;
const rawTo = document.getElementById('toDate').value;
const fromDate = toApiDdMmYyyy(rawFrom);
const toDate = toApiDdMmYyyy(rawTo);
const res = await fetch(`${apiBase}/zoneWisePercentage?fromDate=${encodeURIComponent(fromDate)}&toDate=${encodeURIComponent(toDate)}`);
const data = await res.json();
if (!Array.isArray(data) || data.length === 0) {
target.innerHTML = '< div class = "alert alert-info" > No zone percentage data available< / div > ';
return;
}
let html = '< div class = "row" > ';
data.forEach(item => {
const zoneName = item.ZoneName || 'Unknown Zone';
const facialPct = item.facialPercentage != null ? item.facialPercentage : 0;
const presentPct = item.presentPercentage != null ? item.presentPercentage : 0;
html += `< div class = "col-md-4" > < div class = "card p-3 mb-2 text-center" >
< strong > ${zoneName}< / strong >
< div class = "row mt-2" >
< div class = "col-6" >
< div style = "font-size: 1.3rem; font-weight: bold;" >
${facialPct}%
< / div >
< div class = "small text-muted" > Facial Registered %< / div >
< / div >
< div class = "col-6" >
< div style = "font-size: 1.3rem; font-weight: bold;" >
${presentPct}%
< / div >
< div class = "small text-muted" > Present %< / div >
< / div >
< / div >
< / div > < / div > `;
});
html += '< / div > ';
target.innerHTML = html;
} catch (e) {
target.innerHTML = `< div class = "alert alert-danger" > Error: ${e}< / div > `;
}
}
function createLoader() {
return '< div class = "spinner-container" > < div class = "spinner-border" role = "status" > < span class = "visually-hidden" > Loading...< / span > < / div > < / div > ';
}
@ -411,11 +461,11 @@
html += `< td class = "text-center" > ${total}< / td > `;
html += `< td class = "text-center" > ${facialRegistered}< / td > `;
// html += `< td class = "text-center" > ${facialNotRegistered}< / td > `;
html += `< td class = "text-center" > ` + (facialNotRegistered > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-category = "notregistered" > ${facialNotRegistered}< / a > ` : `< span class = "disabled-count" > ${facialNotRegistered}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (present > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-category = "present" > ${present}< / a > ` : `< span class = "disabled-count" > ${present}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (od > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-category = "od" > ${od}< / a > ` : `< span class = "disabled-count" > ${od}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (leave > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-category = "leave" > ${leave}< / a > ` : `< span class = "disabled-count" > ${leave}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (notMarked > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-category = "notmarked" > ${notMarked}< / a > ` : `< span class = "disabled-count" > ${notMarked}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (facialNotRegistered > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-parkname = "${parkDisplay}" data- category = "notregistered" > ${facialNotRegistered}< / a > ` : `< span class = "disabled-count" > ${facialNotRegistered}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (present > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-parkname = "${parkDisplay}" data- category = "present" > ${present}< / a > ` : `< span class = "disabled-count" > ${present}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (od > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-parkname = "${parkDisplay}" data- category = "od" > ${od}< / a > ` : `< span class = "disabled-count" > ${od}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (leave > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-parkname = "${parkDisplay}" data- category = "leave" > ${leave}< / a > ` : `< span class = "disabled-count" > ${leave}< / span > `) + `< / td > `;
html += `< td class = "text-center" > ` + (notMarked > 0 ? `< a href = "#" class = "park-employee-count" data-zone = "${zone}" data-parkid = "${parkId}" data-parkname = "${parkDisplay}" data- category = "notmarked" > ${notMarked}< / a > ` : `< span class = "disabled-count" > ${notMarked}< / span > `) + `< / td > `;
html += '< / tr > ';
});
@ -491,7 +541,7 @@
}
// loadDetailed may be called with explicit empType/category/zone (when clicking a zone count)
async function loadDetailed(empType, category, zoneVal, parkName) {
async function loadDetailed(empType, category, zoneVal, parkId, park Name) {
// show details in modal
const rawFrom = document.getElementById('fromDate').value;
const rawTo = document.getElementById('toDate').value;
@ -507,7 +557,7 @@
return;
}
let titleSuffix = parkName ? ` — Park ${parkName }` : '';
let titleSuffix = parkName ? ` — ${parkName} - ${parkId }` : '';
modalTitle.innerText = `${empType} — Zone ${zoneVal}${titleSuffix} — ${category.toUpperCase()} (${rawFrom} → ${rawTo})`;
modalBody.innerHTML = createLoader();
// Show modal immediately with loader
@ -516,8 +566,8 @@
try {
let url = `${apiBase}/detailed?empType=${encodeURIComponent(empType)}&category=${encodeURIComponent(category)}&fromDate=${encodeURIComponent(fromDate)}&toDate=${encodeURIComponent(toDate)}`;
url += `&zone=${encodeURIComponent(zoneVal)}`;
if (parkName ) {
url += `&parkId=${encodeURIComponent(parkName )}`;
if (parkId ) {
url += `&parkId=${encodeURIComponent(parkId )}`;
}
const res = await fetch(url);
if (!res.ok) {
@ -647,7 +697,7 @@
alert('To date must be the same as or later than From date');
return;
}
loadAbstractCounts(); loadZoneSummary();
loadAbstractCounts(); loadZoneWisePercentage(); loadZone Summary();
});
// delegate clicks: zone links -> park-wise summary, zone counts -> detailed, park counts -> park-details
document.addEventListener('click', (ev) => {
@ -692,8 +742,9 @@
ev.preventDefault();
const zone = parkEmployeeAnchor.getAttribute('data-zone');
const parkId = parkEmployeeAnchor.getAttribute('data-parkid');
const parkName = parkEmployeeAnchor.getAttribute('data-parkname');
const category = parkEmployeeAnchor.getAttribute('data-category');
loadDetailed('Park', category, zone, parkId);
loadDetailed('Park', category, zone, parkId, parkName );
return;
}
});
@ -701,6 +752,7 @@
setDateLimits();
// load initial
loadAbstractCounts();
loadZoneWisePercentage();
loadZoneSummary();
});