This portal only for GCC Officers. Based on there roles and Group ID they can view there module`s Like PGR, QR Public Asset Feedback, Makkalai Thedi Mayor,etc...
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.

1122 lines
44 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head th:replace="~{fragments/modules/base/head :: head}">
  4. </head>
  5. <style>
  6. #modalBody h4 {
  7. font-weight: bold;
  8. margin-top: 15px;
  9. margin-bottom: 10px;
  10. }
  11. #modalBody p {
  12. margin: 5px 0;
  13. }
  14. #modalBody hr {
  15. margin: 15px 0;
  16. }
  17. .comment {
  18. margin-bottom: 20px;
  19. }
  20. .comment p {
  21. margin: 0;
  22. }
  23. hr {
  24. border: 1px solid #ccc;
  25. }
  26. .col-4 {
  27. font-weight: bold;
  28. text-align: right;
  29. }
  30. .col-8 {
  31. padding-left: 20px;
  32. }
  33. .comment {
  34. padding: 10px 0;
  35. }
  36. .comment .text-muted {
  37. font-size: 0.9em;
  38. color: #6c757d;
  39. }
  40. .comment hr {
  41. border-top: 1px solid #ccc;
  42. }
  43. .comment .btn-link {
  44. font-size: 0.9em;
  45. text-decoration: underline;
  46. color: #007bff;
  47. }
  48. /* Make the modal body scrollable */
  49. #detailsModal .modal-body {
  50. max-height: 70vh; /* Limit the height to 70% of the viewport */
  51. overflow-y: auto; /* Enable vertical scrolling */
  52. }
  53. .width1{
  54. width: 100px;
  55. }
  56. .width2{
  57. width: 120px;
  58. }
  59. </style>
  60. <body>
  61. <div class="wrapper">
  62. <!-- header top menu Start -->
  63. <header th:replace="~{fragments/modules/base/header :: header}"></header>
  64. <!-- header top menu end -->
  65. <div class="page-wrap">
  66. <!--=============== Left Menu side Start ================-->
  67. <div th:replace="~{fragments/modules/base/menu :: menu}"></div>
  68. <!--=============== Left side End ================-->
  69. <div class="main-content">
  70. <div class="container-fluid">
  71. <div class="page-header mb-2">
  72. <div class="row align-items-end">
  73. <div class="col-lg-8">
  74. <div class="page-header-title">
  75. <i class="fa fa-list-alt bg-blue"></i>
  76. <div class="d-inline">
  77. <h5>SocialMedia Complaints</h5>
  78. <span>View Social Media Complaints</span>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="col-lg-4">
  83. <nav class="breadcrumb-container" aria-label="breadcrumb">
  84. <ol class="breadcrumb">
  85. <li class="breadcrumb-item"><a href="/gcc/index"><i
  86. class="ik ik-home"></i></a></li>
  87. <li class="breadcrumb-item" aria-current="page">1913 Social Media</li>
  88. <li class="breadcrumb-item active" aria-current="page">Social Media Complaints</li>
  89. </ol>
  90. </nav>
  91. </div>
  92. </div>
  93. </div>
  94. <div class="row mt-4">
  95. <div class="col-12">
  96. <div class="card">
  97. <div class="card-header">
  98. <span style="font-size: medium;font-weight: bold;">Select Filters</span>
  99. </div>
  100. <div class="card-body">
  101. <div class="row">
  102. <div class="col-sm-2 mb-2">
  103. <label for="startDate" style="font-size: medium;">From Date:</label>
  104. <input type="date" id="startDate" class="form-control">
  105. </div>
  106. <div class="col-sm-2 mb-2">
  107. <label for="endDate" style="font-size: medium;">To Date:</label>
  108. <input type="date" id="endDate" class="form-control">
  109. </div>
  110. <div class="col-sm-2 mb-2">
  111. <label for="stsdropdown" style="font-size: medium;">Status:</label>
  112. <select id="stsdropdown" class="form-control">
  113. <option value="total">Select</option>
  114. <option value="processing">Processing</option>
  115. <option value="reopened">Reopened</option>
  116. <option value="redressed">Action Taken 1</option>
  117. <option value="closed" selected>Action Taken 2</option>
  118. <option value="completed">Completed</option>
  119. </select>
  120. </div>
  121. <div class="col-sm-2 mb-2">
  122. <label for="dropdown4" style="font-size: medium;">Group:</label>
  123. <select id="dropdown4" class="form-control">
  124. <option value="">Select</option>
  125. <!-- <option value="">general</option> -->
  126. <!-- Add options here -->
  127. </select>
  128. </div>
  129. <div class="col-sm-2 mb-2">
  130. <label for="dropdown1" style="font-size: medium;">Type:</label>
  131. <select id="dropdown1" class="form-control">
  132. <option value="">Select</option>
  133. <!-- <option value="Cleanliness in Parks">Cleanliness in Parks</option> -->
  134. </select>
  135. </div>
  136. <div class="col-sm-2 mb-2">
  137. <label for="dropdown2" style="font-size: medium;">Zone:</label>
  138. <select id="dropdown2" class="form-control">
  139. <option value="">Select</option>
  140. <!-- <option value="N01">N01</option> -->
  141. <!-- <option value="ALL">ALL</option> -->
  142. </select>
  143. </div>
  144. </div>
  145. <div class="row mt-4">
  146. <div class="col-12 d-flex justify-content-center">
  147. <button type="submit" id="searchBtn"
  148. class="btn btn-primary mx-2">Search</button>
  149. <button class="btn btn-danger mx-2" id="resetBtn"
  150. name="reset" type="reset">Reset</button>
  151. </div>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. <div class="row mt-4">
  158. <div class="col-md-12">
  159. <div class="card">
  160. <div class="card-header">
  161. <h3 style="font-weight: bold;font-size: large;"><span th:text="${userId}" style="display:none;" id="userIdSpan"></span>
  162. <span th:text="${agent_name}" style="font-weight: bold; font-size: large;"></span>'s Complaints List</h3>
  163. </div>
  164. <div class="card-body">
  165. <div class="table-responsive">
  166. <table id="ReportDatatable"
  167. class="table table-striped table-bordered table-hover dataTable"
  168. role="grid">
  169. <thead class="thead-dark">
  170. <tr role="row">
  171. <th class="text-center no-sort">S.No</th>
  172. <th class="text-center">Reg Date</th>
  173. <th class="text-center no-sort">Complaint No</th>
  174. <th class="text-center">ComplaintType</th>
  175. <th class="text-center">View</th>
  176. <th class="text-center">Officer Name</th>
  177. <th class="text-center">Officer Mobile no</th>
  178. <th class="text-center width1">Status</th>
  179. <th class="text-center width2">Remarks</th>
  180. <th class="text-center">Action</th>
  181. </tr>
  182. </thead>
  183. <tbody>
  184. </tbody>
  185. </table>
  186. </div>
  187. </div>
  188. </div>
  189. </div>
  190. </div>
  191. </div>
  192. </div>
  193. <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;">
  194. <div style="position: relative; top: 50%; transform: translateY(-50%);">
  195. <div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
  196. <span class="sr-only">Loading...</span>
  197. </div>
  198. <!-- Add the 'Please wait' text below the spinner -->
  199. <p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p>
  200. </div>
  201. </div>
  202. <footer th:replace="~{fragments/modules/base/footer :: footer}"></footer>
  203. </div>
  204. </div>
  205. <!-- Modal -->
  206. <div class="modal fade" id="detailsModal" tabindex="-1" role="dialog"
  207. aria-labelledby="detailsModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
  208. <div class="modal-dialog modal-lg" role="document">
  209. <div class="modal-content">
  210. <div class="modal-header">
  211. <h5 class="modal-title" id="detailsModalLabel">Complaint
  212. Details</h5>
  213. <button type="button" class="close" data-dismiss="modal"
  214. aria-label="Close">
  215. <span aria-hidden="true">&times;</span>
  216. </button>
  217. </div>
  218. <div class="modal-body" id="modalBody">
  219. <!-- Details will be populated here -->
  220. </div>
  221. <div class="modal-footer">
  222. <button type="button" class="btn btn-secondary"
  223. data-dismiss="modal">Close</button>
  224. </div>
  225. </div>
  226. </div>
  227. </div>
  228. <!-- in the code use to show the download different type microsoft -->
  229. <!-- Include jQuery and Bootstrap JavaScript -->
  230. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  231. <script
  232. src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
  233. <script th:replace="~{fragments/modules/base/script :: script}"></script>
  234. <!-- sweet alert -->
  235. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  236. <script type="text/javascript">
  237. document.addEventListener("DOMContentLoaded", function () {
  238. const complaintTypeDropdown = document.getElementById('dropdown1');
  239. const complaintGroupDropdown = document.getElementById('dropdown4');
  240. const complaintModeDropdown = document.getElementById('dropdown3');
  241. const complaintZoneDropdown = document.getElementById('dropdown2');
  242. // Load Complaint Groups
  243. fetch('/gcc/api/callcenterqaqc/getComplaintGroup')
  244. .then(response => response.json())
  245. .then(data => {
  246. // Clear existing options
  247. complaintGroupDropdown.innerHTML = `<option value="">Select</option>`;
  248. // Add options from the API response
  249. data.forEach(complaintGroupName => {
  250. complaintGroupDropdown.innerHTML += `<option value="${complaintGroupName}">${complaintGroupName}</option>`;
  251. });
  252. })
  253. .catch(error => {
  254. console.error('Error fetching complaint groups:', error);
  255. });
  256. // Event Listener for Complaint Group Change
  257. complaintGroupDropdown.addEventListener('change', function () {
  258. const selectedGroup = complaintGroupDropdown.value;
  259. if (selectedGroup) {
  260. // Fetch Complaint Types based on selected group
  261. fetch(`/gcc/api/callcenterqaqc/getComplaintTypesByGroup?group=${encodeURIComponent(selectedGroup)}`)
  262. .then(response => response.json())
  263. .then(data => {
  264. // Clear existing options
  265. complaintTypeDropdown.innerHTML = `<option value="">Select</option>`;
  266. // Add options from the API response
  267. data.forEach(complaintTypeName => {
  268. complaintTypeDropdown.innerHTML += `<option value="${complaintTypeName}">${complaintTypeName}</option>`;
  269. });
  270. })
  271. .catch(error => {
  272. console.error('Error fetching complaint types:', error);
  273. });
  274. } else {
  275. // Clear complaint type dropdown if no group is selected
  276. complaintTypeDropdown.innerHTML = `<option value="">Select</option>`;
  277. }
  278. });
  279. // Load all zones
  280. fetch('/gcc/api/callcenterqaqc/getZones')
  281. .then(response => response.json())
  282. .then(data => {
  283. // Clear existing options
  284. complaintZoneDropdown.innerHTML = `<option value="">Select</option>`;
  285. // Add options from the API response
  286. data.forEach(complaintZone => {
  287. complaintZoneDropdown.innerHTML += `<option value="${complaintZone}">${complaintZone}</option>`;
  288. });
  289. })
  290. .catch(error => {
  291. console.error('Error fetching complaint groups:', error);
  292. });
  293. const today = new Date();
  294. const year = today.getFullYear();
  295. const month = String(today.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
  296. const day = String(today.getDate()).padStart(2, '0');
  297. // Calculate the first day of the current month
  298. const startOfMonth = `${year}-${month}-01`; // yyyy-MM-dd format
  299. const todayFormatted = `${year}-${month}-${day}`; // yyyy-MM-dd format
  300. const startDateField = document.getElementById('startDate');
  301. const endDateField = document.getElementById('endDate');
  302. // Event listener for To Date
  303. endDateField.addEventListener('change', function () {
  304. if (endDateField.value) {
  305. startDateField.max = endDateField.value; // Set max value for From Date
  306. } else {
  307. startDateField.removeAttribute('max'); // Remove max if To Date is cleared
  308. }
  309. // Validate From Date against updated To Date
  310. if (startDateField.value && new Date(startDateField.value) > new Date(endDateField.value)) {
  311. startDateField.value = ''; // Clear invalid From Date
  312. alert("From Date cannot be after To Date.");
  313. }
  314. });
  315. // Event listener for From Date
  316. startDateField.addEventListener('change', function () {
  317. if (startDateField.value) {
  318. endDateField.min = startDateField.value; // Set min value for To Date
  319. } else {
  320. endDateField.removeAttribute('min'); // Remove min if From Date is cleared
  321. }
  322. // Validate To Date against updated From Date
  323. if (endDateField.value && new Date(endDateField.value) < new Date(startDateField.value)) {
  324. endDateField.value = ''; // Clear invalid To Date
  325. alert("To Date cannot be before From Date.");
  326. }
  327. });
  328. // Set the default date values in the input fields
  329. if (startDateField && endDateField) {
  330. startDateField.value = startOfMonth; // yyyy-MM-dd format required for input[type="date"]
  331. endDateField.value = todayFormatted; // yyyy-MM-dd format required for input[type="date"]
  332. } else {
  333. console.error("Start date or end date input field not found!");
  334. }
  335. // Call the search logic directly to load default data on page load
  336. //loadDashboardData(formatDateForAPI(startOfMonth), formatDateForAPI(todayFormatted));
  337. });
  338. $(document).ready(function() {
  339. var table = $('#ReportDatatable').DataTable({
  340. dom: 'Bftip',
  341. columnDefs: [
  342. { targets: 'no-sort', orderable: false }
  343. ],
  344. lengthMenu: [
  345. [50, 100, 200, 500, -1],
  346. ['50 rows', '100 rows', '200 rows', 'Show all']
  347. ],
  348. scrollY: '100vh',
  349. fixedColumns: {
  350. left: 1,
  351. right: 1
  352. },
  353. buttons: [
  354. {
  355. extend: 'pageLength',
  356. className: 'btn btn-warning'
  357. },
  358. {
  359. title: 'Data export',
  360. extend: 'copyHtml5',
  361. text: 'Copy',
  362. className: 'btn btn-secondary',
  363. id: 'copyBtn'
  364. },
  365. {
  366. extend: 'excelHtml5',
  367. text: 'Excel',
  368. className: 'btn btn-secondary',
  369. id: 'excelBtn'
  370. },
  371. {
  372. extend: 'csvHtml5',
  373. text: 'CSV',
  374. className: 'btn btn-secondary',
  375. id: 'csvBtn'
  376. },
  377. {
  378. extend: 'pdfHtml5',
  379. text: 'PDF',
  380. className: 'btn btn-secondary',
  381. id: 'pdfBtn'
  382. }
  383. ],
  384. fixedHeader: true,
  385. autoWidth: true,
  386. responsive: true,
  387. searching: true,
  388. select: false,
  389. processing: false
  390. });
  391. // Event listener for From Date selection
  392. $("#startDate").on("change", function() {
  393. const startDate = $(this).val(); // Get the selected start date
  394. // Set the min value for To Date to be the selected From Date
  395. $("#endDate").attr("min", startDate);
  396. });
  397. // Event listener for To Date selection
  398. $("#endDate").on("change", function() {
  399. const endDate = $(this).val(); // Get the selected end date
  400. // Set the max value for From Date to be the selected To Date
  401. $("#startDate").attr("max", endDate);
  402. });
  403. document.getElementById('resetBtn').addEventListener('click', function () {
  404. event.preventDefault();
  405. window.location.href="/gcc/callcenterqaqc/socialmediacomplaints";
  406. });
  407. document.getElementById('searchBtn').addEventListener('click', function (e) {
  408. let startDate = document.getElementById('startDate').value;
  409. let endDate = document.getElementById('endDate').value;
  410. let status = document.getElementById('stsdropdown').value;
  411. let group= document.getElementById('dropdown4').value;
  412. let type= document.getElementById('dropdown1').value;
  413. let zone= document.getElementById('dropdown2').value;
  414. console.log("startdate: ",startDate);
  415. console.log("enddate: ",endDate);
  416. console.log("status: ",status);
  417. console.log("group: ",group);
  418. console.log("type: ",type);
  419. console.log("zone: ",zone);
  420. if (!startDate && !endDate) {
  421. Swal.fire({
  422. icon: 'warning',
  423. title: 'Error',
  424. text: 'Please Select Both From Date and To Date',
  425. showConfirmButton: true,
  426. timer: 3000
  427. });
  428. e.preventDefault(); // Prevent the default action
  429. return; // Stop further execution
  430. }
  431. const params = new URLSearchParams({
  432. startDate: startDate,
  433. endDate: endDate,
  434. status: status,
  435. group: group,
  436. type: type,
  437. zone: zone
  438. });
  439. $('#pageLoader').show();
  440. $.ajax({
  441. type: "GET",
  442. url: `/gcc/api/callcenterqaqc/socialmedia/getsocialmediacomps?${params.toString()}`, // Append query string to URL
  443. contentType: "application/json",
  444. success: function (response) {
  445. console.log("API Response:", response);
  446. $('#pageLoader').hide();
  447. if (response.status === "success") {
  448. const tableHeader = `
  449. <tr>
  450. <th class="text-center no-sort">S.No</th>
  451. <th class="text-center">Reg Date</th>
  452. <th class="text-center no-sort">Complaint No</th>
  453. <th class="text-center">Complaint Type</th>
  454. <th class="text-center">View</th>
  455. <th class="text-center">Officer Name</th>
  456. <th class="text-center">Officer Mobile no</th>
  457. ${
  458. status === "closed"
  459. ? `
  460. <th class="text-center width1">Status</th>
  461. <th class="text-center width2">Remarks</th>
  462. <th class="text-center">Action</th>
  463. `
  464. : ''
  465. }
  466. </tr>
  467. `;
  468. $('#ReportDatatable thead').html(tableHeader); // Dynamically update <thead>
  469. console.log("Details Array:", response.details);
  470. // Ensure details is an array and has at least one entry
  471. if (Array.isArray(response.details) && response.details.length > 0) {
  472. // Dynamically configure columns based on the status
  473. let columns = [
  474. {
  475. data: null,
  476. render: (data, type, row, meta) => meta.row + 1,
  477. className: 'text-center'
  478. },
  479. { data: 'Complaint Opened Date', defaultContent: '-', className: 'text-center' },
  480. { data: 'Complaint Number', defaultContent: '-', className: 'text-center' },
  481. { data: 'Complaint Type', defaultContent: '-', className: 'text-center' },
  482. {
  483. data: 'Complaint Number',
  484. render: function(data) {
  485. return `<button type="button" class="btn btn-info" data-complaint-number="${data}">View</button>`;
  486. },
  487. className: 'text-center'
  488. },
  489. { data: 'Assign To', defaultContent: '-', className: 'text-center' },
  490. { data: 'Official Contact Number', defaultContent: '-', className: 'text-center' },
  491. {
  492. data: '',
  493. defaultContent: '-',
  494. className: 'text-center',
  495. render: function(data, type, row) {
  496. return status === "closed"
  497. ? `<td class="text-center">
  498. <select class="form-control" id="action">
  499. <option value="">Select</option>
  500. <option value="COMPLETED">COMPLETED</option>
  501. <option value="REOPENED">REOPEN</option>
  502. </select>
  503. </td>`
  504. : '-';
  505. }
  506. },
  507. {
  508. data: null,
  509. defaultContent: '-',
  510. render: function(data, type, row) {
  511. return status === "closed"
  512. ? `<input type="text" class="form-control" id="remarks" placeholder="Enter remarks">`
  513. : '-';
  514. },
  515. className: 'text-center'
  516. },
  517. {
  518. data: 'Complaint Number',
  519. render: function(data) {
  520. return status === "closed"
  521. ? `<button type="button" class="btn btn-success" data-complaint-number="${data}">Submit</button>`
  522. : '-';
  523. },
  524. className: 'text-center'
  525. }
  526. ];
  527. // Initialize the DataTable
  528. $('#ReportDatatable').DataTable({
  529. destroy: true,
  530. searching: true,
  531. autoWidth: true,
  532. responsive: true,
  533. paging: true,
  534. info: true,
  535. data: response.details,
  536. columns: columns,
  537. dom: 'Bfrtip',
  538. buttons: [
  539. {
  540. extend: 'pageLength',
  541. className: 'btn btn-warning'
  542. },
  543. {
  544. title: 'Data export',
  545. extend: 'copyHtml5',
  546. text: 'Copy',
  547. className: 'btn btn-secondary',
  548. id: 'copyBtn'
  549. },
  550. {
  551. extend: 'excelHtml5',
  552. text: 'Excel',
  553. className: 'btn btn-secondary',
  554. id: 'excelBtn'
  555. },
  556. {
  557. extend: 'csvHtml5',
  558. text: 'CSV',
  559. className: 'btn btn-secondary',
  560. id: 'csvBtn'
  561. },
  562. {
  563. extend: 'pdfHtml5',
  564. text: 'PDF',
  565. className: 'btn btn-secondary',
  566. id: 'pdfBtn'
  567. }
  568. ],
  569. columnDefs: [
  570. { targets: 'no-sort', orderable: false }
  571. ],
  572. lengthMenu: [
  573. [50, 100, 200, 500, -1],
  574. ['50 rows', '100 rows', '200 rows', 'Show all']
  575. ],
  576. scrollY: '100vh',
  577. fixedColumns: {
  578. left: 1,
  579. right: 1
  580. }
  581. });
  582. }
  583. }
  584. else if (response.status === "nodata") {
  585. $('#pageLoader').hide();
  586. Swal.fire({
  587. icon: 'warning',
  588. title: 'No Data',
  589. text: 'No data available for the selected criteria.'
  590. });
  591. }
  592. else if (response.status === "error") {
  593. $('#pageLoader').hide();
  594. Swal.fire({
  595. icon: 'error',
  596. title: 'Error',
  597. text: response.message
  598. });
  599. }
  600. else {
  601. $('#pageLoader').hide();
  602. Swal.fire({
  603. icon: 'error',
  604. title: 'Error',
  605. text: response.message
  606. });
  607. }
  608. },
  609. error: function (error) {
  610. $('#pageLoader').hide();
  611. Swal.fire({
  612. icon: 'error',
  613. title: 'Error',
  614. text: response.message
  615. });
  616. }
  617. });
  618. });
  619. $(document).on('click', '.btn-info', function () {
  620. // Retrieve the complaint number from the data attribute
  621. var complaintNumber = $(this).data('complaint-number');
  622. console.log("complaintNumber===="+complaintNumber);
  623. // Call the viewDetails function with the complaint number
  624. viewDetails(complaintNumber);
  625. });
  626. $(document).on('click', '.btn.btn-success', function () {
  627. // Retrieve the complaint number from the data attribute
  628. var complaintNumber = $(this).data('complaint-number');
  629. //console.log("complaintNumberP===="+complaintNumber);
  630. // Call the viewDetails function with the complaint number
  631. CloseComplaints(complaintNumber);
  632. });
  633. });
  634. function viewDetails(complaintNumber) {
  635. document.getElementById('modalBody').innerHTML = '<p style="text-align:center;">Please wait, loading data...</p>';
  636. $('#detailsModal').modal('show');
  637. // Fetch the main complaint details
  638. $.ajax({
  639. url: '/gcc/api/callcenterqaqc/agent/qaqccalls/getComplaintDetails',
  640. type: 'GET',
  641. data:{complaintNumber:complaintNumber},
  642. success: function (data) {
  643. //console.log('Complaint Data:', data);
  644. // Fetch agent response details
  645. $.ajax({
  646. url: '/gcc/api/callcenterqaqc/agent/attendedcomplaintshistory',
  647. type: 'GET',
  648. data: { complaintNumber },
  649. success: function (agentResponse) {
  650. //console.log('Agent Response:', agentResponse);
  651. // Use the logic for building complaint and agent details as HTML
  652. const detailsHtml = generateDetailsHtml(data, agentResponse);
  653. // Update modal content
  654. document.getElementById('modalBody').innerHTML = detailsHtml;
  655. },
  656. error: function () {
  657. document.getElementById('modalBody').innerHTML = '<p>Error loading agent details. Please try again later.</p>';
  658. }
  659. });
  660. },
  661. error: function () {
  662. document.getElementById('modalBody').innerHTML = '<p>Error loading complaint details. Please try again later.</p>';
  663. }
  664. });
  665. }
  666. // Function to generate complaint and agent details HTML
  667. function generateDetailsHtml(data, agentResponse) {
  668. // Helper function to format date to yyyy-mm-dd
  669. function formatDate(dateStr) {
  670. if (!dateStr) return 'N/A';
  671. const date = new Date(dateStr);
  672. return date.toISOString().split('T')[0]; // Formats to yyyy-mm-dd
  673. }
  674. // Format complaint messages
  675. let messagesHtml = '';
  676. if (data.comments && data.comments.length > 0) {
  677. data.comments.forEach((comment) => {
  678. const commentImageLink = comment.Comp_Image && comment.Comp_Image !== "No image available"
  679. ? `<a href="${comment.Comp_Image}" target="_blank" style="color: green; text-decoration: underline;">View Image</a>`
  680. : '<p style="color: red;">No image available for this comment.</p>';
  681. messagesHtml += `
  682. <div class="comment mb-4">
  683. <div class="row">
  684. <div class="col-2 text-center">
  685. <p class="text-muted">${comment.comment_date}</p>
  686. </div>
  687. <div class="col-10">
  688. <p><strong>${comment.user || 'N/A'}</strong> Says:</p>
  689. <p>${comment.message || 'No message available'}</p>
  690. <div class="user-image">${commentImageLink}</div>
  691. </div>
  692. </div>
  693. </div>
  694. `;
  695. });
  696. } else {
  697. messagesHtml = '<p>No comments available.</p>';
  698. }
  699. // Format agent responses
  700. let agentRepliedHtml = '';
  701. if (agentResponse.length > 0) {
  702. agentResponse.forEach((agentItem) => {
  703. agentRepliedHtml += `
  704. <div class="comment mb-4">
  705. <div class="row">
  706. <div class="col-2 text-center">
  707. <p class="text-muted"><strong>${formatDate(agentItem.created_date) || 'N/A'}</strong></p>
  708. </div>
  709. <div class="col-10">
  710. <p><strong>${agentItem.agent_name || 'N/A'}</strong>, Says: ${agentItem.call_status || 'N/A'}</p>
  711. <p><strong>Remarks: </strong>${agentItem.remarks || 'N/A'}</p>
  712. <p><strong>Remainder Date: </strong> ${formatDate(agentItem.remainder_date) || 'N/A'}</p>
  713. </div>
  714. </div>
  715. </div>
  716. `;
  717. });
  718. } else {
  719. agentRepliedHtml = '<p>No agent replied messages available.</p>';
  720. }
  721. // Check for user images
  722. const userImages = data.userimages && data.userimages.length > 0 ? data.userimages[0] : {};
  723. const imageLink = userImages.Comp_Image && userImages.Comp_Image !== "null" && userImages.Comp_Image !== ""
  724. ? `<a href="${userImages.Comp_Image}" target="_blank" style="color: green; text-decoration: underline;">UserImages: View Image</a>`
  725. : '<p style="color: red;">UserImages: No image available.</p>';
  726. // Combine all sections into the final HTML
  727. const detailsHtml = `
  728. <div class="row">
  729. <div class="col-md-12">
  730. <h4>Complaint Person's Details:</h4>
  731. <div class="row">
  732. <div class="col-6">
  733. <p><strong>Complaint Number:</strong> ${data.complaintid || 'N/A'}</p>
  734. </div>
  735. <div class="col-6">
  736. <p><strong>Complaint Date:</strong> ${data.complaintdate}</p>
  737. </div>
  738. </div>
  739. <div class="row">
  740. <div class="col-6">
  741. <p><strong>Complainant's Name:</strong> ${data.complainantName || 'N/A'}</p>
  742. </div>
  743. <div class="col-6">
  744. <p><strong>Phone Number:</strong> ${data.mobileNumber || 'N/A'}</p>
  745. </div>
  746. </div>
  747. <div class="row">
  748. <div class="col-6">
  749. <p><strong>Zone:</strong> ${data.Zone || 'N/A'}</p>
  750. </div>
  751. <div class="col-6">
  752. <p><strong>Division:</strong> ${data.Division || 'N/A'}</p>
  753. </div>
  754. </div>
  755. <div class="row">
  756. <div class="col-6">
  757. <p><strong>Gender:</strong> ${data.gender || 'N/A'}</p>
  758. </div>
  759. <div class="col-6">
  760. <p><strong>Complainant's Address:</strong> ${data.Street || 'N/A'}, ${data.Area || 'N/A'}, ${data.Landmark || 'N/A'}, ${data.Location || 'N/A'}</p>
  761. </div>
  762. </div>
  763. </div>
  764. </div>
  765. <hr>
  766. <h4>Complaint Details:</h4>
  767. <div class="row">
  768. <div class="col-6">
  769. <p><strong>Complaint Dept:</strong> ${data.deptname || 'N/A'}</p>
  770. </div>
  771. <div class="col-6">
  772. <p><strong>Complaint Mode:</strong> ${data.compmode || 'N/A'}</p>
  773. </div>
  774. </div>
  775. <div class="row">
  776. <div class="col-6">
  777. <p><strong>Complaint Type:</strong> ${data.complaintType || 'N/A'}</p>
  778. </div>
  779. <div class="col-6">
  780. <p><strong>Details of Complaint:</strong> ${data.description || 'N/A'}</p>
  781. </div>
  782. </div>
  783. <div class="row">
  784. <div class="col-6">
  785. <p><strong>Current Status:</strong> ${data.currentStatus || 'N/A'}</p>
  786. </div>
  787. <div class="col-6">
  788. <p><strong style="color:blue;">Official Mobile:</strong> ${data.OfficialMobile || 'N/A'}</p>
  789. </div>
  790. </div>
  791. <div class="row">
  792. <div class="col-6">
  793. <p><strong>Last Updated Date:</strong> ${data.greatestDate || 'N/A'}</p>
  794. </div>
  795. </div>
  796. <hr>
  797. <h4>Complaint Messages:</h4>
  798. ${messagesHtml}
  799. <hr>
  800. <h4>Agent Replied Messages:</h4>
  801. ${agentRepliedHtml}
  802. <hr>
  803. ${imageLink}
  804. `;
  805. return detailsHtml;
  806. }
  807. /* function CloseComplaints(complaintNumber) {
  808. var userId = document.getElementById('userIdSpan').textContent;
  809. console.log("userId="+userId);
  810. $('.btn.btn-success').prop('disabled', true);
  811. $.ajax({
  812. url: '/gcc/api/callcenterqaqc/socialmedia/update',
  813. type: 'POST',
  814. data: {
  815. complaintNumber: complaintNumber,
  816. userId:userId
  817. },
  818. success: function (response) {
  819. if (response === "Success") {
  820. //console.log("Success");
  821. Swal.fire({
  822. icon: 'success',
  823. title: 'Success',
  824. text: 'Complaint updated successfully!',
  825. allowOutsideClick: false, // Disable clicking outside the modal to close it
  826. allowEscapeKey: false, // Disable escape key to close the modal
  827. allowEnterKey: false, // Disable enter key to close the modal
  828. showConfirmButton: true, // Ensure the confirm button is visible
  829. }).then((result) => {
  830. if (result.isConfirmed) {
  831. console.log("ok button");
  832. $('.btn.btn-success').prop('disabled', false);
  833. // Get the DataTable instance
  834. const table = $('#ReportDatatable').DataTable();
  835. // Find the row with the clicked button
  836. let row = $(`.btn[data-complaint-number='${complaintNumber}']`).closest('tr');
  837. // Remove the row
  838. table.row(row).remove();
  839. // Reindex the "S.No" column
  840. table.rows().every(function (index) {
  841. const row = this.node();
  842. $(row).find('td:first-child').text(index + 1); // Update "S.No"
  843. });
  844. // Redraw the table
  845. table.draw(false);
  846. }
  847. });
  848. }
  849. else if (response === "error") {
  850. $('.btn.btn-success').prop('disabled', false);
  851. Swal.fire("Error", "Please try again later.", "error");
  852. }
  853. else {
  854. $('.btn.btn-success').prop('disabled', false);
  855. Swal.fire("Error", "Failed to update complaint. Please try again.", "error");
  856. }
  857. },
  858. error: function (xhr, status, error) {
  859. $('.btn.btn-success').prop('disabled', false);
  860. Swal.fire("Error", `An error occurred: ${xhr.responseText || error}`, "error");
  861. }
  862. });
  863. } */
  864. function CloseComplaints(complaintNumber) {
  865. const userId = document.getElementById('userIdSpan').textContent;
  866. console.log("userId=" + userId);
  867. // Find the closest table row and get the specific action and remarks
  868. const row = $(`button[data-complaint-number="${complaintNumber}"]`).closest('tr');
  869. const actionValue = row.find('select').val(); // Select dropdown within the same row
  870. const remarksValue = row.find('input').val(); // Input field within the same row
  871. console.log("complaintNumber:",complaintNumber)
  872. console.log("Action:", actionValue);
  873. console.log("Remarks:", remarksValue);
  874. // Check if action is empty
  875. if (actionValue === "") {
  876. Swal.fire({
  877. icon: 'warning',
  878. title: 'Action Required',
  879. text: 'Please select an action.'
  880. });
  881. return; // Stop execution if no action is selected
  882. }
  883. if (actionValue === "REOPENED" && remarksValue.trim() === "") {
  884. Swal.fire({
  885. icon: 'warning',
  886. title: 'Remarks Required',
  887. text: 'Please enter remarks.'
  888. });
  889. return; // Stop execution if remarks are empty
  890. }
  891. Swal.fire({
  892. title: 'Are you sure to update?',
  893. text: 'This action will update the complaint.',
  894. icon: 'warning',
  895. allowOutsideClick: false,
  896. allowEscapeKey: false,
  897. allowEnterKey: false,
  898. confirmButtonColor: 'blue',
  899. cancelButtonColor: 'red',
  900. showCancelButton: true, // Enable cancel button
  901. confirmButtonText: 'Yes', // Text for the confirmation button
  902. cancelButtonText: 'No', // Text for the cancel button
  903. reverseButtons: false // Optional: makes the "No" button appear first
  904. }).then((result) => {
  905. if (result.isConfirmed) {
  906. // User clicked "Yes"
  907. $('.btn.btn-success').prop('disabled', true);
  908. $.ajax({
  909. url: '/gcc/api/callcenterqaqc/socialmedia/update',
  910. type: 'POST',
  911. data: {
  912. complaintNumber: complaintNumber,
  913. userId: userId,
  914. action: actionValue, // Add selected action to the request data
  915. remarks: remarksValue // Add remarks to the request data
  916. },
  917. success: function (response) {
  918. if (response === "Success") {
  919. Swal.fire({
  920. icon: 'success',
  921. title: 'Success',
  922. text: 'Complaint updated successfully!',
  923. allowOutsideClick: false,
  924. allowEscapeKey: false,
  925. allowEnterKey: false,
  926. showConfirmButton: true,
  927. }).then(() => {
  928. $('.btn.btn-success').prop('disabled', false);
  929. // Get the DataTable instance
  930. const table = $('#ReportDatatable').DataTable();
  931. // Find the row with the clicked button
  932. let row = $(`.btn[data-complaint-number='${complaintNumber}']`).closest('tr');
  933. // Remove the row
  934. table.row(row).remove();
  935. // Reindex the "S.No" column
  936. table.rows().every(function (index) {
  937. const row = this.node();
  938. $(row).find('td:first-child').text(index + 1);
  939. });
  940. // Redraw the table
  941. table.draw(false);
  942. });
  943. } else if (response === "error") {
  944. $('.btn.btn-success').prop('disabled', false);
  945. Swal.fire("Error", "Please try again later.", "error");
  946. }else if (response === "erpname") {
  947. $('.btn.btn-success').prop('disabled', false);
  948. Swal.fire("Error", "Issue in ERP_Username", "error");
  949. }
  950. else if (response === "save") {
  951. $('.btn.btn-success').prop('disabled', false);
  952. Swal.fire("Error", "In Erp updated, but issue in saving", "error");
  953. }
  954. else {
  955. $('.btn.btn-success').prop('disabled', false);
  956. Swal.fire("Error", "Failed to update complaint. Please try again.", "error");
  957. }
  958. },
  959. error: function (xhr, status, error) {
  960. $('.btn.btn-success').prop('disabled', false);
  961. Swal.fire("Error", `An error occurred: ${xhr.responseText || error}`, "error");
  962. }
  963. });
  964. } else if (result.isDismissed) {
  965. // User clicked "No" or dismissed the dialog
  966. console.log("Update action cancelled by the user.");
  967. }
  968. });
  969. }
  970. </script>
  971. <!--
  972. {
  973. data: '',
  974. defaultContent: '-',
  975. className: 'text-center',
  976. render: function(data, type, row) {
  977. return `
  978. <td class="text-center">
  979. <select class="form-control" id="action">
  980. <option value="">Select</option>
  981. <option value="COMPLETED">COMPLETED</option>
  982. <option value="REOPENED">REOPEN</option>
  983. </select>
  984. </td>`;
  985. }
  986. },
  987. -->
  988. </body>
  989. </html>