Browse Source

changes in food distribution api controller and service and pages newly added detailed report

master
yoganandhan 6 months ago
parent
commit
6c0cbc662e
  1. 34
      src/main/java/com/gcc/commissionerreview/controller/FoodDistrubutionReportApiController.java
  2. 5
      src/main/java/com/gcc/commissionerreview/controller/MainController.java
  3. 47
      src/main/java/com/gcc/commissionerreview/service/Fooddistributionservice.java
  4. 566
      src/main/resources/templates/modules/fooddistribution/fooddistributiondetailedreport.html
  5. 59
      src/main/resources/templates/modules/fooddistribution/fooddistributionreport.html

34
src/main/java/com/gcc/commissionerreview/controller/FoodDistrubutionReportApiController.java

@ -34,6 +34,34 @@ public class FoodDistrubutionReportApiController {
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
@GetMapping("/hubs")
public ResponseEntity<Map<String, Object>> getHubs() {
Map<String, Object> response = new HashMap<>();
try {
List<Map<String, Object>> hubs = fooddistributionservice.getHubs();
response.put("status", "success");
response.put("data", hubs);
} catch (Exception e) {
response.put("status", "error");
response.put("message", e.getMessage());
}
return ResponseEntity.ok(response);
}
@GetMapping("/shifts")
public ResponseEntity<Map<String, Object>> getShiftsWise() {
Map<String, Object> response = new HashMap<>();
try {
List<Map<String, Object>> shifts = fooddistributionservice.getShifts();
response.put("status", "success");
response.put("data", shifts);
} catch (Exception e) {
response.put("status", "error");
response.put("message", e.getMessage());
}
return ResponseEntity.ok(response);
}
@GetMapping("/complianceAbstract") @GetMapping("/complianceAbstract")
public ResponseEntity<Map<String, Object>> getComplianceAbstractReport( public ResponseEntity<Map<String, Object>> getComplianceAbstractReport(
@RequestParam(value = "fromDate", required = false) String fromDate, @RequestParam(value = "fromDate", required = false) String fromDate,
@ -55,10 +83,10 @@ public class FoodDistrubutionReportApiController {
public ResponseEntity<Map<String, Object>> getComplianceDetailedReport( public ResponseEntity<Map<String, Object>> getComplianceDetailedReport(
@RequestParam(value = "fromDate", required = false) String fromDate, @RequestParam(value = "fromDate", required = false) String fromDate,
@RequestParam(value = "toDate", required = false) String toDate, @RequestParam(value = "toDate", required = false) String toDate,
@RequestParam("hubId") Integer hubId,
@RequestParam(value = "hubId", required = false) Integer hubId,
@RequestParam(value = "shiftId", required = false) Integer shiftId, @RequestParam(value = "shiftId", required = false) Integer shiftId,
@RequestParam("qid") Integer qid,
@RequestParam("statusFilter") String statusFilter) {
@RequestParam(value = "qid", required = false) Integer qid,
@RequestParam(value = "statusFilter", required = false) String statusFilter) {
Map<String, Object> response = new HashMap<>(); Map<String, Object> response = new HashMap<>();
try { try {
List<Map<String, Object>> detailedData = fooddistributionservice.getComplianceDetailedReport(fromDate, List<Map<String, Object>> detailedData = fooddistributionservice.getComplianceDetailedReport(fromDate,

5
src/main/java/com/gcc/commissionerreview/controller/MainController.java

@ -306,4 +306,9 @@ public class MainController {
return "modules/fooddistribution/fooddistributionreport"; return "modules/fooddistribution/fooddistributionreport";
} }
@GetMapping("/fooddistributiondetailedreport")
public String fooddistributiondetailedreport(Model model) {
return "modules/fooddistribution/fooddistributiondetailedreport";
}
} }

47
src/main/java/com/gcc/commissionerreview/service/Fooddistributionservice.java

@ -34,6 +34,20 @@ public class Fooddistributionservice {
return jdbcTemplate.queryForList(sql); return jdbcTemplate.queryForList(sql);
} }
public List<Map<String, Object>> getHubs() {
String sql = "SELECT id as hub_id, " +
"CONCAT(IFNULL(NULLIF(si_name, ''), CONCAT('Kitchen ', id)), " +
"IF(permanent_location IS NOT NULL AND permanent_location != '', CONCAT(' - ', permanent_location), '')) as hub_name "
+
"FROM hub_master WHERE is_active = 1 AND is_delete = 0 ORDER BY id";
return jdbcTemplate.queryForList(sql);
}
public List<Map<String, Object>> getShifts() {
String sql = "SELECT shiftid, name as shift_name FROM shift_master WHERE isactive = 1 AND isdelete = 0 ORDER BY orderby";
return jdbcTemplate.queryForList(sql);
}
public List<Map<String, Object>> getComplianceAbstractReport(String fromDate, String toDate) { public List<Map<String, Object>> getComplianceAbstractReport(String fromDate, String toDate) {
String dateFilter = ""; String dateFilter = "";
java.util.List<Object> params = new java.util.ArrayList<>(); java.util.List<Object> params = new java.util.ArrayList<>();
@ -106,6 +120,21 @@ public class Fooddistributionservice {
shiftFilter = " AND pa.shiftid = ? "; shiftFilter = " AND pa.shiftid = ? ";
} }
String hubFilter = "";
if (hubId != null && hubId > 0) {
hubFilter = " AND hm.id = ? ";
}
String qidFilter = "";
if (qid != null && qid > 0) {
qidFilter = " AND pqm.qid = ? ";
}
String statusFilter = "";
if (status != null && !status.trim().isEmpty()) {
statusFilter = " AND pam.english_name = ? ";
}
String sql = "SELECT " + String sql = "SELECT " +
"pf.id, " + "pf.id, " +
"DATE_FORMAT(pa.cdate, '%d/%m/%y %h:%i %p') AS auditDate, " + "DATE_FORMAT(pa.cdate, '%d/%m/%y %h:%i %p') AS auditDate, " +
@ -133,11 +162,11 @@ public class Fooddistributionservice {
"FROM pmc_feedback pf " + "FROM pmc_feedback pf " +
"JOIN pmc_audit pa ON pa.id = pf.pmc_audit_id AND pa.isactive = 1 AND pa.isdelete = 0 " + dateFilter "JOIN pmc_audit pa ON pa.id = pf.pmc_audit_id AND pa.isactive = 1 AND pa.isdelete = 0 " + dateFilter
+ shiftFilter + + shiftFilter +
"JOIN hub_master hm ON hm.id = pa.hub_id AND hm.id = ? " +
"JOIN hub_master hm ON hm.id = pa.hub_id " + hubFilter +
"LEFT JOIN shift_master sm ON sm.shiftid = pa.shiftid AND sm.isactive = 1 AND sm.isdelete = 0 " + "LEFT JOIN shift_master sm ON sm.shiftid = pa.shiftid AND sm.isactive = 1 AND sm.isdelete = 0 " +
"JOIN pmc_questions_master pqm ON pqm.qid = pf.questions AND pqm.qid = ? " +
"JOIN pmc_questions_master pqm ON pqm.qid = pf.questions " + qidFilter +
"JOIN questions_category_master qcm ON qcm.qcm_id = pf.qcmid " + "JOIN questions_category_master qcm ON qcm.qcm_id = pf.qcmid " +
"JOIN pmc_answer_master pam ON pam.aid = pf.answer AND pam.english_name = ? " +
"JOIN pmc_answer_master pam ON pam.aid = pf.answer " + statusFilter +
"LEFT JOIN driver_login dl ON pa.cby = dl.loginid " + "LEFT JOIN driver_login dl ON pa.cby = dl.loginid " +
"LEFT JOIN driver_login dl2 ON pf.fs_cby = dl2.loginid " + "LEFT JOIN driver_login dl2 ON pf.fs_cby = dl2.loginid " +
"WHERE pf.isactive = 1 AND pf.isdelete = 0 " + "WHERE pf.isactive = 1 AND pf.isdelete = 0 " +
@ -146,9 +175,15 @@ public class Fooddistributionservice {
if (shiftId != null && shiftId > 0) { if (shiftId != null && shiftId > 0) {
params.add(shiftId); params.add(shiftId);
} }
params.add(hubId);
params.add(qid);
params.add(status);
if (hubId != null && hubId > 0) {
params.add(hubId);
}
if (qid != null && qid > 0) {
params.add(qid);
}
if (status != null && !status.trim().isEmpty()) {
params.add(status);
}
return jdbcTemplate.queryForList(sql, params.toArray()); return jdbcTemplate.queryForList(sql, params.toArray());
} }

566
src/main/resources/templates/modules/fooddistribution/fooddistributiondetailedreport.html

@ -0,0 +1,566 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Food Distribution Detailed Report</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- FontAwesome CSS for icons -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<!-- DataTables CSS -->
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/buttons/2.3.6/css/buttons.dataTables.min.css" rel="stylesheet" />
<style>
body {
background-color: #f8f9fa;
}
.page-header-title h4 {
margin: 20px 0;
text-align: center;
font-weight: bold;
color: #003366;
text-transform: uppercase;
}
.filter-section {
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
margin-bottom: 20px;
}
table.dataTable th,
table.dataTable td {
text-align: center;
vertical-align: middle;
border: 1px solid #dee2e6 !important;
white-space: normal !important;
word-break: break-word;
}
thead th {
background-color: #003366 !important;
color: white !important;
}
.report-container {
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}
.dataTables_scrollBody {
overflow-x: auto !important;
overflow-y: auto !important;
white-space: nowrap;
touch-action: pan-x pan-y;
cursor: grab;
}
.dataTables_scrollBody:active {
cursor: grabbing;
}
.dataTables_wrapper {
overflow-x: auto !important;
}
.dataTables_scrollHeadInner table,
.report-container table.dataTable {
min-width: 2400px !important;
width: 100% !important;
}
.dataTables_scrollBody::-webkit-scrollbar {
height: 8px;
width: 8px;
}
.dataTables_scrollBody::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.dataTables_scrollBody::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
.dataTables_scrollBody::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.swal2-container.swal2-custom-zindex {
z-index: 1070 !important;
}
/* Ensure columns don't squish */
#detailDataTable th {
max-width: 150px;
}
</style>
</head>
<body>
<div class="container-fluid py-4">
<div class="page-header-title d-flex justify-content-between align-items-center">
<a href="/commissionerreview/fooddistributionreport" class="btn btn-secondary"><i
class="fa-solid fa-arrow-left"></i> Back to Abstract</a>
<h4 class="mb-0 mx-auto">Food Distribution Detailed Report</h4>
<div style="width: 100px;"></div>
</div>
<!-- Filter Section -->
<div class="filter-section d-flex justify-content-center align-items-end gap-3 flex-wrap mt-3">
<div>
<label for="hubId" class="form-label fw-bold">Kitchen/Hub:</label>
<select id="hubId" class="form-select">
<option value="">All Kitchens</option>
</select>
</div>
<div>
<label for="shiftId" class="form-label fw-bold">Shift:</label>
<select id="shiftId" class="form-select">
<option value="">All Shifts</option>
</select>
</div>
<div>
<label for="fromDate" class="form-label fw-bold">From Date:</label>
<input type="date" id="fromDate" class="form-control">
</div>
<div>
<label for="toDate" class="form-label fw-bold">To Date:</label>
<input type="date" id="toDate" class="form-control">
</div>
<div>
<button id="btnSearch" class="btn btn-primary px-4"><i class="fa-solid fa-search"></i> Search</button>
</div>
</div>
<div class="report-container">
<table id="detailDataTable" class="table table-striped table-bordered display nowrap" style="width:100%;">
<thead>
<tr>
<th class="text-center">S.No</th>
<th class="text-center">Audit Date</th>
<th class="text-center">Shift</th>
<th class="text-center">Zone</th>
<th class="text-center">Ward</th>
<th class="text-center">Address</th>
<th class="text-center">Audit Category</th>
<th class="text-center">Question</th>
<th class="text-center">Answer</th>
<th class="text-center">Kitchen SI Name</th>
<th class="text-center">Raised By</th>
<th class="text-center">PMC Remarks</th>
<th class="text-center">PMC Image/Video Viewer</th>
<th class="text-center">FS Status</th>
<th class="text-center">FS Challenged By</th>
<th class="text-center">FS Remarks</th>
<th class="text-center">FS Image/Video Viewer</th>
<th class="text-center">CE Status</th>
<th class="text-center">CE Remarks</th>
<th class="text-center">CE Updated By</th>
<th class="text-center">Penalty Amount</th>
</tr>
</thead>
<tbody>
<!-- Populated via JS -->
</tbody>
</table>
</div>
<!-- Media Modal -->
<div class="modal fade" id="mediaModal" tabindex="-1" aria-labelledby="mediaModalLabel" aria-hidden="true"
style="z-index: 1060;">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mediaModalLabel">Media Viewer</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center" id="mediaModalBody">
</div>
</div>
</div>
</div>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<!-- DataTables JS -->
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.3.6/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.3.6/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.3.6/js/buttons.print.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
<!-- SweetAlert -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script th:inline="none">
var dataTable = null;
$(document).ready(function () {
// Set default dates to yesterday
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var yyyy = yesterday.getFullYear();
var mm = String(yesterday.getMonth() + 1).padStart(2, '0');
var dd = String(yesterday.getDate()).padStart(2, '0');
var defaultDate = yyyy + '-' + mm + '-' + dd;
$('#fromDate').val(defaultDate);
$('#toDate').val(defaultDate);
$('#toDate').attr('min', defaultDate);
$('#fromDate').on('change', function () {
var selectedDate = $(this).val();
$('#toDate').attr('min', selectedDate);
if ($('#toDate').val() < selectedDate) {
$('#toDate').val(selectedDate);
}
});
// Load Hubs
$.ajax({
url: '/commissionerreview/api/fooddistribution/hubs',
method: 'GET',
success: function (res) {
if (res.status === 'success') {
res.data.forEach(function (hub) {
$('#hubId').append('<option value="' + hub.hub_id + '">' + hub.hub_name + '</option>');
});
}
}
});
// Load Shifts
$.ajax({
url: '/commissionerreview/api/fooddistribution/shifts',
method: 'GET',
success: function (res) {
if (res.status === 'success') {
res.data.forEach(function (shift) {
$('#shiftId').append('<option value="' + shift.shiftid + '">' + shift.shift_name + '</option>');
});
}
}
});
// Init DataTable
dataTable = $('#detailDataTable').DataTable({
autoWidth: false,
columnDefs: [
{ width: "60px", targets: 0 },
{ width: "120px", targets: 1 },
{ width: "60px", targets: 2 },
{ width: "60px", targets: 3 },
{ width: "60px", targets: 4 },
{ width: "200px", targets: 5 },
{ width: "180px", targets: 6 },
{ width: "180px", targets: 7 },
{ width: "120px", targets: 8 },
{ width: "180px", targets: 9 },
{ width: "150px", targets: 10 },
{ width: "220px", targets: 11 },
{ width: "150px", targets: 12 },
{ width: "120px", targets: 13 },
{ width: "150px", targets: 14 },
{ width: "220px", targets: 15 },
{ width: "150px", targets: 16 },
{ width: "120px", targets: 17 },
{ width: "220px", targets: 18 },
{ width: "150px", targets: 19 },
{ width: "150px", targets: 20 }
],
responsive: false,
scrollX: true,
scrollY: "60vh",
scrollCollapse: true,
searching: true,
paging: true,
info: true,
ordering: false,
dom: '<"d-flex justify-content-between align-items-center mb-3"lfB>rtip',
lengthMenu: [[10, 50, 100, -1], [10, 50, 100, "All"]],
buttons: [
{
extend: 'excelHtml5',
text: '<i class="fa-solid fa-file-excel"></i> Excel',
className: 'btn btn-success btn-sm me-2',
title: function () {
return 'Food Distribution Detailed Report - ' + $('#hubId option:selected').text();
},
filename: function () {
var kitchen = $('#hubId option:selected').text().replace(/[^a-zA-Z0-9]/g, '_');
return 'Detailed_Food_Distribution_' + kitchen + '_' + ($('#fromDate').val() || 'All') + '_to_' + ($('#toDate').val() || 'All');
},
exportOptions: {
format: {
body: function (data, row, column, node) {
if (typeof data === 'string' && data.indexOf('showMedia') !== -1) {
var match = data.match(/showMedia\('([^']+)'/);
if (match) return match[1];
}
if (typeof data === 'string' && data.indexOf('data-fulltext') !== -1) {
var match = data.match(/data-fulltext="([^"]+)"/);
if (match) return decodeURIComponent(match[1]);
}
var div = document.createElement("div");
div.innerHTML = data;
return div.textContent || div.innerText || "";
}
}
}
},
{
extend: 'pdfHtml5',
text: '<i class="fa-solid fa-file-pdf"></i> PDF',
className: 'btn btn-danger btn-sm',
orientation: 'landscape',
pageSize: 'A0', // Set to A0 so the rows don't get cut due to extreme width
title: function () {
return 'Food Distribution Detailed Report - ' + $('#hubId option:selected').text();
},
filename: function () {
var kitchen = $('#hubId option:selected').text().replace(/[^a-zA-Z0-9]/g, '_');
return 'Detailed_Food_Distribution_' + kitchen + '_' + ($('#fromDate').val() || 'All') + '_to_' + ($('#toDate').val() || 'All');
},
exportOptions: {
format: {
body: function (data, row, column, node) {
if (typeof data === 'string' && data.indexOf('showMedia') !== -1) {
var match = data.match(/showMedia\('([^']+)'/);
if (match) return match[1];
}
if (typeof data === 'string' && data.indexOf('data-fulltext') !== -1) {
var match = data.match(/data-fulltext="([^"]+)"/);
if (match) return decodeURIComponent(match[1]);
}
var div = document.createElement("div");
div.innerHTML = data;
return div.textContent || div.innerText || "";
}
}
},
customize: function (doc) {
doc.styles.tableHeader.fontSize = 12;
doc.defaultStyle.fontSize = 10;
// Prevent breaking cells across pages
doc.content[1].table.dontBreakRows = true;
}
}
]
});
$('#btnSearch').click(function () {
loadDetailedReport();
});
// Load data initially
loadDetailedReport();
});
function loadDetailedReport() {
var fromDate = $('#fromDate').val() || '';
var toDate = $('#toDate').val() || '';
var hubId = $('#hubId').val() || '';
var shiftId = $('#shiftId').val() || '';
Swal.fire({ title: 'Loading...', allowOutsideClick: false, didOpen: function () { Swal.showLoading(); } });
$.ajax({
url: '/commissionerreview/api/fooddistribution/complianceDetails',
method: 'GET',
data: {
fromDate: fromDate,
toDate: toDate,
hubId: hubId,
shiftId: shiftId
},
success: function (res) {
Swal.close();
if (res.status === 'success') {
populateTable(res.data);
} else {
Swal.fire('Error', 'Failed to load detailed data.', 'error');
}
},
error: function () {
Swal.close();
Swal.fire('Error', 'API error loading detailed data.', 'error');
}
});
}
function populateTable(data) {
dataTable.clear();
data.forEach(function (item, index) {
var ext = item.videoLink ? item.videoLink.split('.').pop().toLowerCase() : '';
var isVideo = ['mp4', 'mov', 'avi', 'webm', 'mkv'].includes(ext);
var imgBtn = '-';
if (item.videoLink && item.videoLink.indexOf('null') === -1) {
imgBtn = '<button onclick="showMedia(\'' + item.videoLink + '\', ' + isVideo + ')" class="btn btn-sm btn-info text-white"><i class="fa fa-' + (isVideo ? 'video' : 'image') + '"></i> View</button>';
}
var fsExt = item.fsVideoLink ? item.fsVideoLink.split('.').pop().toLowerCase() : '';
var isFsVideo = ['mp4', 'mov', 'avi', 'webm', 'mkv'].includes(fsExt);
var fsImgBtn = '-';
if (item.fsVideoLink && item.fsVideoLink.indexOf('null') === -1) {
fsImgBtn = '<button onclick="showMedia(\'' + item.fsVideoLink + '\', ' + isFsVideo + ')" class="btn btn-sm btn-info text-white"><i class="fa fa-' + (isFsVideo ? 'video' : 'image') + '"></i> View</button>';
}
var fsStsStr = (item.fsStatus || '').toLowerCase();
var ceStsStr = (item.chief_engineer_status || '').toLowerCase();
var pAmt = parseInt(item.penalty_amt) || 0;
var finalPenalty = pAmt;
if (item.english_name === 'Issue Found') {
if (fsStsStr === 'challenge' && ceStsStr === 'reject') {
finalPenalty = pAmt;
} else if (fsStsStr === 'accept') {
finalPenalty = pAmt;
} else if (fsStsStr === 'challenge' && ceStsStr === 'accept') {
finalPenalty = '-';
} else if (fsStsStr === 'challenge' && (ceStsStr === '' || ceStsStr === '-')) {
finalPenalty = 'Pending';
}
} else {
finalPenalty = '-';
}
dataTable.row.add([
index + 1,
item.auditDate || '-',
item.shift || '-',
item.zone || '-',
item.ward || '-',
item.address || '-',
item.audit_category || '-',
item.q_english || '-',
item.english_name || '-',
item.name || '-',
item.raisedBy || '-',
formatRemark(item.pmcRemarks, 'PMC Remarks'),
imgBtn,
item.fsStatus || '-',
item.fsChallengedBy || '-',
formatRemark(item.fsRemarks, 'FS Remarks'),
fsImgBtn,
item.chief_engineer_status || '-',
formatRemark(item.chief_engineer_remarks, 'CE Remarks'),
item.chief_engineer || '-',
finalPenalty
]);
});
dataTable.draw();
setTimeout(function () {
dataTable.columns.adjust().draw();
}, 300);
}
function formatRemark(text, title) {
if (!text || text === '-' || String(text).trim() === '' || String(text) === 'null') {
return '-';
}
var str = String(text);
if (str.length > 100) {
var truncated = $('<div>').text(str.substring(0, 100) + '...').html();
var encodedText = encodeURIComponent(str);
return '<span class="text-primary fw-bold" style="cursor:pointer; text-decoration:underline;" onclick="showFullRemark(\'' + title + '\', \'' + encodedText + '\')" title="Click to view full remark" data-fulltext="' + encodedText + '">' + truncated + '</span>';
}
return $('<div>').text(str).html();
}
function showFullRemark(title, encodedText) {
var text = decodeURIComponent(encodedText);
Swal.fire({
title: title,
html: '<div style="text-align: left; white-space: pre-wrap; font-size: 18px; max-height: 400px; overflow-y: auto; padding: 10px; background: #f8f9fa; border-radius: 5px; border: 1px solid #dee2e6;">' + $('<div>').text(text).html() + '</div>',
width: '600px',
confirmButtonText: 'Close',
customClass: {
popup: 'swal2-custom-zindex'
}
});
}
function showMedia(url, isVideo) {
var mediaHtml = '';
if (isVideo) {
mediaHtml = '<video width="100%" controls controlsList="nodownload" disablePictureInPicture><source src="' + url + '">Your browser does not support the video tag.</video>';
} else {
mediaHtml = '<img src="' + url + '" style="max-width: 100%; height: auto; pointer-events: none; user-select: none;" oncontextmenu="return false;" />';
}
$('#mediaModalBody').html(mediaHtml);
$('#mediaModal').modal('show');
$('#mediaModal').off('hidden.bs.modal').on('hidden.bs.modal', function () {
$('#mediaModalBody').empty();
});
}
// Drag scroll functionality
let isDown = false;
let startX;
let scrollLeft;
$(document).on('mousedown', '.dataTables_scrollBody', function (e) {
isDown = true;
$(this).css('cursor', 'grabbing');
startX = e.pageX - this.offsetLeft;
scrollLeft = this.scrollLeft;
});
$(document).on('mouseleave', '.dataTables_scrollBody', function () {
isDown = false;
$(this).css('cursor', '');
});
$(document).on('mouseup', '.dataTables_scrollBody', function () {
isDown = false;
$(this).css('cursor', '');
});
$(document).on('mousemove', '.dataTables_scrollBody', function (e) {
if (!isDown) return;
e.preventDefault();
const x = e.pageX - this.offsetLeft;
const walk = (x - startX) * 1.5;
this.scrollLeft = scrollLeft - walk;
});
// Touchpad scroll mapping support
$(document).on('wheel', '.dataTables_scrollBody', function (e) {
const el = this;
const deltaX = e.originalEvent.deltaX;
const deltaY = e.originalEvent.deltaY;
const canScrollY = el.scrollHeight > Math.ceil(el.clientHeight) + 5;
const canScrollX = el.scrollWidth > Math.ceil(el.clientWidth) + 5;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
el.scrollLeft += deltaX;
e.preventDefault();
}
else if (!canScrollY && canScrollX && Math.abs(deltaY) > 0) {
el.scrollLeft += deltaY;
e.preventDefault();
}
});
</script>
</body>
</html>

59
src/main/resources/templates/modules/fooddistribution/fooddistributionreport.html

@ -155,6 +155,10 @@
filter: invert(1); filter: invert(1);
} }
.swal2-container.swal2-custom-zindex {
z-index: 1070 !important;
}
#modalDataTable th { #modalDataTable th {
white-space: normal !important; white-space: normal !important;
/* allow wrapping */ /* allow wrapping */
@ -211,6 +215,10 @@
<div> <div>
<button id="btnSearch" class="btn btn-primary px-4"><i class="fa-solid fa-search"></i> Search</button> <button id="btnSearch" class="btn btn-primary px-4"><i class="fa-solid fa-search"></i> Search</button>
</div> </div>
<div>
<a href="/commissionerreview/fooddistributiondetailedreport" class="btn btn-info px-4 text-white"><i
class="fa-solid fa-file-alt"></i> Detailed Report</a>
</div>
</div> </div>
<div class="report-container"> <div class="report-container">
@ -220,7 +228,7 @@
<tr id="header-row-1"> <tr id="header-row-1">
<th rowspan="3" style="vertical-align: middle;">Zone</th> <th rowspan="3" style="vertical-align: middle;">Zone</th>
<th rowspan="3" <th rowspan="3"
style="vertical-align: middle; white-space: normal; min-width: 250px; max-width: 350px;">
style="vertical-align: middle; white-space: normal; word-break: break-word; min-width: 150px; max-width: 200px;">
Kitchen Name / Address</th> Kitchen Name / Address</th>
<th rowspan="3" style="vertical-align: middle;">Shift</th> <th rowspan="3" style="vertical-align: middle;">Shift</th>
<!-- Category Headers will be appended here via JS --> <!-- Category Headers will be appended here via JS -->
@ -389,6 +397,10 @@
var match = data.match(/showMedia\('([^']+)'/); var match = data.match(/showMedia\('([^']+)'/);
if (match) return match[1]; if (match) return match[1];
} }
if (typeof data === 'string' && data.indexOf('data-fulltext') !== -1) {
var match = data.match(/data-fulltext="([^"]+)"/);
if (match) return decodeURIComponent(match[1]);
}
var div = document.createElement("div"); var div = document.createElement("div");
div.innerHTML = data; div.innerHTML = data;
return div.textContent || div.innerText || ""; return div.textContent || div.innerText || "";
@ -409,6 +421,10 @@
var match = data.match(/showMedia\('([^']+)'/); var match = data.match(/showMedia\('([^']+)'/);
if (match) return match[1]; if (match) return match[1];
} }
if (typeof data === 'string' && data.indexOf('data-fulltext') !== -1) {
var match = data.match(/data-fulltext="([^"]+)"/);
if (match) return decodeURIComponent(match[1]);
}
var div = document.createElement("div"); var div = document.createElement("div");
div.innerHTML = data; div.innerHTML = data;
return div.textContent || div.innerText || ""; return div.textContent || div.innerText || "";
@ -667,7 +683,7 @@
if (index === 0) { if (index === 0) {
tr.append('<td class="text-center align-middle" rowspan="' + rowSpanCount + '">' + hub.zone + '</td>'); tr.append('<td class="text-center align-middle" rowspan="' + rowSpanCount + '">' + hub.zone + '</td>');
tr.append('<td class="text-center align-middle" rowspan="' + rowSpanCount + '" style="white-space: normal; min-width: 250px; max-width: 350px;">' + hub.name_address + '</td>');
tr.append('<td class="text-center align-middle" rowspan="' + rowSpanCount + '" style="white-space: normal; word-break: break-word; min-width: 150px; max-width: 200px;">' + hub.name_address + '</td>');
} else { } else {
// Maintain perfect column counts for DataTables matrix validation to completely prevent tn/18 error! // Maintain perfect column counts for DataTables matrix validation to completely prevent tn/18 error!
tr.append('<td style="display: none;"></td>'); tr.append('<td style="display: none;"></td>');
@ -784,14 +800,14 @@
item.english_name || '-', item.english_name || '-',
item.name || '-', item.name || '-',
item.raisedBy || '-', item.raisedBy || '-',
item.pmcRemarks || '-',
formatRemark(item.pmcRemarks, 'PMC Remarks'),
imgBtn, imgBtn,
item.fsStatus || '-', item.fsStatus || '-',
item.fsChallengedBy || '-', item.fsChallengedBy || '-',
item.fsRemarks || '-',
formatRemark(item.fsRemarks, 'FS Remarks'),
fsImgBtn, fsImgBtn,
item.chief_engineer_status || '-', item.chief_engineer_status || '-',
item.chief_engineer_remarks || '-',
formatRemark(item.chief_engineer_remarks, 'CE Remarks'),
item.chief_engineer || '-', item.chief_engineer || '-',
finalPenalty finalPenalty
]); ]);
@ -802,6 +818,32 @@
}, 300); }, 300);
} }
function formatRemark(text, title) {
if (!text || text === '-' || String(text).trim() === '' || String(text) === 'null') {
return '-';
}
var str = String(text);
if (str.length > 100) {
var truncated = $('<div>').text(str.substring(0, 100) + '...').html();
var encodedText = encodeURIComponent(str);
return '<span class="text-primary fw-bold" style="cursor:pointer; text-decoration:underline;" onclick="showFullRemark(\'' + title + '\', \'' + encodedText + '\')" title="Click to view full remark" data-fulltext="' + encodedText + '">' + truncated + '</span>';
}
return $('<div>').text(str).html();
}
function showFullRemark(title, encodedText) {
var text = decodeURIComponent(encodedText);
Swal.fire({
title: title,
html: '<div style="text-align: left; white-space: pre-wrap; font-size: 18px; max-height: 400px; overflow-y: auto; padding: 10px; background: #f8f9fa; border-radius: 5px; border: 1px solid #dee2e6;">' + $('<div>').text(text).html() + '</div>',
width: '600px',
confirmButtonText: 'Close',
customClass: {
popup: 'swal2-custom-zindex' // Just in case it needs to be higher than bootstrap modal
}
});
}
function showMedia(url, isVideo) { function showMedia(url, isVideo) {
var mediaHtml = ''; var mediaHtml = '';
if (isVideo) { if (isVideo) {
@ -942,6 +984,13 @@
} }
}); });
// if (true) {
// var a = 5;
// }
// console.log(a);
// Error
// Recalculate precisely after Bootstrap entirely finishes destroying modal padding // Recalculate precisely after Bootstrap entirely finishes destroying modal padding
</script> </script>

Loading…
Cancel
Save