|
|
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head th:replace="~{fragments/modules/base/head :: head}"><style>
</style></head><body>
<div class="wrapper"> <!-- header top menu Start --> <header th:replace="~{fragments/modules/base/header :: header}"></header> <!-- header top menu end --> <div class="page-wrap"> <!--=============== Left Menu side Start ================--> <div th:replace="~{fragments/modules/base/menu :: menu}"></div> <!--=============== Left side End ================--> <div class="main-content"> <div class="container-fluid"> <div class="page-header mb-2"> <div class="row align-items-end"> <div class="col-lg-8"> <div class="page-header-title"> <!-- <i class="far fa-calendar-plus bg-blue"></i> --> <div class="d-inline"> <h5 >Hall Absents</h5> <span>Reports for Hall Absents</span> </div> </div> </div>
</div> </div>
<div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-header"> Filters </div> <div class="card-body"> <div class="row"> <div class="col-sm-4"> <label for="attendance">Select Training</label> <select id="attendance" class="form-control"> <option value="">Select</option> <option value="1" >First Training Attendance</option> <option value="2">Second Training Attendance</option> <option value="3">Second Repeat Training Attendance</option> <option value="4" selected="selected">Third Training Attendance</option> </select> </div> <div class="col-sm-4"> <label for="att_type">Select Report Type</label> <select id="att_type" class="form-control"> <option value="">Select</option> <option value="abstract">Abstract</option> <option value="summary">Summary</option> </select> </div> <div class="col-sm-4"> <label for="tr_center">Select Training center</label> <select id="tr_center" class="form-control"> <option value="">Select</option> </select> </div> </div> <div class="row mt-4"> <div class="col-12 d-flex justify-content-center"> <button type="submit" id="searchBtn" class="btn btn-primary mx-2">Search</button>
<button class="btn btn-danger mx-2" id="resetBtn" name="reset" type="reset">Reset</button> </div> </div> </div> </div> </div> </div> <div class="row"> <div class="col-md-12"> <div class="card" id="reportDiv" style="display: none;"> <div class="card-body"> <div class="table-responsive" id="summaryDiv" style="display: none;">
<table id="summaryreportTable" class="table table-striped table-bordered table-hover dataTable" role="grid"> <thead class="thead-dark"> <!-- Main Header Row --> <tr role="row"> <th rowspan="2" class="text-center">HALL NO</th> <th colspan="5" class="text-center">CATEGORY</th> <th rowspan="2" class="text-center">Total</th> </tr> <!-- Sub Header 1 --> <tr> <th class="text-center">PO</th> <th class="text-center">PO1</th> <th class="text-center">PO2</th> <th class="text-center">PO3</th> <th class="text-center">PO4</th> </tr> </thead> <tbody> <!-- Rows will be dynamically inserted via JS/DataTable --> </tbody> </table>
</div> <div class="table-responsive" id="abstractDiv" style="display: none;">
<table id="abstractreportTable" class="table table-striped table-bordered table-hover dataTable" role="grid"> <thead class="thead-dark"> <!-- Main Header Row --> <tr role="row"> <th colspan="5" class="text-center">PO</th> <th colspan="5" class="text-center">PO1</th> <th colspan="5" class="text-center">PO2</th> <th colspan="5" class="text-center">PO3</th> <th colspan="5" class="text-center">PO4</th> <th colspan="5" class="text-center">TOTAL</th> </tr> <!-- Sub Header 1 --> <tr> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> <th class="text-center">TOTAL</th> <th class="text-center">ABSENT</th> <th class="text-center">PRESENT</th> <th class="text-center">% of ABSENT</th> <th class="text-center">% of PRESENT</th> </tr> </thead> <tbody> <!-- Rows will be dynamically inserted via JS/DataTable --> </tbody> </table>
</div> </div> </div> </div> </div>
</div> </div> <div id="pageLoader" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); z-index: 9999; text-align: center;"> <div style="position: relative; top: 50%; transform: translateY(-50%);"> <div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"> <span class="sr-only">Loading...</span> </div> <!-- Add the 'Please wait' text below the spinner --> <p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p> </div> </div>
<div th:replace="~{fragments/modules/base/app_module_menu_box :: div}"></div>
<footer th:replace="~{fragments/modules/base/footer :: footer}"></footer> </div></div>
<script th:replace="~{fragments/modules/base/script :: script}"></script>
<script type="text/javascript">document.getElementById('resetBtn').addEventListener('click', function () { event.preventDefault(); location.reload(); });$(document).ready(function () { $.ajax({ url: "/election/api/dataentryadmin/report/Attendance/trainingconstituencies", method: "GET", success: function (data) {
let dropdown = $("#tr_center");
dropdown.empty(); dropdown.append('<option value="">-- Select --</option>');
data.forEach(function (row) { dropdown.append( `<option value="${row.c_code}"> ${row.display_name} </option>` ); });
}, error: function () { alert("Failed to load training constituencies"); } }); $('#summaryDiv').hide(); $('#abstractDiv').hide(); $('#reportDiv').hide(); });
document.getElementById('searchBtn').addEventListener('click', function (e) { let attendance=$('#attendance').val(); let att_type=$('#att_type').val(); let tr_center=$('#tr_center').val(); let tr_center_text = $('#tr_center option:selected').text();
let trainingTitle = "";
if (attendance === "1") { trainingTitle = "1st DAY REVISED POLLING PERSONAL TRAINING ATTENDANCE - 28.03.2026"; } else if (attendance === "2") { trainingTitle = "2nd DAY POLLING PERSONAL TRAINING ATTENDANCE - 11.04.2026"; } else if (attendance === "3") { trainingTitle = "2nd REPEAT POLLING PERSONAL TRAINING ATTENDANCE - 19.04.2026"; } else if (attendance === "4") { trainingTitle = "3rd DAY POLLING PERSONAL TRAINING ATTENDANCE - 22.04.2026"; } if(!attendance){ Swal.fire({ icon: 'warning', title: 'Training Required', text: 'Please Select Training' }); return; } if(!att_type){ Swal.fire({ icon: 'warning', title: 'Attendance Type Required', text: 'Please Select Type' }); return; } if(!tr_center){ Swal.fire({ icon: 'warning', title: 'Training center Required', text: 'Please Select Training center' }); return; } console.log("attendance="+attendance); console.log("att_type="+att_type); console.log("tr_center="+tr_center); $('#pageLoader').show();
// Step 1: Check data first $.ajax({ url: '/election/api/dataentryadmin/attendance/getAbsentees', type: 'GET', data: { attendance: attendance,att_type:att_type,tr_center:tr_center }, success: function (res) { $('#pageLoader').hide(); console.log(res.data);
if (res.status==="Success") { $('#reportDiv').show(); if(res.report==="abstract"){ $('#summaryDiv').hide(); $('#abstractDiv').show(); let table = $('#abstractreportTable').DataTable({ destroy: true, searching: true, autoWidth: true, paging: true, info: true, data: res.data, responsive: false, scrollX: true, columns: [ { data: 'PO_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'PO_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'PO_PRESENT', defaultContent: '-', className: 'text-center' }, { data: 'PO_ABSENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'PO_PRESENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'PO1_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'PO1_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'PO1_PRESENT', defaultContent: '-', className: 'text-center' }, {data: 'PO1_ABSENT_PER', defaultContent: '-', className: 'text-center' }, {data: 'PO1_PRESENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'PO2_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'PO2_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'PO2_PRESENT', defaultContent: '-', className: 'text-center' }, {data: 'PO2_ABSENT_PER', defaultContent: '-', className: 'text-center' }, {data: 'PO2_PRESENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'PO3_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'PO3_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'PO3_PRESENT', defaultContent: '-', className: 'text-center' }, {data: 'PO3_ABSENT_PER', defaultContent: '-', className: 'text-center' }, {data: 'PO3_PRESENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'PO4_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'PO4_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'PO4_PRESENT', defaultContent: '-', className: 'text-center' }, {data: 'PO4_ABSENT_PER', defaultContent: '-', className: 'text-center' }, {data: 'PO4_PRESENT_PER', defaultContent: '-', className: 'text-center' }, { data: 'TOTAL_TOTAL', defaultContent: '-', className: 'text-center' }, { data: 'TOTAL_ABSENT', defaultContent: '-', className: 'text-center' }, { data: 'TOTAL_PRESENT', defaultContent: '-', className: 'text-center' }, {data: 'TOTAL_ABSENT_PER', defaultContent: '-', className: 'text-center' }, {data: 'TOTAL_PRESENT_PER', defaultContent: '-', className: 'text-center' } ], dom: 'Bfrtip', buttons: [ { extend: 'pageLength', className: 'btn btn-warning' }, { title: 'Data export', extend: 'copyHtml5', text: 'Copy', className: 'btn btn-secondary', id: 'copyBtn' }, { extend: 'excelHtml5', text: 'Excel', title: null, className: 'btn btn-secondary', id: 'excelBtn', filename: function () { return tr_center+'_AttendanceAbsReport_' + getTimestamp(); }, customize: function (xlsx) {
var sheet = xlsx.xl.worksheets['sheet1.xml']; var sheetData = $('sheetData', sheet);
let tr_center_text = $('#tr_center option:selected').text();
// 🔹 1. SHIFT EXISTING ROWS DOWN BY 3 $('row', sheetData).each(function () { var r = parseInt($(this).attr('r')); $(this).attr('r', r + 4);
$(this).find('c').each(function () { var cellRef = $(this).attr('r'); var col = cellRef.replace(/[0-9]/g, ''); var row = parseInt(cellRef.replace(/[A-Z]/g, '')); $(this).attr('r', col + (row + 4)); }); });
// 🔹 2. ADD HEADER ROWS sheetData.prepend(` <row r="1"> <c t="inlineStr" r="A1"><is><t>${tr_center_text}</t></is></c> </row> <row r="2"> <c t="inlineStr" r="A2"><is><t>{trainingTitle}</t></is></c> </row> <row r="3"> <c t="inlineStr" r="A3"><is><t>ABSTRACT</t></is></c> </row> `); sheetData.prepend(` <row r="4"> <c t="inlineStr" r="A4"><is><t>PO</t></is></c> <c t="inlineStr" r="F4"><is><t>PO1</t></is></c> <c t="inlineStr" r="K4"><is><t>PO2</t></is></c> <c t="inlineStr" r="P4"><is><t>PO3</t></is></c> <c t="inlineStr" r="U4"><is><t>PO4</t></is></c> <c t="inlineStr" r="Z4"><is><t>TOTAL</t></is></c> </row> `);
// 🔹 3. MERGE CELLS (FULL WIDTH) var mergeCells = $('mergeCells', sheet);
if (mergeCells.length === 0) { mergeCells = $('<mergeCells count="0"/>').appendTo(sheet); }
mergeCells.append(` <mergeCell ref="A1:AD1"/> <mergeCell ref="A2:AD2"/> <mergeCell ref="A3:AD3"/> <mergeCell ref="A4:E4"/> <mergeCell ref="F4:J4"/> <mergeCell ref="K4:O4"/> <mergeCell ref="P4:T4"/> <mergeCell ref="U4:Y4"/> <mergeCell ref="Z4:AD4"/> `);
// 🔹 4. UPDATE MERGE COUNT mergeCells.attr('count', mergeCells.find('mergeCell').length);
// 5. CENTER ALIGN TITLE ROWS (ADD HERE) var styles = xlsx.xl['styles.xml'];
$('cellXfs', styles).append( '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1">' + '<alignment horizontal="center"/>' + '</xf>' );
var styleIndex = $('cellXfs xf', styles).length - 1;
// Apply style to title rows $('c[r="A1"]', sheet).attr('s', styleIndex); $('c[r="A2"]', sheet).attr('s', styleIndex); $('c[r="A3"]', sheet).attr('s', styleIndex); ['A4','F4','K4','P4','U4','Z4'].forEach(function(cell){ $('c[r="' + cell + '"]', sheet).attr('s', styleIndex); }); } }, { extend: 'csvHtml5', text: 'CSV', className: 'btn btn-secondary', id: 'csvBtn', filename: function () { return tr_center+'_AttendanceAbsReport_' + getTimestamp(); } }, { extend: 'pdfHtml5', text: 'PDF', title: null, className: 'btn btn-secondary', id: 'pdfBtn', filename: function () { return tr_center+'_AttendanceAbsReport_' + getTimestamp(); }, orientation: 'landscape', pageSize: 'A3', customize: function (doc) { let tr_center_text = $('#tr_center option:selected').text(); // 🔹 1. ADD HEADINGS doc.content.unshift( { text: tr_center_text, alignment: 'center', bold: true, fontSize: 14, margin: [0, 0, 0, 5] }, { text: trainingTitle, alignment: 'center', bold: true, fontSize: 11, margin: [0, 0, 0, 5] }, { text: 'ABSTRACT', alignment: 'center', bold: true, fontSize: 12, margin: [0, 0, 0, 10] } ); // 🔹 2. PAGE SETTINGS doc.pageOrientation = 'landscape'; doc.pageMargins = [10, 10, 10, 10]; doc.defaultStyle.fontSize = 8; doc.styles.tableHeader.fontSize = 8; doc.styles.tableHeader.alignment = 'center'; doc.styles.tableHeader.bold = true; doc.styles.tableHeader.fillColor = null; doc.styles.tableHeader.color = '#000'; doc.styles.tableBodyEven.fillColor = null; doc.styles.tableBodyOdd.fillColor = null; // 🔹 3. FIND TABLE (IMPORTANT) var tableNode; doc.content.forEach(function (item) { if (item.table) { tableNode = item; } }); if (!tableNode) return; // safety let tableBody = tableNode.table.body; // 🔹 4. ADD GROUP HEADER ROW tableBody.unshift([ { text: 'PO', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {}, { text: 'PO1', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {}, { text: 'PO2', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {}, { text: 'PO3', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {}, { text: 'PO4', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {}, { text: 'TOTAL', colSpan: 5, alignment: 'center', bold: true, fillColor: null, color: '#000' }, {}, {}, {}, {} ]); // 🔹 5. HEADER ROW COUNT tableNode.table.headerRows = 2; // 🔹 6. FULL WIDTH FIX tableNode.table.widths = Array(30).fill(35); // 🔹 7. TABLE STYLE tableNode.layout = { hLineWidth: () => 0.3, vLineWidth: () => 0.3, hLineColor: () => '#000', vLineColor: () => '#000', paddingLeft: () => 2, paddingRight: () => 2, paddingTop: () => 2, paddingBottom: () => 2 }; // CENTER ALL CELLS tableNode.table.body = tableNode.table.body.map(function (row) { return row.map(function (cell) {
// 🔹 Case 1: valid object with text if (typeof cell === 'object' && cell !== null && ('text' in cell || 'colSpan' in cell)) { return { ...cell, fillColor: null, alignment: 'center' }; }
// 🔹 Case 2: empty placeholder {} → KEEP AS IS if (typeof cell === 'object' && cell !== null) { return cell; }
// 🔹 Case 3: normal value return { text: cell !== null ? cell.toString() : '', fillColor: null, alignment: 'center' }; }); }); } } ], columnDefs: [ { targets: 'no-sort', orderable: false } ], lengthMenu: [ [ 100, 200, 500, -1], ['100 rows', '200 rows', 'Show all'] ], scrollY: '40vh', searching: true, }); } else{ $('#abstractDiv').hide(); $('#summaryDiv').show(); let table = $('#summaryreportTable').DataTable({ destroy: true, searching: true, autoWidth: false, paging: true, info: true, ordering: false, data: res.data.sort((a, b) => a.sort_order - b.sort_order), responsive: false, scrollX: true, columns: [ { data: 'hall_no', defaultContent: '', className: 'text-center' }, { data: 'PO', defaultContent: '', className: 'text-center' }, { data: 'PO1', defaultContent: '', className: 'text-center' }, { data: 'PO2', defaultContent: '', className: 'text-center' }, { data: 'PO3', defaultContent: '', className: 'text-center' }, { data: 'PO4', defaultContent: '', className: 'text-center' }, { data: 'Total', defaultContent: '', className: 'text-center' } ], rowCallback: function (row, data) { if (data.hall_no === 'Grand Total') { $(row).css({ 'font-weight': 'bold', 'background-color': '#f2f2f2' }); } }, dom: 'Bfrtip', buttons: [ { extend: 'pageLength', className: 'btn btn-warning' }, { title: 'Data export', extend: 'copyHtml5', text: 'Copy', className: 'btn btn-secondary', id: 'copyBtn' }, { extend: 'excelHtml5', text: 'Excel', title: null, className: 'btn btn-secondary', id: 'excelBtn', filename: function () { return tr_center+'_AttendanceSumReport_' + getTimestamp(); }, customize: function (xlsx) {
var sheet = xlsx.xl.worksheets['sheet1.xml']; var sheetData = $('sheetData', sheet);
let tr_center_text = $('#tr_center option:selected').text();
// 🔹 1. SHIFT EXISTING ROWS DOWN BY 2 (IMPORTANT CHANGE) $('row', sheetData).each(function () { var r = parseInt($(this).attr('r')); $(this).attr('r', r + 2);
$(this).find('c').each(function () { var cellRef = $(this).attr('r'); var col = cellRef.replace(/[0-9]/g, ''); var row = parseInt(cellRef.replace(/[A-Z]/g, '')); $(this).attr('r', col + (row + 2)); }); });
// 🔹 2. ADD ONLY 2 HEADER ROWS sheetData.prepend(` <row r="1"> <c t="inlineStr" r="A1"><is><t>${tr_center_text}</t></is></c> </row> <row r="2"> <c t="inlineStr" r="A2"><is><t>HALLWISE ABSENTEES LIST</t></is></c> </row> `);
// 🔹 3. MERGE CELLS (ONLY 2 ROWS) var mergeCells = $('mergeCells', sheet);
if (mergeCells.length === 0) { mergeCells = $('<mergeCells count="0"/>').appendTo(sheet); }
mergeCells.append(` <mergeCell ref="A1:AD1"/> <mergeCell ref="A2:AD2"/> `);
mergeCells.attr('count', mergeCells.find('mergeCell').length);
// 🔹 4. CENTER ALIGN var styles = xlsx.xl['styles.xml'];
$('cellXfs', styles).append( '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1">' + '<alignment horizontal="center"/>' + '</xf>' );
var styleIndex = $('cellXfs xf', styles).length - 1;
$('c[r="A1"]', sheet).attr('s', styleIndex); $('c[r="A2"]', sheet).attr('s', styleIndex); } }, { extend: 'csvHtml5', text: 'CSV', className: 'btn btn-secondary', id: 'csvBtn', filename: function () { return tr_center+'_AttendanceSumReport_' + getTimestamp(); } }, { extend: 'pdfHtml5', text: 'PDF', title: null, className: 'btn btn-secondary', id: 'pdfBtn', filename: function () { return tr_center+'_AttendanceSumReport_' + getTimestamp(); }, orientation: 'portrait', pageSize: 'A3', customize: function (doc) {
doc.pageOrientation = 'portrait'; doc.pageSize = 'A4'; doc.pageMargins = [20, 20, 20, 20];
let tr_center_text = $('#tr_center option:selected').text();
// Remove default title if (doc.content[0].text) { doc.content.splice(0, 1); }
// Add headings doc.content.unshift( { text: tr_center_text, alignment: 'center', bold: true, fontSize: 14, margin: [0, 0, 0, 5] }, { text: 'HALLWISE ABSENTEES LIST', alignment: 'center', bold: true, fontSize: 12, margin: [0, 0, 0, 10] } );
// Find table var tableNode = doc.content.find(item => item.table); if (!tableNode) return;
// Remove only actual header rows safely if (tableNode.table.body.length > 0) { tableNode.table.body.shift(); // remove first header row // If second row is also header (PO, PO1...), remove it if (tableNode.table.body.length > 0 && tableNode.table.body[0].some(cell => (cell.text || cell).toString().toUpperCase().includes('PO') ) ) { tableNode.table.body.shift(); } }
// 🔥 STEP 2: ADD CORRECT HEADER WITH CATEGORY tableNode.table.body.unshift( [ { text: 'HALL NO', rowSpan: 2, bold: true, alignment: 'center' }, { text: 'CATEGORY', colSpan: 5, bold: true, alignment: 'center' }, {}, {}, {}, {}, { text: 'Total', rowSpan: 2, bold: true, alignment: 'center' } ], [ {}, { text: 'PO', bold: true, alignment: 'center' }, { text: 'PO1', bold: true, alignment: 'center' }, { text: 'PO2', bold: true, alignment: 'center' }, { text: 'PO3', bold: true, alignment: 'center' }, { text: 'PO4', bold: true, alignment: 'center' }, {} ] );
// Header rows count tableNode.table.headerRows = 2; for (let i = 0; i < tableNode.table.headerRows; i++) {
let row = tableNode.table.body[i];
for (let j = 0; j < row.length; j++) {
let cell = row[j];
// 🔹 CASE 1: Proper object cell if (typeof cell === 'object' && cell !== null && ('text' in cell || 'colSpan' in cell || 'rowSpan' in cell)) {
cell.text = cell.text || ''; // ✅ ensure text exists cell.bold = true; cell.alignment = 'center'; cell.fillColor = null;
if (cell.rowSpan) { cell.margin = [0, 5, 0, 5]; }
} // 🔹 CASE 2: Empty placeholder (colspan/rowspan filler) else if (cell === null || cell === undefined || cell === '') {
row[j] = { text: '', bold: true, alignment: 'center', fillColor: null }; } // 🔹 CASE 3: Plain value else {
row[j] = { text: cell.toString(), bold: true, alignment: 'center', fillColor: null }; } } }
// Widths tableNode.table.widths = ['*','*','*','*','*','*','*'];
// Center table tableNode.alignment = 'center'; tableNode.margin = [30, 5, 30, 0];
// Remove colors doc.styles.tableHeader.fillColor = null; doc.styles.tableHeader.color = '#000'; doc.styles.tableBodyEven.fillColor = null; doc.styles.tableBodyOdd.fillColor = null;
doc.styles.tableHeader.alignment = 'center'; doc.styles.tableHeader.bold = true; doc.defaultStyle.fontSize = 8; doc.styles.tableHeader.fontSize = 9;
// Compact layout tableNode.layout = { hLineWidth: () => 0.5, vLineWidth: () => 0.5, hLineColor: () => '#000', vLineColor: () => '#000', paddingLeft: () => 4, paddingRight: () => 4, paddingTop: () => 5, paddingBottom: () => 5 };
// Center all cells tableNode.table.body = tableNode.table.body.map(function (row) { return row.map(function (cell) {
if (typeof cell === 'object' && cell !== null && ('text' in cell || 'rowSpan' in cell || 'colSpan' in cell)) { return { ...cell, alignment: 'center', fillColor: null }; }
if (typeof cell === 'object' && cell !== null) { return cell; }
return { text: cell !== null ? cell.toString() : '', alignment: 'center', fillColor: null }; }); }); // 🔹 MAKE "Grand Total" ROW BOLD
tableNode.table.body.forEach(function (row) {
let firstCell = row[0];
let text = '';
if (typeof firstCell === 'object' && firstCell !== null) { text = (firstCell.text || '').toString().toUpperCase(); } else { text = (firstCell || '').toString().toUpperCase(); }
if (text.includes('GRAND TOTAL')) {
row.forEach(function (cell, index) {
if (typeof cell === 'object' && cell !== null) { cell.bold = true; } else { row[index] = { text: cell != null ? cell.toString() : '', bold: true, alignment: 'center' }; } }); } });
// Page break spacing doc.pageBreakBefore = function (currentNode) { return currentNode.y > 760; }; } } ], columnDefs: [ { targets: 'no-sort', orderable: false } ], lengthMenu: [ [ 100, 200, 500, -1], ['100 rows', '200 rows', 'Show all'] ], scrollY: '40vh', searching: true, }); }
} else { Swal.fire({ icon: 'warning', title: res.message }); } }, error: function () { $('#pageLoader').hide(); Swal.fire({ icon: 'error', title: 'Error', text: 'Issue in Fetching Attendance Data' }); } }); });
function getTimestamp() { const now = new Date(); const yyyy = now.getFullYear(); const mm = String(now.getMonth() + 1).padStart(2, '0'); const dd = String(now.getDate()).padStart(2, '0'); const hh = String(now.getHours()).padStart(2, '0'); const mi = String(now.getMinutes()).padStart(2, '0'); const ss = String(now.getSeconds()).padStart(2, '0');
return `${yyyy}${mm}${dd}_${hh}${mi}${ss}`;}
</script>
</body></html>
|