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.

986 lines
35 KiB

2 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
2 years ago
2 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
2 years ago
2 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
2 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
2 years ago
​
​
​
​
​
  1. <!DOCTYPE html>
  2. <html lang="en" dir="">
  3. <html xmlns:th="http://www.thymeleaf.org">
  4. <head th:replace="~{fragments/modules/base/head :: head}"></head>
  5. <style>
  6. /* Base Layout and Styling */
  7. /* Base Layout and Styling */
  8. body {
  9. font-family: 'Arial', sans-serif;
  10. background-color: #f8f9fa;
  11. }
  12. /* Card Styling */
  13. .card-box {
  14. border-radius: 12px;
  15. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  16. background-color: #ffffff;
  17. padding: 20px;
  18. transition: transform 0.3s ease-in-out;
  19. }
  20. .card-box:hover {
  21. transform: translateY(-5px);
  22. }
  23. /* Title Styling */
  24. h3 {
  25. font-size: 1.2rem;
  26. color: #444;
  27. }
  28. /* Styling for Overall Pendency and Stage Wise Sections */
  29. .charts {
  30. display: flex;
  31. flex-wrap: wrap;
  32. gap: 30px; /* Increased gap between charts */
  33. margin-top: 30px; /* Increased margin-top */
  34. }
  35. .chart {
  36. flex: 1;
  37. background: #ffffff; /* White background */
  38. padding: 20px;
  39. border-radius: 8px;
  40. border: 2px solid #1e90ff; /* Add a colored border */
  41. width: 100%;
  42. box-sizing: border-box;
  43. text-align: center;
  44. transition: all 0.3s ease-in-out;
  45. }
  46. .chart:hover {
  47. box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
  48. }
  49. .chart h3 {
  50. font-size: 1.2rem;
  51. color: #333;
  52. margin-bottom: 15px;
  53. }
  54. .chart canvas {
  55. display: block;
  56. max-width: 100%;
  57. height: auto;
  58. }
  59. /* Stats Cards */
  60. .stats {
  61. display: flex;
  62. flex-wrap: wrap;
  63. justify-content: center;
  64. gap: 30px; /* Increased gap between stats cards */
  65. margin-top: 30px; /* Increased margin-top */
  66. }
  67. .stat-card {
  68. background: #ffffff;
  69. border: 2px solid #1e90ff;
  70. padding: 20px;
  71. border-radius: 8px;
  72. text-align: center;
  73. flex: 1;
  74. margin: 10px; /* Reduced margin for cleaner look */
  75. }
  76. .stat-card:hover {
  77. transform: scale(1.05);
  78. box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
  79. }
  80. .stat-card h2 {
  81. margin: 0;
  82. font-size: 36px;
  83. color: #007bff;
  84. }
  85. .stat-card p {
  86. margin: 5px 0 0;
  87. font-size: 13px;
  88. color: #555;
  89. font-weight:bold;
  90. }
  91. /* Buttons */
  92. .btn-primary:hover {
  93. background-color: #0056b3;
  94. }
  95. .btn-warning:hover {
  96. background-color: #e0a800;
  97. }
  98. /* Container Styling */
  99. .container {
  100. max-width: 1200px;
  101. margin: 20px auto;
  102. padding: 20px;
  103. background: #fff;
  104. border-radius: 8px;
  105. box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  106. }
  107. /* Date Input Row */
  108. .row.mt-3.ml-2 {
  109. margin-top: 20px; /* Increased margin-top */
  110. }
  111. /* Responsive Design */
  112. @media (min-width: 768px) {
  113. .charts {
  114. flex-direction: row;
  115. }
  116. .charts .chart {
  117. width: calc(50% - 15px); /* Increased gap between charts */
  118. }
  119. .stats {
  120. flex-direction: row;
  121. }
  122. }
  123. @media (max-width: 576px) {
  124. .charts,
  125. .stats {
  126. flex-direction: column;
  127. }
  128. .stat-card {
  129. margin: 15px 0; /* Increased margin for better spacing */
  130. }
  131. }
  132. .file-history-list {
  133. margin-top: 10px;
  134. padding-left: 0;
  135. }
  136. .file-history-item {
  137. display: flex;
  138. padding: 5px 10px;
  139. font-family: Arial, sans-serif;
  140. align-items: flex-start;
  141. position: relative; /* For vertical line positioning */
  142. margin-bottom: 1px; /* Add space between items */
  143. }
  144. .file-history-item::before {
  145. content: '';
  146. position: absolute;
  147. top: 0;
  148. bottom: 0;
  149. left: 120px; /* Position of the vertical line */
  150. width: 1px;
  151. background-color: #ddd;
  152. }
  153. .file-history-date {
  154. margin-top: 10px;
  155. flex: 0 0 100px;
  156. font-weight: bold;
  157. font-size: 0.8rem;
  158. color: #333;
  159. text-align: center;
  160. white-space: nowrap; /* Prevent wrapping */
  161. }
  162. .file-history-content {
  163. flex: 5;
  164. padding-left: 20px; /* Adds space after the vertical line */
  165. display: flex;
  166. flex-direction: column;
  167. gap: 5px; /* Adds spacing between status and remarks */
  168. white-space: pre-wrap; /* Prevents wrapping */
  169. overflow: visible; /* Prevents content overflow */
  170. text-overflow: ellipsis; /* Adds ellipsis if content exceeds container width */
  171. }
  172. .file-history-status {
  173. font-size: 0.8rem;
  174. font-weight: bold;
  175. color: #555;
  176. white-space:pre-wrap; /* Prevent wrapping */
  177. overflow: visible;
  178. text-overflow: ellipsis;
  179. }
  180. .file-history-remarks {
  181. font-size: 0.8rem;
  182. color: #777;
  183. white-space: pre-wrap; /* Prevent wrapping */
  184. overflow: visible;
  185. text-overflow: ellipsis;
  186. }
  187. #EmpcodeDetailsModal .modal-body{
  188. max-height:80vh;
  189. overflow-y:auto;
  190. }
  191. </style>
  192. <body>
  193. <div class="wrapper">
  194. <header th:replace="~{fragments/modules/base/header :: header}"></header>
  195. <div class="page-wrap">
  196. <div th:replace="~{fragments/modules/base/menu :: menu}"></div>
  197. <div class="main-content">
  198. <div class="container-fluid">
  199. <div class="page-header mb-2">
  200. <div class="row align-items-end">
  201. <div class="col-lg-8">
  202. <div class="page-header-title">
  203. <i class="fa fa-list-alt bg-blue"></i>
  204. <div class="d-inline">
  205. <h5>Dashboard</h5>
  206. <span>Department Entries</span>
  207. </div>
  208. </div>
  209. </div>
  210. <div class="col-lg-4">
  211. <nav class="breadcrumb-container" aria-label="breadcrumb">
  212. <ol class="breadcrumb">
  213. <li class="breadcrumb-item"><a href="/gcc/index"><i class="ik ik-home"></i></a></li>
  214. <li class="breadcrumb-item">Department Entries</li>
  215. <li class="breadcrumb-item active" aria-current="page">Dashboard</li>
  216. </ol>
  217. </nav>
  218. </div>
  219. </div>
  220. </div>
  221. <div class="row mt-4">
  222. <div class="col-md-12">
  223. <div class="card">
  224. <div class="card-block">
  225. <h5 style="font-weight:bold;"><span th:text="${deptId}" id="deptId" style="display:none"></span></h5>
  226. <h5 style="font-weight:bold;"><span th:text="${deptname}" id="deptname"></span> Department</h5>
  227. <div class="d-flex justify-content-end align-items-center">
  228. <label for="EmpCode" style="font-weight: bold; margin-right: 10px;" class="mb-0">EmpCode:</label>
  229. <input type="text" id="EmpCode" name="EmpCode" class="form-control" style="width: auto; min-width: 150px; margin-right: 10px;">
  230. <button id="searchEmpButton" class="btn btn-primary">Search</button>
  231. </div>
  232. <div class="row mt-3 ml-2 ">
  233. <div class="col-md-3">
  234. <label for="startDate" class="form-label">From Date:</label>
  235. <input type="date" id="startDate" class="form-control" onclick="this.showPicker()">
  236. </div>
  237. <div class="col-md-3">
  238. <label for="endDate" class="form-label">To Date:</label>
  239. <input type="date" id="endDate" class="form-control" onclick="this.showPicker()">
  240. </div>
  241. <div class="col-12 d-flex justify-content-center mt-3">
  242. <button id="searchButton" class="btn btn-primary mx-2">Search</button>
  243. <input type="reset" class="btn btn-warning mx-2" value="Reset">
  244. </div>
  245. </div>
  246. <div id="stats" class="stats"></div>
  247. <div class="charts">
  248. <div class="chart">
  249. <h3>File Category - Overall</h3>
  250. <div id="overallPieChart" style="width: 100%; height: 400px;"></div>
  251. </div>
  252. <div class="chart">
  253. <h3>File Category Stages Wise</h3>
  254. <div id="stagesBarChart" style="width: 100%; height: 400px;"></div>
  255. </div>
  256. </div>
  257. </div>
  258. </div>
  259. </div>
  260. </div>
  261. </div>
  262. </div>
  263. <footer th:replace="~{fragments/modules/base/footer :: footer}"></footer>
  264. </div>
  265. </div>
  266. <!-- Modal -->
  267. <div class="modal fade" id="zoneDetailsModal" tabindex="-1"
  268. role="dialog" aria-labelledby="zoneDetailsModalLabel"
  269. aria-hidden="true" data-backdrop="static" data-keyboard="false">
  270. <div class="modal-dialog modal-lg" role="document">
  271. <div class="modal-content">
  272. <div id="zone-details-content"></div>
  273. </div>
  274. </div>
  275. </div>
  276. <div class="modal fade" id="EmpcodeDetailsModal" tabindex="-1"
  277. role="dialog" aria-labelledby="zoneDetailsModalLabel"
  278. aria-hidden="true" data-backdrop="static" data-keyboard="false">
  279. <div class="modal-dialog modal-xl" role="document">
  280. <div class="modal-content">
  281. <div id="emp-details-content">
  282. <div class="col-md-12 mt-4">
  283. <h5>File History</h5>
  284. <div id="fileHistoryContainer" class="file-history-list">
  285. File History will be dynamically loaded here
  286. </div>
  287. </div>
  288. </div>
  289. </div>
  290. </div>
  291. </div>
  292. <div th:replace="~{fragments/modules/base/app_module_menu_box :: div}"></div>
  293. <script th:replace="~{fragments/modules/base/script :: script}"></script>
  294. <script src="node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.min.js"></script>
  295. <!-- Bootstrap 5 JS -->
  296. <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
  297. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  298. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  299. <script src="/assets/node_modules/amcharts3/amcharts/amcharts.js"></script>
  300. <script src="/assets/node_modules/amcharts3/amcharts/serial.js"></script>
  301. <script src="/assets/node_modules/amcharts3/amcharts/pie.js"></script>
  302. <script src="/assets/node_modules/amcharts3/amcharts/themes/light.js"></script>
  303. <script>
  304. document.addEventListener('DOMContentLoaded', function () {
  305. // Function to fetch data from /dashboard endpoint
  306. $('#searchEmpButton').on('click', function () {
  307. const Empcode = document.getElementById('EmpCode').value.trim();
  308. if (!Empcode) {
  309. Swal.fire({
  310. icon: "warning",
  311. title: "Enter Employee Code",
  312. text: "Please enter an Employee Code before searching.",
  313. confirmButtonText: "OK",
  314. });
  315. return;
  316. }
  317. // First API call to fetch employee details
  318. $.ajax({
  319. url: `/fetchEmpDetails?Empcode=${Empcode}`,
  320. type: 'GET',
  321. success: function (response) {
  322. if (!response || response.length === 0) {
  323. Swal.fire({
  324. icon: "warning",
  325. title: "No Data Found",
  326. text: "No records found for the entered Employee Code.",
  327. confirmButtonText: "OK",
  328. });
  329. return;
  330. }
  331. const isGISEmployee = response.some(emp => emp.file_name === "GIS");
  332. // Second API call to check department validity (only if not GIS)
  333. $.ajax({
  334. url: '/gcc/api/department/newentry/employeedetails',
  335. type: 'GET',
  336. data: { empCode: Empcode },
  337. success: function (deptResponse) {
  338. if (deptResponse.list && deptResponse.list.length > 0) {
  339. let employeeDetails = deptResponse.list[0];
  340. const loggedInDeptId = $('#deptId').text().trim();
  341. if (employeeDetails.ID_DEPT !== loggedInDeptId) {
  342. Swal.fire({
  343. icon: "error",
  344. title: "Access Denied",
  345. text: "This employee does not belong to your department.",
  346. confirmButtonText: "OK",
  347. });
  348. } else {
  349. renderEmpDetailsModal(response);
  350. }
  351. } else {
  352. if (isGISEmployee) {
  353. renderEmpDetailsModal(response);
  354. } else {
  355. Swal.fire({
  356. icon: "warning",
  357. title: "No Data Found",
  358. text: "No details found for this employee number.",
  359. confirmButtonText: "OK",
  360. });
  361. }
  362. }
  363. },
  364. error: function () {
  365. Swal.fire({
  366. icon: "error",
  367. title: "Error",
  368. text: "An error occurred while fetching department details.",
  369. confirmButtonText: "OK",
  370. });
  371. }
  372. });
  373. },
  374. error: function (xhr) {
  375. console.error("Error fetching employee details:", xhr.status, xhr.responseText);
  376. }
  377. });
  378. });
  379. function renderEmpDetailsModal(data) {
  380. const Empcode = document.getElementById('EmpCode').value.trim();
  381. let modalContent = `
  382. <div class="modal-header">
  383. <h5 class="modal-title">Employee Details for ${Empcode}</h5>
  384. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  385. <span aria-hidden="true">&times;</span>
  386. </button>
  387. </div>
  388. <div class="modal-body">
  389. <div class="table-responsive">
  390. <table id="DashboardDatatable" class="table table-striped table-bordered table-hover dataTable">
  391. <thead class="thead-dark">
  392. <tr>
  393. <th class="text-center">S.NO</th>
  394. <th class="text-center">Department Name</th>
  395. <th class="text-center">File Name</th>
  396. <th class="text-center">File Status</th>
  397. <th class="text-center">History</th>
  398. </tr>
  399. </thead>
  400. <tbody>
  401. ${data.map((item, index) => `
  402. <tr data-temp-id="${item.temp_id}">
  403. <td>${index + 1}</td>
  404. <td>${item.dept_name}</td>
  405. <td>${item.file_name}</td>
  406. <td>${item.file_status}</td>
  407. <td class="file-history-column" id="history-${item.temp_id}">Loading...</td>
  408. </tr>
  409. `).join('')}
  410. </tbody>
  411. </table>
  412. </div>
  413. </div>
  414. `;
  415. document.getElementById('emp-details-content').innerHTML = modalContent;
  416. let fetchPromises = data.map(item => fetchDetailsByTempId(item.temp_id));
  417. Promise.all(fetchPromises).then(() => {
  418. $('#EmpcodeDetailsModal').modal({
  419. backdrop: 'static',
  420. keyboard: false
  421. });
  422. // **Initialize DataTable AFTER Data is Loaded**
  423. setTimeout(() => {
  424. let table = $('#DashboardDatatable').DataTable({
  425. dom: 'Bftip',
  426. columnDefs: [{ targets: 'no-sort', orderable: false }],
  427. lengthMenu: [
  428. [50, 100, 200, 500, -1],
  429. ['50 rows', '100 rows', '200 rows', '500 rows', 'Show all']
  430. ],
  431. scrollY: '50vh',
  432. buttons: [
  433. { extend: 'pageLength', className: 'btn btn-warning' },
  434. {
  435. extend: 'copyHtml5',
  436. text: 'Copy',
  437. className: 'btn btn-secondary',
  438. exportOptions: { columns: ':visible', modifier: { page: 'all' } }
  439. },
  440. {
  441. extend: 'excelHtml5',
  442. text: 'Excel',
  443. className: 'btn btn-secondary',
  444. customize: function (xlsx) {
  445. let sheet = xlsx.xl.worksheets['sheet1.xml'];
  446. $('row c[r]', sheet).attr('s', '55'); // Apply word-wrap formatting
  447. }
  448. },
  449. {
  450. extend: 'csvHtml5',
  451. text: 'CSV',
  452. className: 'btn btn-secondary',
  453. exportOptions: { columns: ':visible' }
  454. },
  455. {
  456. extend: 'pdfHtml5',
  457. text: 'PDF',
  458. className: 'btn btn-secondary',
  459. orientation: 'landscape',
  460. pageSize: 'A4',
  461. customize: function (doc) {
  462. doc.styles.tableBodyEven.alignment = 'left';
  463. doc.styles.tableBodyOdd.alignment = 'left';
  464. doc.styles.tableHeader.alignment = 'center';
  465. doc.defaultStyle.fontSize = 9;
  466. doc.styles.tableHeader.fontSize = 10;
  467. doc.content[1].table.widths = Array(doc.content[1].table.body[0].length + 1).join('*').split('');
  468. }
  469. }
  470. ],
  471. responsive: false,
  472. processing: false
  473. });
  474. // ✅ Adjust column width after table is fully initialized
  475. }, 200);
  476. });
  477. }
  478. // **Ensure Header Aligns Properly When Modal is Opened**
  479. // **Fetch History & Update Table**
  480. function fetchDetailsByTempId(tempId) {
  481. return new Promise((resolve, reject) => {
  482. $.ajax({
  483. url: `/fetchTempDetails?tempId=${tempId}`,
  484. type: 'GET',
  485. success: function (response) {
  486. let historyContainer = $(`#history-${tempId}`);
  487. if (response.length > 0) {
  488. let historyHtml = response.map(item => `
  489. <div class="file-history-item">
  490. <div class="file-history-date">${formatDateTime(item.file_moved_date)}</div>
  491. <div class="file-history-content">
  492. <div class="file-history-status">${item.file_status}</div>
  493. <div class="file-history-remarks">Comments/Remarks: ${item.remarks || 'N/A'}</div>
  494. </div>
  495. </div>
  496. `).join('');
  497. historyContainer.html(historyHtml);
  498. $('#EmpcodeDetailsModal').modal({
  499. backdrop: 'static',
  500. keyboard: false
  501. });
  502. $('#EmpcodeDetailsModal').modal('show');
  503. $('.close').click(function () {
  504. $('#EmpcodeDetailsModal').modal('hide');
  505. });
  506. } else {
  507. historyContainer.html('<p>No file history available.</p>');
  508. $('#EmpcodeDetailsModal').modal({
  509. backdrop: 'static',
  510. keyboard: false
  511. });
  512. $('#EmpcodeDetailsModal').modal('show');
  513. $('.close').click(function () {
  514. $('#EmpcodeDetailsModal').modal('hide');
  515. });
  516. }
  517. resolve();
  518. },
  519. error: function () {
  520. $(`#history-${tempId}`).html('<p>Failed to load file history.</p>');
  521. $('#EmpcodeDetailsModal').modal({
  522. backdrop: 'static',
  523. keyboard: false
  524. });
  525. $('#EmpcodeDetailsModal').modal('show');
  526. $('.close').click(function () {
  527. $('#EmpcodeDetailsModal').modal('hide');
  528. });
  529. resolve();
  530. }
  531. });
  532. });
  533. }
  534. // **Helper Function to Format Date**
  535. function formatDateTime(dateString) {
  536. const options = { day: '2-digit', month: '2-digit', year: 'numeric' };
  537. return new Date(dateString).toLocaleDateString('en-GB', options);
  538. }
  539. function loadDashboardData() {
  540. const deptId = $('#deptId').text().trim();
  541. console.log(deptId);
  542. $.ajax({
  543. url: `/deparmentdashboard?deptId=${encodeURIComponent(deptId)}`,
  544. type: 'GET',
  545. contentType: 'application/json',
  546. success: function (data) {
  547. const statsDiv = document.getElementById('stats');
  548. statsDiv.innerHTML = ''; // Clear previous stats
  549. data.forEach(item => {
  550. const statCard = document.createElement('div');
  551. statCard.className = 'stat-card';
  552. statCard.innerHTML = `
  553. <h2>${item.category_count}</h2>
  554. <p>${item.file_name}</p>
  555. `;
  556. statsDiv.appendChild(statCard);
  557. });
  558. // Call renderCharts after data is loaded
  559. renderCharts();
  560. },
  561. error: function (xhr) {
  562. console.error('Error fetching dashboard data:', xhr.status, xhr.responseText);
  563. }
  564. });
  565. }
  566. // Function to handle search button click
  567. document.getElementById('searchButton').addEventListener('click', function () {
  568. const startDate = document.getElementById('startDate').value;
  569. const endDate = document.getElementById('endDate').value;
  570. const deptId = $('#deptId').text().trim();
  571. if (!startDate || !endDate) {
  572. Swal.fire({
  573. icon: "warning",
  574. title: "Incomplete Search",
  575. text: "Please select the Date Before searching",
  576. confirmButtonText: "OK",
  577. });
  578. } else {
  579. $.ajax({
  580. url: `/dashboardsearch?startdate=${startDate}&enddate=${endDate}&deptId=${encodeURIComponent(deptId)}`,
  581. type: 'GET',
  582. success: function (data) {
  583. const statsDiv = document.getElementById('stats');
  584. statsDiv.innerHTML = data.map(item => `
  585. <div class='stat-card'>
  586. <h2>${item.category_count}</h2>
  587. <p>${item.file_name}</p>
  588. </div>
  589. `).join('');
  590. // Re-render charts after search results load
  591. renderCharts(startDate, endDate);
  592. },
  593. error: function (xhr) {
  594. console.error('Error fetching search data:', xhr.status, xhr.responseText);
  595. }
  596. });
  597. }
  598. });
  599. // Function to validate and set min-date for endDate
  600. /* function validateEndDate() {
  601. const startDate = document.getElementById('startDate').value;
  602. const endDateInput = document.getElementById('endDate');
  603. if (startDate) {
  604. const startDateObj = new Date(startDate);
  605. const minEndDateISO = startDateObj.toISOString().split('T')[0];
  606. endDateInput.setAttribute('min', minEndDateISO);
  607. // Clear endDate if it's less than startDate
  608. if (endDateInput.value && new Date(endDateInput.value) < startDateObj) {
  609. endDateInput.value = '';
  610. }
  611. }
  612. }
  613. document.getElementById('startDate').addEventListener('change', validateEndDate);
  614. */
  615. function renderCharts(startDate = null, endDate = null) {
  616. const deptId = $('#deptId').text().trim();
  617. // Dispose existing amCharts instances
  618. disposeExistingCharts();
  619. function disposeExistingCharts() {
  620. if (window.chart1) {
  621. window.chart1.clear();
  622. window.chart1 = null;
  623. }
  624. if (window.chart2) {
  625. window.chart2.clear();
  626. window.chart2 = null;
  627. }
  628. }
  629. // Render pie chart
  630. const fixedColors = {
  631. "Retirement": "#6A0572", // Deep Purple
  632. "Family Pension": "#2C3E50", // Dark Blue-Gray
  633. "Pendency": "#A569BD", // Soft Lavender
  634. "GIS": "#17A589", // Teal Green
  635. "Provisional Retirement": "#D35400", // Warm Orange
  636. "Contributory Pension Scheme": "#5D6D7E" // Muted Gray-Blue
  637. };
  638. $.ajax({
  639. url: `/categoryCounts`,
  640. type: 'GET',
  641. data: { deptId, startDate, endDate },
  642. dataType: 'json',
  643. success: function (data) {
  644. const chartData = data.map((item, index) => ({
  645. category: item.file_name,
  646. value: item.category_count,
  647. color: fixedColors[item.file_name], // Assign colors cyclically
  648. id: item.id
  649. }));
  650. window.chart1 = AmCharts.makeChart("overallPieChart", {
  651. type: "pie",
  652. theme: "dark",
  653. dataProvider: chartData,
  654. valueField: "value",
  655. titleField: "category",
  656. colorField: "color",
  657. radius: "40%",
  658. innerRadius: "50%",
  659. labelText: "[[[[title]]]] - [[[[value]]]]",
  660. fontSize: 14,
  661. labelsEnabled: true,
  662. pullOutRadius: 0,
  663. gradientRatio: [-0.4, -0.4, -0.4, -0.4, -0.4, -0.4, 0, 0.1, 0.2, 0.1, 0, -0.2, -0.5],
  664. export: {
  665. enabled: true,
  666. },
  667. responsive: {
  668. enabled: true,
  669. },
  670. legend: {
  671. position: "bottom",
  672. markerType: "circle",
  673. valueText: "[[[[value]]]]",
  674. switchable: false,
  675. markerSize: 12,
  676. fontSize: 12
  677. },
  678. balloon: {
  679. fixedPosition: true
  680. },
  681. listeners: [
  682. {
  683. event: "clickSlice",
  684. method: function (event) {
  685. const FileId = event.dataItem.dataContext.id;
  686. const FileNumber = event.dataItem.dataContext.category;
  687. fetchempno(FileId, FileNumber);
  688. }
  689. }
  690. ]
  691. });
  692. },
  693. error: function (xhr, status, error) {
  694. console.error("AJAX Request Failed:", status, error);
  695. console.error("Response Text:", xhr.responseText);
  696. }
  697. });
  698. function fetchempno(FileId, FileNumber) {
  699. $.ajax({
  700. url: `/getEmpNoByFileName?file_id=${FileId}`,
  701. type: 'GET',
  702. success: function (response) {
  703. renderEmpNoModal(FileNumber, response);
  704. },
  705. error: function (xhr) {
  706. console.error("Error fetching region details:", xhr.status, xhr.responseText);
  707. }
  708. });
  709. }
  710. function renderEmpNoModal(FileNumber, empNos) {
  711. console.log(empNos);
  712. const modalContent = `
  713. <div class="modal-header">
  714. <h5 class="modal-title">Employee Numbers for ${FileNumber}</h5>
  715. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  716. <span aria-hidden="true">&times;</span>
  717. </button>
  718. </div>
  719. <div class="modal-body">
  720. <table class="table table-bordered">
  721. <thead>
  722. <tr><th>Employee Number</th></tr>
  723. </thead>
  724. <tbody>
  725. ${empNos.map(empNo => `<tr><td>${empNo.emp_no}</td></tr>`).join('')}
  726. </tbody>
  727. </table>
  728. </div>
  729. `;
  730. document.getElementById('zone-details-content').innerHTML = modalContent;
  731. // $('#zoneDetailsModal').modal('show');
  732. $('.close').click(function () {
  733. $('#zoneDetailsModal').modal('hide');
  734. });
  735. }
  736. function getRandomColor() {
  737. const letters = '0123456789ABCDEF';
  738. let color = '#';
  739. for (let i = 0; i < 6; i++) {
  740. color += letters[Math.floor(Math.random() * 16)];
  741. }
  742. return color;
  743. }
  744. // Render bar chart
  745. $.ajax({
  746. url: `/stageWiseCounts`,
  747. type: 'GET',
  748. data: { deptId, startDate, endDate },
  749. dataType: 'json',
  750. success: function (data) {
  751. const colorMapping = {
  752. "Audit Pending": "#FF5733",
  753. "Department Pending": "#FFC300",
  754. "Pension Pending": "#28B463",
  755. "Completed": "#3498DB"
  756. };
  757. const chartData = data.map(item => ({
  758. category: item.category,
  759. value: item.category_count,
  760. color: colorMapping[item.category] || '#D3D3D3'
  761. }));
  762. window.chart2 = AmCharts.makeChart("stagesBarChart", {
  763. type: "serial",
  764. theme: "light",
  765. dataProvider: chartData,
  766. depth3D: 2,
  767. angle: 10,
  768. valueAxes: [{
  769. axisAlpha: 0,
  770. gridAlpha: 0,
  771. labelsEnabled: false,
  772. dashLength: 0
  773. }],
  774. startDuration: 1,
  775. startEffect: "easeInSine",
  776. graphs: [{
  777. balloonText: "[[[[category]]]]: [[[[value]]]]",
  778. fillAlphas: 0.8,
  779. lineAlpha: 0.3,
  780. topRadius: 1,
  781. type: "column",
  782. valueField: "value",
  783. fillColorsField: "color",
  784. columnWidth: 0.5,
  785. labelText: "[[[[value]]]]",
  786. labelPosition: "right",
  787. fontSize: 14,
  788. color: "#000000"
  789. }],
  790. categoryField: "category",
  791. categoryAxis: {
  792. gridPosition: "start",
  793. gridAlpha: 0,
  794. autoWrap: false,
  795. axisAlpha: 0,
  796. fontSize: 14, // Increases the category label size
  797. // Makes category labels bold
  798. labelRotation: 0
  799. },
  800. rotate: true,
  801. export: {
  802. enabled: true,
  803. position: "bottom-right"
  804. }
  805. });
  806. console.log("Stages Bar Chart Data:", chartData);
  807. },
  808. error: function (xhr, status, error) {
  809. console.error("AJAX Request Failed:", status, error);
  810. }
  811. });
  812. }
  813. // Initial data load
  814. loadDashboardData();
  815. document.querySelector("input[type='reset']").addEventListener('click', function () {
  816. window.location.href = "/gcc/department/dashboard";
  817. });
  818. })
  819. $('#startDate').on('change', function () {
  820. const fromDate = $(this).val();
  821. $('#endDate').attr('min', fromDate); // Update min attribute for To Date
  822. // If To Date is earlier than the new From Date, adjust it
  823. const toDate = $('#endDate').val();
  824. if (new Date(toDate) < new Date(fromDate)) {
  825. $('#endDate').val(fromDate);
  826. }
  827. });
  828. // Update From Date's max attribute when To Date changes
  829. $('#endDate').on('change', function () {
  830. const toDate = $(this).val();
  831. $('#startDate').attr('max', toDate); // Update max attribute for From Date
  832. // If From Date is later than the new To Date, adjust it
  833. const fromDate = $('#startDate').val();
  834. if (new Date(fromDate) > new Date(toDate)) {
  835. $('#startDate').val(toDate);
  836. }
  837. });
  838. //disable future dates
  839. document.addEventListener("DOMContentLoaded", function () {
  840. let today = new Date().toISOString().split("T")[0]; // Get today's date in YYYY-MM-DD format
  841. let startdateInput = document.getElementById("startDate");
  842. let enddateInput = document.getElementById("endDate");
  843. // Set max attribute to disable future dates
  844. startdateInput.setAttribute("max", today);
  845. enddateInput.setAttribute("max", today);
  846. // Prevent manual future date entry
  847. startdateInput.addEventListener("change", function () {
  848. if (this.value > today) {
  849. Swal.fire({
  850. icon: 'warning',
  851. title: 'Invalid Date',
  852. text: 'Future dates are not allowed.',
  853. confirmButtonText: 'OK'
  854. });
  855. this.value = ""; // Clear the invalid input
  856. }
  857. });
  858. });
  859. </script>
  860. </body>
  861. </html>