5 changed files with 716 additions and 1 deletions
-
22src/main/java/com/gcc/commissionerreview/configuration/DataSourceConfig.java
-
72src/main/java/com/gcc/commissionerreview/controller/FlagpolesApiController.java
-
6src/main/java/com/gcc/commissionerreview/controller/MainController.java
-
199src/main/java/com/gcc/commissionerreview/service/FlagpolesService.java
-
416src/main/resources/templates/modules/flagpoles/abstractreport.html
@ -0,0 +1,72 @@ |
|||
package com.gcc.commissionerreview.controller; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import org.springframework.ui.Model; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.servlet.ModelAndView; |
|||
import com.gcc.commissionerreview.service.FlagpolesService; |
|||
import java.util.Map; |
|||
import java.util.List; |
|||
|
|||
@RestController |
|||
@RequestMapping("/commissionerreview/api") |
|||
public class FlagpolesApiController { |
|||
|
|||
@Autowired |
|||
private FlagpolesService flagpolesService; |
|||
|
|||
@GetMapping("/flagpoles/abstract") |
|||
public Map<String, Object> getAbstractData( |
|||
@RequestParam(required = false) String fromDate, |
|||
@RequestParam(required = false) String toDate, |
|||
@RequestParam(required = false) String applicationType) { |
|||
try { |
|||
List<Map<String, Object>> data = flagpolesService.getZoneWiseRequestCounts(fromDate, toDate, |
|||
applicationType); |
|||
// Only fetch summary if no type filter or type is NULL (to avoid re-fetching |
|||
// summary when just filtering table, but front-end might want to keep it) |
|||
// Actually, keep fetching summary so numbers don't disappear if specific logic |
|||
// changes. |
|||
// But usually summary counts should stay static for the date range unless date |
|||
// changes. |
|||
Map<String, Integer> summary = flagpolesService.getTypeWiseCounts(fromDate, toDate); |
|||
return Map.of("status", "success", "data", data, "summary", summary); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
return Map.of("status", "error", "message", e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@GetMapping("/flagpoles/details") |
|||
public Map<String, Object> getRequestDetails( |
|||
@RequestParam String zone, |
|||
@RequestParam(required = false) String status, |
|||
@RequestParam(required = false) String fromDate, |
|||
@RequestParam(required = false) String toDate) { |
|||
try { |
|||
List<Map<String, Object>> data = flagpolesService.getRequestDetails(zone, status, fromDate, toDate); |
|||
return Map.of("status", "success", "data", data); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
return Map.of("status", "error", "message", e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@GetMapping("/flagpoles/street-details") |
|||
public Map<String, Object> getStreetDetails(@RequestParam int requestId) { |
|||
try { |
|||
List<Map<String, Object>> data = flagpolesService.getStreetDetails(requestId); |
|||
return Map.of("status", "success", "data", data); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
return Map.of("status", "error", "message", e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,199 @@ |
|||
package com.gcc.commissionerreview.service; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
import javax.sql.DataSource; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class FlagpolesService { |
|||
|
|||
private JdbcTemplate jdbcTemplate; |
|||
|
|||
@Autowired |
|||
public void setDataSource(@Qualifier("mysqlFlagPoleManagerSystemDataSource") DataSource dataSource) { |
|||
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|||
} |
|||
|
|||
public List<Map<String, Object>> getZoneWiseRequestCounts(String fromDate, String toDate, String applicationType) { |
|||
StringBuilder sql = new StringBuilder("SELECT " + |
|||
" s.zone, " + |
|||
" COUNT(u.id) as total_requests, " + |
|||
" SUM(CASE WHEN u.final_status = 'Approved' THEN 1 ELSE 0 END) as approved_count, " + |
|||
" SUM(CASE WHEN u.final_status = 'Rejected' THEN 1 ELSE 0 END) as rejected_count " + |
|||
"FROM user_request_details u " + |
|||
"JOIN street_details s ON u.id = s.user_req_id " + |
|||
"LEFT JOIN application_type at ON u.application_type = at.id "); |
|||
|
|||
List<Object> params = new java.util.ArrayList<>(); |
|||
|
|||
// Helper to handle date and type filters |
|||
boolean whereAdded = false; |
|||
|
|||
if (fromDate != null && !fromDate.isEmpty() && toDate != null && !toDate.isEmpty()) { |
|||
sql.append("WHERE DATE(u.cdate) BETWEEN ? AND ? "); |
|||
params.add(fromDate); |
|||
params.add(toDate); |
|||
whereAdded = true; |
|||
} |
|||
|
|||
if (applicationType != null && !applicationType.isEmpty() && !applicationType.equalsIgnoreCase("OVERALL")) { |
|||
if (whereAdded) { |
|||
sql.append("AND at.type = ? "); |
|||
} else { |
|||
sql.append("WHERE at.type = ? "); |
|||
whereAdded = true; |
|||
} |
|||
params.add(applicationType); |
|||
} |
|||
|
|||
sql.append("GROUP BY s.zone ORDER BY s.zone"); |
|||
|
|||
List<Map<String, Object>> dbResults = jdbcTemplate.queryForList(sql.toString(), params.toArray()); |
|||
|
|||
// Parse DB results into a Map keyed by Zone Number for robust matching |
|||
Map<Integer, Map<String, Object>> zoneDataMap = new java.util.HashMap<>(); |
|||
for (Map<String, Object> row : dbResults) { |
|||
String zoneStr = (String) row.get("zone"); |
|||
if (zoneStr != null) { |
|||
// Extract number from "Zone 10" or "Zone 05" |
|||
String numberOnly = zoneStr.replaceAll("[^0-9]", ""); |
|||
if (!numberOnly.isEmpty()) { |
|||
try { |
|||
int zoneNum = Integer.parseInt(numberOnly); |
|||
// Update zone display format to "01", "02", etc. |
|||
row.put("zone", String.format("%02d", zoneNum)); |
|||
zoneDataMap.put(zoneNum, row); |
|||
} catch (NumberFormatException e) { |
|||
// ignore malformed zone names |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Generate final list for Zones 1 to 15 |
|||
List<Map<String, Object>> finalResults = new java.util.ArrayList<>(); |
|||
for (int i = 1; i <= 15; i++) { |
|||
if (zoneDataMap.containsKey(i)) { |
|||
finalResults.add(zoneDataMap.get(i)); |
|||
} else { |
|||
Map<String, Object> emptyRow = new java.util.HashMap<>(); |
|||
emptyRow.put("zone", String.format("%02d", i)); |
|||
emptyRow.put("total_requests", 0); |
|||
emptyRow.put("approved_count", 0); |
|||
emptyRow.put("rejected_count", 0); |
|||
finalResults.add(emptyRow); |
|||
} |
|||
} |
|||
|
|||
return finalResults; |
|||
} |
|||
|
|||
public List<Map<String, Object>> getRequestDetails(String zone, String status, String fromDate, String toDate) { |
|||
StringBuilder sql = new StringBuilder("SELECT " + |
|||
" u.id, " + |
|||
" u.refid, " + |
|||
" u.applicant_name, " + |
|||
" u.mobile_number, " + |
|||
" u.event_desc, " + |
|||
" DATE_FORMAT(u.event_date, '%d-%m-%Y') as event_date, " + |
|||
" u.final_status, " + |
|||
" at.type as application_type, " + |
|||
" ao.option_name as application_option, " + |
|||
" it.installation_name as installation_type " + |
|||
"FROM user_request_details u " + |
|||
"LEFT JOIN application_type at ON u.application_type = at.id " + |
|||
"LEFT JOIN application_option ao ON u.application_option = ao.id " + |
|||
"LEFT JOIN installation_type it ON u.installation_type = it.id " + |
|||
"LEFT JOIN street_details s ON u.id = s.user_req_id " + |
|||
"WHERE 1=1 "); |
|||
|
|||
List<Object> params = new java.util.ArrayList<>(); |
|||
|
|||
if (zone != null && !zone.isEmpty()) { |
|||
// Handle various zone formats (01, 1, Zone 1, Zone 01) |
|||
String zoneNumStr = zone.replaceAll("[^0-9]", ""); |
|||
if (!zoneNumStr.isEmpty()) { |
|||
try { |
|||
int z = Integer.parseInt(zoneNumStr); |
|||
sql.append("AND (s.zone = ? OR s.zone = ? OR s.zone = ? OR s.zone = ?) "); |
|||
params.add("Zone " + z); |
|||
params.add("Zone " + String.format("%02d", z)); |
|||
params.add(String.valueOf(z)); |
|||
params.add(String.format("%02d", z)); |
|||
} catch (NumberFormatException e) { |
|||
sql.append("AND s.zone = ? "); |
|||
params.add(zone); |
|||
} |
|||
} else { |
|||
sql.append("AND s.zone = ? "); |
|||
params.add(zone); |
|||
} |
|||
} |
|||
|
|||
if (status != null && !status.isEmpty() && !status.equalsIgnoreCase("Total")) { |
|||
sql.append("AND u.final_status = ? "); |
|||
params.add(status); |
|||
} |
|||
|
|||
if (fromDate != null && !fromDate.isEmpty() && toDate != null && !toDate.isEmpty()) { |
|||
sql.append("AND DATE(u.cdate) BETWEEN ? AND ? "); |
|||
params.add(fromDate); |
|||
params.add(toDate); |
|||
} |
|||
|
|||
sql.append("GROUP BY u.id ORDER BY u.cdate DESC"); |
|||
|
|||
return jdbcTemplate.queryForList(sql.toString(), params.toArray()); |
|||
} |
|||
|
|||
public List<Map<String, Object>> getStreetDetails(int requestId) { |
|||
String sql = "SELECT " + |
|||
" s.zone, " + |
|||
" s.ward, " + |
|||
" s.street_name, " + |
|||
" s.no_of_poles, " + |
|||
" s.no_of_statue " + |
|||
"FROM street_details s " + |
|||
"WHERE s.user_req_id = ?"; |
|||
return jdbcTemplate.queryForList(sql, requestId); |
|||
} |
|||
|
|||
public Map<String, Integer> getTypeWiseCounts(String fromDate, String toDate) { |
|||
StringBuilder sql = new StringBuilder("SELECT " + |
|||
" at.type, " + |
|||
" COUNT(u.id) as count " + |
|||
"FROM user_request_details u " + |
|||
"JOIN application_type at ON u.application_type = at.id "); |
|||
|
|||
Object[] params = new Object[] {}; |
|||
|
|||
if (fromDate != null && !fromDate.isEmpty() && toDate != null && !toDate.isEmpty()) { |
|||
sql.append("WHERE DATE(u.cdate) BETWEEN ? AND ? "); |
|||
params = new Object[] { fromDate, toDate }; |
|||
} |
|||
|
|||
sql.append("GROUP BY at.type"); |
|||
|
|||
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql.toString(), params); |
|||
Map<String, Integer> counts = new java.util.HashMap<>(); |
|||
counts.put("TEMPORARY", 0); |
|||
counts.put("PERMANENT", 0); |
|||
|
|||
int total = 0; |
|||
for (Map<String, Object> row : results) { |
|||
String type = ((String) row.get("type")).toUpperCase(); |
|||
Number count = (Number) row.get("count"); |
|||
int val = count.intValue(); |
|||
counts.put(type, val); |
|||
total += val; |
|||
} |
|||
counts.put("OVERALL", total); |
|||
return counts; |
|||
} |
|||
} |
|||
@ -0,0 +1,416 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> |
|||
|
|||
<head> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<title>Flagpoles Abstract Report</title> |
|||
|
|||
<!-- Bootstrap 5 CSS --> |
|||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.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> |
|||
table.dataTable th, |
|||
table.dataTable td { |
|||
text-align: center; |
|||
vertical-align: middle; |
|||
border: 1px solid #000; |
|||
} |
|||
|
|||
table.dataTable th { |
|||
background-color: #003366 !important; |
|||
color: white; |
|||
text-align: center !important; |
|||
} |
|||
|
|||
.page-header-title h4 { |
|||
margin: 20px 0; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div class="page-header-title"> |
|||
<h4>Flagpoles Abstract Report</h4> |
|||
</div> |
|||
|
|||
<div class="container mt-4"> |
|||
<div class="card p-3 shadow-sm"> |
|||
<h5 class="mb-3"><strong>Report</strong></h5> |
|||
|
|||
<div class="row g-3 align-items-end mb-3"> |
|||
<div class="col-auto"> |
|||
<label for="fromDate" class="form-label">From Date</label> |
|||
<input type="date" id="fromDate" class="form-control"> |
|||
</div> |
|||
<div class="col-auto"> |
|||
<label for="toDate" class="form-label">To Date</label> |
|||
<input type="date" id="toDate" class="form-control"> |
|||
</div> |
|||
<div class="col-auto"> |
|||
<button class="btn btn-primary" id="searchBtn">Search</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Summary Cards --> |
|||
<!-- Summary Card --> |
|||
<!-- Summary Card --> |
|||
<div class="card mb-3 shadow-sm border-primary"> |
|||
<div class="card-body"> |
|||
<div class="row text-center"> |
|||
<div class="col-md-4 border-end" style="cursor: pointer;" onclick="loadReport('OVERALL')"> |
|||
<h6 class="text-primary fw-bold">Total Overall Requests</h6> |
|||
<h3 id="overallCount">0</h3> |
|||
</div> |
|||
<div class="col-md-4 border-end" style="cursor: pointer;" onclick="loadReport('TEMPORARY')"> |
|||
<h6 class="text-info fw-bold">Total Temporary Requests</h6> |
|||
<h3 id="tempCount">0</h3> |
|||
</div> |
|||
<div class="col-md-4" style="cursor: pointer;" onclick="loadReport('PERMANENT')"> |
|||
<h6 class="text-success fw-bold">Total Permanent Requests</h6> |
|||
<h3 id="permCount">0</h3> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<table id="reportTable" class="table table-bordered table-hover mt-3 text-center"> |
|||
<thead class="table-primary"> |
|||
<tr> |
|||
<th class="text-center">Zone</th> |
|||
<th class="text-center">Total Requests</th> |
|||
<th class="text-center">Approved</th> |
|||
<th class="text-center">Rejected</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="reportTableBody"> |
|||
<!-- Data will be populated here --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="pageLoader" |
|||
style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); z-index: 9999; text-align: center;"> |
|||
<div style="position: relative; top: 50%; transform: translateY(-50%);"> |
|||
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"> |
|||
<span class="sr-only"></span> |
|||
</div> |
|||
<p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Details Modal --> |
|||
<div class="modal fade" id="detailsModal" tabindex="-1" aria-labelledby="detailsModalLabel" aria-hidden="true"> |
|||
<div class="modal-dialog modal-xl"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h5 class="modal-title" id="detailsModalLabel">Request Details</h5> |
|||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<div id="detailsPageLoader" style="display: none; text-align: center; padding: 20px;"> |
|||
<div class="spinner-border text-primary" role="status"> |
|||
<span class="sr-only"></span> |
|||
</div> |
|||
<p>Loading details...</p> |
|||
</div> |
|||
<table id="detailsTable" class="table table-bordered table-striped" style="width:100%"> |
|||
<thead class="table-dark"> |
|||
<tr> |
|||
<th>S.No</th> |
|||
<th>Ref ID</th> |
|||
<th>Applicant Name</th> |
|||
<th>Mobile</th> |
|||
<th>App Type</th> |
|||
<th>App Option</th> |
|||
<th>Install Type</th> |
|||
<th>Event Desc</th> |
|||
<th>Event Date</th> |
|||
<th>Status</th> |
|||
<th>Action</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="detailsTableBody"> |
|||
<!-- Details will be populated here --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> |
|||
</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.1.36/pdfmake.min.js"></script> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js"></script> |
|||
|
|||
<script> |
|||
let dataTable; |
|||
|
|||
function initDataTable() { |
|||
dataTable = $('#reportTable').DataTable({ |
|||
dom: 'Bfrtip', |
|||
paging: false, |
|||
searching: false, |
|||
ordering: false, // Let SQL handle ordering by Zone |
|||
info: false, |
|||
buttons: [ |
|||
{ |
|||
extend: 'excelHtml5', |
|||
title: 'Flagpoles_Abstract_Report', |
|||
text: 'Download Excel', |
|||
className: 'btn btn-success btn-sm' |
|||
}, |
|||
{ |
|||
extend: 'pdfHtml5', |
|||
title: 'Flagpoles_Abstract_Report', |
|||
text: 'Download PDF', |
|||
className: 'btn btn-danger btn-sm', |
|||
orientation: 'portrait', |
|||
pageSize: 'A4' |
|||
} |
|||
] |
|||
}); |
|||
} |
|||
|
|||
let currentType = 'OVERALL'; |
|||
|
|||
function loadReport(type) { |
|||
if (type) { |
|||
currentType = type; |
|||
} else { |
|||
if (!currentType) currentType = 'OVERALL'; |
|||
} |
|||
|
|||
const fromDate = document.getElementById("fromDate").value; |
|||
const toDate = document.getElementById("toDate").value; |
|||
|
|||
// Update UI to show selected filter (optional visual cue) |
|||
// Reset colors |
|||
$('.card-body .col-md-4').removeClass('bg-light'); |
|||
// Highlight selected |
|||
if (currentType === 'OVERALL') $('.card-body .col-md-4:eq(0)').addClass('bg-light'); |
|||
if (currentType === 'TEMPORARY') $('.card-body .col-md-4:eq(1)').addClass('bg-light'); |
|||
if (currentType === 'PERMANENT') $('.card-body .col-md-4:eq(2)').addClass('bg-light'); |
|||
|
|||
$('#pageLoader').show(); |
|||
|
|||
let url = `/commissionerreview/api/flagpoles/abstract?fromDate=${fromDate}&toDate=${toDate}`; |
|||
if (currentType && currentType !== 'OVERALL') { |
|||
url += `&applicationType=${currentType}`; |
|||
} |
|||
|
|||
fetch(url) |
|||
.then(res => res.json()) |
|||
.then(json => { |
|||
$('#pageLoader').hide(); |
|||
if (json.status === "success") { |
|||
populateTable(json.data); |
|||
if (json.summary) { |
|||
$('#tempCount').text(json.summary.TEMPORARY || 0); |
|||
$('#permCount').text(json.summary.PERMANENT || 0); |
|||
$('#overallCount').text(json.summary.OVERALL || 0); |
|||
} else { |
|||
if (!type) { // Only zero out if not maintaining state, but here we keep summary if just filtering |
|||
// actually backend returns summary always so update it matches current date range |
|||
$('#tempCount').text(0); |
|||
$('#permCount').text(0); |
|||
$('#overallCount').text(0); |
|||
} |
|||
} |
|||
} else { |
|||
alert("Error: " + json.message); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
$('#pageLoader').hide(); |
|||
console.error("Fetch Error:", err); |
|||
alert("Failed to load data"); |
|||
}); |
|||
} |
|||
|
|||
function populateTable(data) { |
|||
dataTable.clear(); |
|||
|
|||
data.forEach(row => { |
|||
dataTable.row.add([ |
|||
row.zone, |
|||
`<a href="#" onclick="viewDetails('${row.zone}', 'Total')">${row.total_requests}</a>`, |
|||
`<a href="#" onclick="viewDetails('${row.zone}', 'Approved')">${row.approved_count}</a>`, |
|||
`<a href="#" onclick="viewDetails('${row.zone}', 'Rejected')">${row.rejected_count}</a>` |
|||
]); |
|||
}); |
|||
|
|||
dataTable.draw(); |
|||
} |
|||
|
|||
function viewDetails(zone, status) { |
|||
const fromDate = document.getElementById("fromDate").value; |
|||
const toDate = document.getElementById("toDate").value; |
|||
|
|||
$('#detailsModalLabel').text(`${status} Requests for ${zone}`); |
|||
$('#detailsPageLoader').show(); |
|||
// Clear previous table data |
|||
if ($.fn.DataTable.isDataTable('#detailsTable')) { |
|||
$('#detailsTable').DataTable().destroy(); |
|||
} |
|||
$('#detailsTableBody').empty(); |
|||
|
|||
new bootstrap.Modal(document.getElementById('detailsModal')).show(); |
|||
|
|||
fetch(`/commissionerreview/api/flagpoles/details?zone=${zone}&status=${status}&fromDate=${fromDate}&toDate=${toDate}`) |
|||
.then(res => res.json()) |
|||
.then(json => { |
|||
$('#detailsPageLoader').hide(); |
|||
if (json.status === "success") { |
|||
populateDetailsTable(json.data); |
|||
} else { |
|||
alert("Error: " + json.message); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
$('#detailsPageLoader').hide(); |
|||
console.error("Fetch Error:", err); |
|||
alert("Failed to load details"); |
|||
}); |
|||
} |
|||
|
|||
function formatValue(val) { |
|||
if (val === null || val === undefined || val === 'null' || val === '') { |
|||
return '-'; |
|||
} |
|||
return val; |
|||
} |
|||
|
|||
function populateDetailsTable(data) { |
|||
const tbody = $('#detailsTableBody'); |
|||
tbody.empty(); |
|||
|
|||
data.forEach((row, index) => { |
|||
tbody.append(` |
|||
<tr> |
|||
<td>${index + 1}</td> |
|||
<td>${formatValue(row.refid)}</td> |
|||
<td>${formatValue(row.applicant_name)}</td> |
|||
<td>${formatValue(row.mobile_number)}</td> |
|||
<td>${formatValue(row.application_type)}</td> |
|||
<td>${formatValue(row.application_option)}</td> |
|||
<td>${formatValue(row.installation_type)}</td> |
|||
<td>${formatValue(row.event_desc)}</td> |
|||
<td>${formatValue(row.event_date)}</td> |
|||
<td>${formatValue(row.final_status)}</td> |
|||
<td> |
|||
<button class="btn btn-info btn-sm" onclick="viewStreetDetails(${row.id})"> |
|||
View Streets |
|||
</button> |
|||
</td> |
|||
</tr> |
|||
`); |
|||
}); |
|||
|
|||
$('#detailsTable').DataTable({ |
|||
dom: 'Bfrtip', |
|||
buttons: [ |
|||
'excelHtml5', |
|||
'pdfHtml5' |
|||
] |
|||
}); |
|||
} |
|||
|
|||
function viewStreetDetails(requestId) { |
|||
$('#streetModalLabel').text(`Street Details for Request ID`); |
|||
$('#streetTableBody').html('<tr><td colspan="5" class="text-center">Loading...</td></tr>'); |
|||
new bootstrap.Modal(document.getElementById('streetModal')).show(); |
|||
|
|||
fetch(`/commissionerreview/api/flagpoles/street-details?requestId=${requestId}`) |
|||
.then(res => res.json()) |
|||
.then(json => { |
|||
const tbody = $('#streetTableBody'); |
|||
tbody.empty(); |
|||
if (json.status === "success" && json.data.length > 0) { |
|||
json.data.forEach(row => { |
|||
tbody.append(` |
|||
<tr> |
|||
<td>${formatValue(row.zone)}</td> |
|||
<td>${formatValue(row.ward)}</td> |
|||
<td>${formatValue(row.street_name)}</td> |
|||
<td>${formatValue(row.no_of_poles)}</td> |
|||
<td>${formatValue(row.no_of_statue)}</td> |
|||
</tr> |
|||
`); |
|||
}); |
|||
} else { |
|||
tbody.html('<tr><td colspan="5" class="text-center">No street details found</td></tr>'); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
console.error("Fetch Error:", err); |
|||
$('#streetTableBody').html('<tr><td colspan="5" class="text-center text-danger">Failed to load data</td></tr>'); |
|||
}); |
|||
} |
|||
|
|||
$(document).ready(function () { |
|||
console.log("Document ready, initializing..."); |
|||
initDataTable(); |
|||
|
|||
// Bind search button click |
|||
$('#searchBtn').click(function () { |
|||
loadReport(); |
|||
}); |
|||
|
|||
// Initial load |
|||
loadReport(); |
|||
}); |
|||
</script> |
|||
|
|||
<!-- Street Details Modal --> |
|||
<div class="modal fade" id="streetModal" tabindex="-1" aria-labelledby="streetModalLabel" aria-hidden="true"> |
|||
<div class="modal-dialog modal-lg"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h5 class="modal-title" id="streetModalLabel">Street Details</h5> |
|||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<table class="table table-bordered table-striped" width="100%"> |
|||
<thead class="table-dark"> |
|||
<tr> |
|||
<th>Zone</th> |
|||
<th>Ward</th> |
|||
<th>Street Name</th> |
|||
<th>Poles</th> |
|||
<th>Statues</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="streetTableBody"> |
|||
<!-- Streets will be populated here --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue