diff --git a/routers/khokhaEntryRouter.js b/routers/khokhaEntryRouter.js index 9716d96..15e7591 100644 --- a/routers/khokhaEntryRouter.js +++ b/routers/khokhaEntryRouter.js @@ -24,6 +24,29 @@ const toISTISOString = (value) => { return new Date(date.getTime() + IST_OFFSET_MS).toISOString(); }; +// For Excel/CSV exports only - readable format +const toReadableIST = (value) => { + if (!value) return ''; + const date = new Date(value); + if (isNaN(date)) return ''; + + // Add IST offset: 5 hours 30 minutes = 330 minutes = 19800000 milliseconds + const istTime = date.getTime() + (330 * 60 * 1000); + const istDate = new Date(istTime); + + const day = istDate.getUTCDate(); + const month = istDate.getUTCMonth() + 1; + const year = istDate.getUTCFullYear(); + const hours = istDate.getUTCHours(); + const minutes = istDate.getUTCMinutes().toString().padStart(2, '0'); + const seconds = istDate.getUTCSeconds().toString().padStart(2, '0'); + + const period = hours >= 12 ? 'PM' : 'AM'; + const hours12 = hours % 12 || 12; + + return `${day}-${month}-${year} ${hours12}:${minutes}:${seconds} ${period}`; +}; + function hmacHex(rollNo) { return crypto.createHmac("sha256", GATELOG_SECRET_KEY) .update(String(rollNo), "utf8") @@ -455,12 +478,12 @@ khokhaEntryRouter.post("/entries/export", async (req, res) => { RoomNumber: d.roomNumber, Destination: d.destination, CheckOutGate: d.checkOutGate, - CheckOutTime: toISTISOString(d.checkOutTime), + CheckOutTime: toReadableIST(d.createdAt), CheckInGate: d.checkInGate || '', - CheckInTime: toISTISOString(d.checkInTime), + CheckInTime: toReadableIST(d.checkInTime), Status: d.isClosed ? 'Closed' : 'Open', - CreatedAt: toISTISOString(d.createdAt), - UpdatedAt: toISTISOString(d.updatedAt), + CreatedAt: toReadableIST(d.createdAt), + UpdatedAt: toReadableIST(d.updatedAt), _id: d._id?.toString() || '' })); @@ -524,12 +547,12 @@ khokhaEntryRouter.post("/entries/export", async (req, res) => { RoomNumber: d.roomNumber, Destination: d.destination, CheckOutGate: d.checkOutGate, - CheckOutTime: toISTISOString(d.checkOutTime), + CheckOutTime: toReadableIST(d.createdAt), CheckInGate: d.checkInGate || '', - CheckInTime: toISTISOString(d.checkInTime), + CheckInTime: toReadableIST(d.checkInTime), Status: d.isClosed ? 'Closed' : 'Open', - CreatedAt: toISTISOString(d.createdAt), - UpdatedAt: toISTISOString(d.updatedAt), + CreatedAt: toReadableIST(d.createdAt), + UpdatedAt: toReadableIST(d.updatedAt), _id: d._id?.toString() || '' })); diff --git a/views/add-entry.ejs b/views/add-entry.ejs index 8501970..cfa597e 100644 --- a/views/add-entry.ejs +++ b/views/add-entry.ejs @@ -84,15 +84,12 @@ - - - +
- Note: If you provide a Check-In Time, the entry will be saved as Closed. - If you leave Check-In empty, it will be saved as Open. + Note: New entries are created as Open. You can close them later when the student returns.
@@ -112,15 +109,19 @@ -
- - -
- -
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- + @@ -208,9 +183,7 @@ const detailsModalEl = document.getElementById('detailsModal'); const getDetailsModal = () => bootstrap.Modal.getOrCreateInstance(detailsModalEl); - // Clean up any stray backdrops if there were multiple instances created earlier detailsModalEl.addEventListener('hidden.bs.modal', () => { - // Defensive cleanup (usually not needed when using getOrCreateInstance) document.querySelectorAll('.modal-backdrop').forEach(el => el.remove()); document.body.classList.remove('modal-open'); document.body.style.removeProperty('padding-right'); @@ -223,7 +196,8 @@ const r = document.getElementById('rollNumber').value || '—'; const e = document.getElementById('outlookEmail').value || '—'; document.getElementById('modalSummary').textContent = `Submitting for ${n} (${r}) • ${e}`; - document.getElementById('m_destination').focus(); + // focus first destination button label for easy selection + document.querySelector('label[for="dest_city"]').focus(); }); // Hidden form setters @@ -279,7 +253,7 @@ roomNumber: data.roomNumber }); - // Open modal using a SINGLE instance (prevents stuck backdrops) + // Open modal getDetailsModal().show(); } catch (err) { showAlert('danger', `Failed to fetch details: ${err.message}`); @@ -300,37 +274,23 @@ // ----- SUBMIT from modal ----- document.getElementById('modalSubmitBtn').addEventListener('click', () => { - const dest = document.getElementById('m_destination').value; // SELECT value (City/Khoka/Other) + const destNode = document.querySelector('input[name="m_destination"]:checked'); const cout = document.getElementById('m_checkOutTime').value; const cg = document.querySelector('input[name="m_checkOutGate"]:checked'); - if (!dest) return showAlert('warning', 'Destination is required.'); + if (!destNode) return showAlert('warning', 'Please choose a destination.'); if (!cout) return showAlert('warning', 'Check-Out Time is required.'); if (!cg) return showAlert('warning', 'Please select an Exit Gate.'); - document.getElementById('f_destination').value = dest; + document.getElementById('f_destination').value = destNode.value; document.getElementById('f_checkOutTime').value = cout; document.getElementById('f_checkOutGate').value = cg.value; - const igNode = document.querySelector('input[name="m_checkInGate"]:checked'); - const ig = igNode ? igNode.value : ''; - let cin = document.getElementById('m_checkInTime').value || ''; - - // If In Gate chosen but no Check-In Time, auto-fill now (local) - if (ig && !cin) { - const now = new Date(); - now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); - cin = now.toISOString().slice(0,16); - } - - document.getElementById('f_checkInTime').value = cin; - document.getElementById('f_checkInGate').value = ig; - - // Hide modal BEFORE posting to avoid any lingering overlay during redirect + // Hide modal BEFORE posting to avoid overlay during redirect const inst = bootstrap.Modal.getInstance(detailsModalEl); if (inst) inst.hide(); - // Submit the hidden form + // Submit the hidden form (no check-in fields anymore) document.getElementById('entryForm').submit(); });