You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
15 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head th:replace="~{fragments/modules/base/head :: head}">
  4. <style>
  5. </style>
  6. </head>
  7. <body>
  8. <div class="wrapper">
  9. <!-- header top menu Start -->
  10. <header th:replace="~{fragments/modules/base/header :: header}"></header>
  11. <!-- header top menu end -->
  12. <div class="page-wrap">
  13. <!--=============== Left Menu side Start ================-->
  14. <div th:replace="~{fragments/modules/base/menu :: menu}"></div>
  15. <!--=============== Left side End ================-->
  16. <div class="main-content">
  17. <div class="container-fluid">
  18. <div class="page-header mb-2">
  19. <div class="row align-items-end">
  20. <div class="col-lg-7">
  21. <div class="page-header-title">
  22. <i class="fas fa-file-invoice bg-blue"></i>
  23. <div class="d-inline">
  24. <h5>Attendance</h5>
  25. <span>Manage the Attendance file</span>
  26. </div>
  27. </div>
  28. </div>
  29. <div class="col-lg-5">
  30. <span id="constituencySpan"></span>
  31. </div>
  32. </div>
  33. </div>
  34. <div class="row">
  35. <div class="col-md-12">
  36. <div class="card mt-2">
  37. <div class="card-header" style="font-weight: bold;">
  38. To Get Attendance Training Template
  39. </div>
  40. <div class="card-body d-flex align-items-end">
  41. <span th:text="${loginUserId}" style="display:none;" id="userIdSpan"></span>
  42. <span th:text="${mapped_ac}" style="display:none;" id="mapped_ac"></span>
  43. <!-- Dropdown -->
  44. <div class="col-sm-4">
  45. <label for="attendance">Select Training</label>
  46. <select id="attendance" class="form-control">
  47. <option value="">Select</option>
  48. <!-- <option value="1" selected="selected">First Training Attendance</option>-->
  49. <!-- <option value="2" selected="selected">Second Training Attendance</option> -->
  50. <!-- <option value="3" selected="selected">Second Repeat Training Attendance</option> -->
  51. <option value="4" selected="selected">Third Training Attendance</option>
  52. </select>
  53. </div>
  54. <!-- Button (hidden initially) -->
  55. <div class="col-sm-4" id="downloadBtnDiv" >
  56. <button id="downloadTemplateBtn" class="btn btn-primary">
  57. Template Download
  58. </button>
  59. </div>
  60. </div>
  61. <div class="mt-3">
  62. <div class="alert alert-info mb-0">
  63. <strong>Note:</strong>
  64. <ol class="mb-0 pl-3">
  65. <li>
  66. Kindly download the template, <b>update only the absentees’ Sl.No.</b> along with the required details, and upload the Excel file.
  67. <!-- Only the <b>Attendance Status</b> field is editable in the downloaded Excel file. Kindly update the attendance status and upload the file.-->
  68. </li>
  69. <li>
  70. Re-uploading will <b>clear previous attendance </b> and new attendance will be captured. Please review before proceeding .
  71. </li>
  72. </ol>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. <div class="row mt-1">
  79. <div class="col-md-12">
  80. <div class="card mt-2">
  81. <div class="card-header" style="font-weight: bold;">
  82. Upload Attendance Training Data
  83. </div>
  84. <div class="card-body d-flex align-items-end">
  85. <!-- Dropdown -->
  86. <div class="col-sm-4">
  87. <label for="dattendance">Select Training</label>
  88. <select id="dattendance" class="form-control">
  89. <option value="">Select</option>
  90. <!-- <option value="1" selected="selected">First Training Attendance</option>-->
  91. <!-- <option value="2" selected="selected">Second Training Attendance</option> -->
  92. <!-- <option value="3" selected="selected">Second Repeat Training Attendance</option> -->
  93. <option value="4" selected="selected">Third Training Attendance</option>
  94. </select>
  95. </div>
  96. <div class="col-sm-4">
  97. <label for="fileUpload">File Upload</label>
  98. <input type="file" id="fileUpload" class="form-control" accept=".xlsx,.csv">
  99. </div>
  100. <!-- Button (hidden initially) -->
  101. <div class="col-sm-4" id="uploadBtnDiv">
  102. <button id="uploadTemplateBtn" class="btn btn-primary">
  103. File Upload
  104. </button>
  105. <button id="resetBtn" class="btn btn-secondary">
  106. Reset
  107. </button>
  108. </div>
  109. </div>
  110. <div class="mt-3">
  111. <div class="alert alert-info mb-0">
  112. <strong>Note:</strong>
  113. <ol class="mb-0 pl-3">
  114. <li>
  115. Kindly download the template, <b>update only the absentees’ Sl.No.</b> along with the required details, and upload the Excel file.
  116. </li>
  117. <li>
  118. Re-uploading will <b>clear previous attendance </b> and new attendance will be captured. Please review before proceeding .
  119. </li>
  120. </ol>
  121. </div>
  122. </div>
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. </div>
  128. <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;">
  129. <div style="position: relative; top: 50%; transform: translateY(-50%);">
  130. <div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
  131. <span class="sr-only">Loading...</span>
  132. </div>
  133. <!-- Add the 'Please wait' text below the spinner -->
  134. <p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p>
  135. </div>
  136. </div>
  137. <div th:replace="~{fragments/modules/base/app_module_menu_box :: div}"></div>
  138. <footer th:replace="~{fragments/modules/base/footer :: footer}"></footer>
  139. </div>
  140. </div>
  141. <script th:replace="~{fragments/modules/base/script :: script}"></script>
  142. <script src="dist/js/xlsx.full.min.js"></script>
  143. <!-- \static\assets\dist\js\xlsx.full.min.js -->
  144. <script type="text/javascript">
  145. let userId = document.getElementById('userIdSpan').textContent;
  146. let mapped_ac = document.getElementById('mapped_ac').textContent;
  147. $(document).ready(function() {
  148. $.ajax({
  149. url: "/election/api/dataentryadmin/attendance/trainingconstituencies",
  150. method: "GET",
  151. data:{c_code:mapped_ac},
  152. success: function (data) {
  153. console.log("Response:", data);
  154. let spanText = "";
  155. if (data && data.length > 0) {
  156. $.each(data, function (index, item) {
  157. spanText += item.c_code +'-'+ item.display_name;
  158. // if multiple records → add comma or line break
  159. if (index !== data.length - 1) {
  160. spanText += ", ";
  161. // OR use "<br>" for new line
  162. }
  163. });
  164. } else {
  165. spanText = "No Constituency Found";
  166. }
  167. $('#constituencySpan').html(spanText);
  168. },
  169. error: function () {
  170. alert("Failed to fetch allocated training constituency");
  171. }
  172. });
  173. });
  174. $('#attendance').on('change', function () {
  175. let selectedAtt = $(this).val();
  176. if (selectedAtt) {
  177. console.log("selectedAtt="+selectedAtt);
  178. console.log("userId="+userId);
  179. console.log("mapped_ac="+mapped_ac);
  180. if(!userId){
  181. Swal.fire({
  182. icon: 'warning',
  183. title: 'No UserId Mapped',
  184. text: 'Issue in login userid'
  185. });
  186. return;
  187. }
  188. if(!mapped_ac){
  189. Swal.fire({
  190. icon: 'warning',
  191. title: 'No Mapped Ac',
  192. text: 'Issue in AC Mapping'
  193. });
  194. //$('#downloadBtnDiv').hide();
  195. return;
  196. }
  197. //$('#downloadBtnDiv').show();
  198. }
  199. else{
  200. //$('#downloadBtnDiv').hide();
  201. return;
  202. }
  203. });
  204. $('#downloadTemplateBtn').on('click', function () {
  205. let ctraining=$('#attendance').val();
  206. if(!ctraining){
  207. Swal.fire({
  208. icon: 'warning',
  209. title: 'Missing Training',
  210. text: 'Please Select Training'
  211. });
  212. return;
  213. }
  214. getTemplate(mapped_ac,ctraining);
  215. });
  216. $('#resetBtn').on('click', function () {
  217. $('#fileUpload').val('');
  218. });
  219. $('#dattendance').on('change', function () {
  220. let selectedAtt = $(this).val();
  221. if (selectedAtt) {
  222. console.log("selectedAtt="+selectedAtt);
  223. console.log("userId="+userId);
  224. console.log("mapped_ac="+mapped_ac);
  225. if(!userId){
  226. Swal.fire({
  227. icon: 'warning',
  228. title: 'No UserId Mapped',
  229. text: 'Issue in login userid'
  230. });
  231. return;
  232. }
  233. if(!mapped_ac){
  234. Swal.fire({
  235. icon: 'warning',
  236. title: 'No Mapped Ac',
  237. text: 'Issue in AC Mapping'
  238. });
  239. //$('#downloadBtnDiv').hide();
  240. return;
  241. }
  242. //$('#uploadBtnDiv').show();
  243. }
  244. else{
  245. //$('#uploadBtnDiv').hide();
  246. return;
  247. }
  248. });
  249. $('#uploadTemplateBtn').on('click', function () {
  250. let fileInput = $('#fileUpload')[0];
  251. let file = fileInput.files[0];
  252. let training=$('#dattendance').val();
  253. if (!training) {
  254. Swal.fire({
  255. icon: 'warning',
  256. title: 'No Attendance Training Selected',
  257. text: 'Please Select Attendance Training'
  258. });
  259. return;
  260. }
  261. if (!file) {
  262. Swal.fire({
  263. icon: 'warning',
  264. title: 'No File Selected',
  265. text: 'Please choose a file to upload'
  266. });
  267. return;
  268. }
  269. let fileName = file.name.toLowerCase();
  270. if (!(fileName.endsWith('.xlsx') || fileName.endsWith('.csv'))) {
  271. Swal.fire({
  272. icon: 'error',
  273. title: 'Invalid File Type',
  274. text: 'Only .xlsx or .csv files are allowed'
  275. });
  276. $('#fileUpload').val('');
  277. return;
  278. }
  279. // Valid file
  280. console.log("File ready for upload:", fileName);
  281. uploadfile(training,userId,mapped_ac,file);
  282. });
  283. function uploadfile(training, userId, mapped_ac, file) {
  284. if(training=="4"){
  285. const expectedHeaders = ["HALL_NO","CATEGORY","SLNO", "NAME"];
  286. const reader = new FileReader();
  287. reader.onload = function (e) {
  288. const data = new Uint8Array(e.target.result);
  289. const workbook = XLSX.read(data, { type: 'array' });
  290. const sheetName = workbook.SheetNames[0];
  291. const sheet = workbook.Sheets[sheetName];
  292. const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });
  293. // 1. Check Header
  294. let header = jsonData[0];
  295. if (!header || header.length < 4) {
  296. Swal.fire("Invalid File", "Header row missing or incorrect", "error");
  297. return;
  298. }
  299. for (let i = 0; i < expectedHeaders.length; i++) {
  300. if ((header[i] || '').toString().trim().toUpperCase() !== expectedHeaders[i]) {
  301. Swal.fire("Invalid Header", "Excel header mismatch", "error");
  302. return;
  303. }
  304. }
  305. // 2. Validate Rows
  306. let validData = [];
  307. for (let i = 1; i < jsonData.length; i++) {
  308. let row = jsonData[i];
  309. if (!row || row.length === 0) continue;
  310. let hallno =row[0];
  311. let cat =row[1];
  312. let slno = row[2];
  313. let name = row[3];
  314. // Empty check
  315. if (!hallno || !cat ||!slno || !name) {
  316. Swal.fire("Invalid Data", `Empty cell found at row ${i + 1}`, "error");
  317. return;
  318. }
  319. // HallNo validation (reject 01, 02, etc.)
  320. let hallStr = hallno.toString().trim();
  321. if (hallStr.startsWith('0') && hallStr.length > 1) {
  322. Swal.fire("Invalid HallNo", `HallNo should not start with 0 at row ${i + 1}`, "error");
  323. return;
  324. }
  325. validData.push({
  326. hallno:hallno,
  327. cat:cat,
  328. slno: slno,
  329. name: name
  330. });
  331. }
  332. $.ajax({
  333. url: "/election/api/election_attendance/getAttendanceId",
  334. type: "GET",
  335. data: {
  336. training: training,
  337. mappedAc: mapped_ac // match param name in controller
  338. },
  339. success: function (res) {
  340. console.log("Attendance ID response:", res);
  341. let attendanceFileId = res.attendanceId;
  342. if (!attendanceFileId) {
  343. saveAttendanceData(training, userId, mapped_ac, validData, file);
  344. return;
  345. }
  346. Swal.fire({
  347. title: "Data Already Exists",
  348. text: "Do you want to overwrite existing attendance?",
  349. icon: "warning",
  350. showCancelButton: true,
  351. confirmButtonText: "Yes, Update",
  352. cancelButtonText: "Cancel"
  353. }).then((result) => {
  354. if (result.isConfirmed) {
  355. $.ajax({
  356. url: "/election/api/election_attendance/deactivateAttendance",
  357. type: "POST",
  358. contentType: "application/json",
  359. data: JSON.stringify({
  360. attendanceId: attendanceFileId,
  361. data : validData,
  362. mappedAc:mapped_ac
  363. }),
  364. success: function (updateRes) {
  365. console.log("Deactivate response:", updateRes);
  366. if (updateRes.status === "error") {
  367. Swal.fire({
  368. icon: "error",
  369. title: updateRes.message
  370. });
  371. return;
  372. }
  373. saveAttendanceData(training, userId, mapped_ac, validData, file);
  374. },
  375. error: function () {
  376. Swal.fire("Error", "Failed to deactivate old data", "error");
  377. }
  378. });
  379. } else {
  380. console.log("User cancelled");
  381. }
  382. });
  383. },
  384. error: function () {
  385. Swal.fire("Error", "Failed to fetch attendance file ID", "error");
  386. }
  387. });
  388. };
  389. reader.readAsArrayBuffer(file);
  390. }
  391. /* else if(training=="5"){
  392. Swal.fire("Please Select Current Training", "No Template For this Training", "error");
  393. return;
  394. } */
  395. else{
  396. Swal.fire("Please Select Current Training", "No Template For this Training", "error");
  397. return;
  398. }
  399. }
  400. function saveAttendanceData(training, userId, mapped_ac, dataList, file) {
  401. $('#pageLoader').show();
  402. let formData = new FormData();
  403. formData.append("file", file);
  404. formData.append("training", training);
  405. formData.append("userId", userId);
  406. formData.append("mapped_ac", mapped_ac);
  407. formData.append("data", JSON.stringify(dataList));
  408. $.ajax({
  409. url: '/election/api/election_attendance/uploadAttendance',
  410. type: 'POST',
  411. data: formData,
  412. processData: false,
  413. contentType: false,
  414. success: function () {
  415. $('#pageLoader').hide();
  416. Swal.fire({
  417. icon: 'success',
  418. title: 'Uploaded Successfully',
  419. allowOutsideClick: false,
  420. allowEscapeKey: false,
  421. allowEnterKey: false
  422. }).then(() => {
  423. location.reload();
  424. });
  425. },
  426. error: function (xhr) {
  427. $('#pageLoader').hide();
  428. let msg = "Upload Failed";
  429. if (xhr.responseText) {
  430. msg = xhr.responseText;
  431. }
  432. Swal.fire({
  433. icon: 'warning',
  434. title: msg
  435. });
  436. }
  437. });
  438. }
  439. function getTemplate(mapped_ac,ctraining) {
  440. if (!mapped_ac) {
  441. Swal.fire({
  442. icon: 'warning',
  443. title: 'Missing AC',
  444. text: 'AC value is required'
  445. });
  446. return;
  447. }
  448. if (!ctraining) {
  449. Swal.fire({
  450. icon: 'warning',
  451. title: 'Missing Training',
  452. text: 'Select Training is required'
  453. });
  454. return;
  455. }
  456. $('#pageLoader').show();
  457. // Step 1: Check data first
  458. $.ajax({
  459. url: '/election/api/election_attendance/checkAttendanceData',
  460. type: 'GET',
  461. data: { mapped_ac: mapped_ac,ctraining:ctraining },
  462. success: function (res) {
  463. if (res.status) {
  464. // Step 2: Trigger download using iframe
  465. let iframe = document.createElement('iframe');
  466. iframe.style.display = 'none';
  467. iframe.src = '/election/api/election_attendance/getAttendanceExcel?mapped_ac=' + mapped_ac+'&ctraining='+ctraining;
  468. document.body.appendChild(iframe);
  469. // Keep loader for a few seconds (until download triggers)
  470. setTimeout(function () {
  471. $('#pageLoader').hide();
  472. }, 3000);
  473. } else {
  474. $('#pageLoader').hide();
  475. Swal.fire({
  476. icon: 'warning',
  477. title: res.message
  478. });
  479. }
  480. },
  481. error: function () {
  482. $('#pageLoader').hide();
  483. Swal.fire({
  484. icon: 'error',
  485. title: 'Error',
  486. text: 'Unable to process request'
  487. });
  488. }
  489. });
  490. }
  491. </script>
  492. </body>
  493. </html>