Browse Source

changes in flagpole dashboard ui and service, controller and pages

master
yoganandhan 8 months ago
parent
commit
011830b687
  1. 10
      src/main/java/com/gcc/commissionerreview/controller/FlagpolesApiController.java
  2. 20
      src/main/java/com/gcc/commissionerreview/controller/MainController.java
  3. 27
      src/main/java/com/gcc/commissionerreview/service/FlagpolesService.java
  4. 354
      src/main/resources/templates/modules/flagpoles/homepage.html
  5. 672
      src/main/resources/templates/modules/flagpoles/permanent_request.html
  6. 676
      src/main/resources/templates/modules/flagpoles/temporary_request.html

10
src/main/java/com/gcc/commissionerreview/controller/FlagpolesApiController.java

@ -2,14 +2,13 @@ package com.gcc.commissionerreview.controller;
import java.util.Map; import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.gcc.commissionerreview.service.FlagpolesService; import com.gcc.commissionerreview.service.FlagpolesService;
import java.util.Map; import java.util.Map;
import java.util.List; import java.util.List;
@ -71,4 +70,11 @@ public class FlagpolesApiController {
} }
} }
@GetMapping("/flagpoles/admin-reports")
public Map<String, Object> getAdminReports(
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
return flagpolesService.getAdminReports(startDate, endDate);
}
} }

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

@ -261,9 +261,24 @@ public class MainController {
// visistor based count // visistor based count
// @GetMapping("/flagpolereporthomepage")
// public String getFlagpoleReportHomePage(Model model) {
// return "modules/flagpoles/homepage";
// }
// @GetMapping("/flagpolereport")
// public String getFlagpoleReportPage(Model model) {
// return "modules/flagpoles/abstractreport";
// }
@GetMapping("/flagpolereport") @GetMapping("/flagpolereport")
public String getFlagpoleReportPage(Model model) {
return "modules/flagpoles/abstractreport";
public String temporaryRequestPage(Model model) {
return "modules/flagpoles/temporary_request";
}
@GetMapping("/flagpoles/permanent")
public String permanentRequestPage(Model model) {
return "modules/flagpoles/permanent_request";
} }
@GetMapping("/attendance-dashboard") @GetMapping("/attendance-dashboard")
@ -275,4 +290,5 @@ public class MainController {
public String greencommitteereport(Model model) { public String greencommitteereport(Model model) {
return "modules/greencommittee/gcreport"; return "modules/greencommittee/gcreport";
} }
} }

27
src/main/java/com/gcc/commissionerreview/service/FlagpolesService.java

@ -202,4 +202,31 @@ public class FlagpolesService {
counts.put("OVERALL", total); counts.put("OVERALL", total);
return counts; return counts;
} }
public Map<String, Object> getAdminReports(String startDate, String endDate) {
String url = "https://gccservices.in/flagpoles/api/flagpoleregistration/getAdminReports?gccUserId=140";
if (startDate != null && !startDate.isEmpty() && endDate != null && !endDate.isEmpty()) {
url += "&startDate=" + startDate + "&endDate=" + endDate;
}
org.springframework.web.client.RestTemplate restTemplate = new org.springframework.web.client.RestTemplate();
try {
@SuppressWarnings("unchecked")
Map<String, Object> response = restTemplate.getForObject(url, Map.class);
if (response == null || response.isEmpty() || !response.containsKey("data")) {
Map<String, Object> empty = new java.util.HashMap<>();
empty.put("status", "success");
empty.put("count", 0);
empty.put("data", java.util.Collections.emptyList());
return empty;
}
return response;
} catch (Exception e) {
e.printStackTrace();
Map<String, Object> error = new java.util.HashMap<>();
error.put("status", "error");
error.put("message", "Failed to fetch external data: " + e.getMessage());
return error;
}
}
} }

354
src/main/resources/templates/modules/flagpoles/homepage.html

@ -0,0 +1,354 @@
<!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,
.table-dark 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 Home Page:</h4>
</div>
<div class="container mt-4">
<div class="card p-3 shadow-sm">
<div class="row g-3 align-items-end mb-3 justify-content-center">
<div class="col-auto">
<button type="button" class="btn btn-primary" id="temporarybtn"
onclick="location.href='/commissionerreview/flagpoles/temporary'">Temporary Request</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-primary" id="permanentbtn"
onclick="location.href='/commissionerreview/flagpoles/permanent'">Permanent Request</button>
</div>
</div>
</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 -->
<!-- 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: ${zone}`);
$('#detailsPageLoader').show();
// Clear previous table data
if ($.fn.DataTable.isDataTable('#detailsTable')) {
$('#detailsTable').DataTable().destroy();
}
$('#detailsTableBody').empty();
new bootstrap.Modal(document.getElementById('detailsModal')).show();
let url = `/commissionerreview/api/flagpoles/details?zone=${zone}&status=${status}&fromDate=${fromDate}&toDate=${toDate}`;
if (currentType && currentType !== 'OVERALL') {
url += `&applicationType=${currentType}`;
}
fetch(url)
.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();
// <td>${formatValue(row.application_option)}</td>
// <td>${formatValue(row.installation_type)}</td>
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.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}, '${row.refid}')">
View Streets
</button>
</td>
</tr>
`);
});
var table = $('#detailsTable').DataTable({
dom: 'Bfrtip',
scrollX: true,
buttons: [
'excelHtml5',
'pdfHtml5'
]
});
// Adjust columns after small delay to ensure modal transition ends
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
}
function viewStreetDetails(requestId, refId) {
$('#streetModalLabel').text(`Street Details for Request: ${refId}`);
// Show modal
const modal = new bootstrap.Modal(document.getElementById('streetModal'));
modal.show();
// Destroy existing DataTable if it exists
if ($.fn.DataTable.isDataTable('#streetTable')) {
$('#streetTable').DataTable().destroy();
}
const tbody = $('#streetTableBody');
tbody.empty();
tbody.html('<tr><td colspan="6" class="text-center">Loading...</td></tr>');
fetch(`/commissionerreview/api/flagpoles/street-details?requestId=${requestId}`)
.then(res => res.json())
.then(json => {
tbody.empty();
if (json.status === "success" && json.data.length > 0) {
json.data.forEach((row, index) => {
tbody.append(`
<tr>
<td>${index + 1}</td>
<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>
`);
});
// Initialize DataTable
var table = $('#streetTable').DataTable({
dom: 'Bfrtip',
scrollX: true,
buttons: [
'excelHtml5',
'pdfHtml5'
]
});
// Adjust columns
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
} else {
tbody.html('<tr><td colspan="6" class="text-center">No street details found</td></tr>');
}
})
.catch(err => {
console.error("Fetch Error:", err);
tbody.html('<tr><td colspan="6" 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 (e) {
e.preventDefault();
loadReport();
});
// Initial load
loadReport();
// Fix DataTable alignment in modals
$('#detailsModal, #streetModal').on('shown.bs.modal', function () {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
});
</script>
</body>
</html>

672
src/main/resources/templates/modules/flagpoles/permanent_request.html

@ -0,0 +1,672 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Permanent Flagpole Request 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,
.table-dark th {
background-color: #003366 !important;
color: white;
text-align: center !important;
}
.page-header-title h4 {
margin: 20px 0;
text-align: center;
}
.clickable-card {
cursor: pointer;
transition: background-color 0.2s;
}
.clickable-card:hover {
background-color: #e2e6ea;
}
.card-active {
background-color: #e2e6ea;
border: 2px solid #0d6efd !important;
}
</style>
</head>
<body>
<div class="container mt-4">
<div class="card p-3 shadow-sm">
<div class="text-center mb-4">
<h4 class="fw-bold text-primary">Permanent Flagpole Request Report</h4>
<div class="d-flex justify-content-center align-items-center gap-2 mt-2">
<label class="fw-bold">Application Type:</label>
<select class="form-select form-select-sm" style="width: auto;"
onchange="if(this.value) window.location.href=this.value;">
<option value="/commissionerreview/flagpolereport">Temporary</option>
<option value="/commissionerreview/flagpoles/permanent" selected>Permanent</option>
</select>
</div>
</div>
<!-- Responsive Filter Section -->
<div class="row g-2 mb-3 justify-content-center justify-content-md-end align-items-end">
<div class="col-6 col-md-auto">
<label class="form-label fw-bold small">From Date</label>
<input type="date" id="fromDate" class="form-control form-control-sm">
</div>
<div class="col-6 col-md-auto">
<label class="form-label fw-bold small">To Date</label>
<input type="date" id="toDate" class="form-control form-control-sm">
</div>
<div class="col-12 col-md-auto">
<div class="d-flex gap-2">
<button class="btn btn-primary btn-sm flex-grow-1 flex-md-grow-0"
onclick="filterByDate()">Search</button>
<button class="btn btn-outline-secondary btn-sm flex-grow-1 flex-md-grow-0"
onclick="resetDateFilter()">Reset</button>
</div>
</div>
</div>
<!-- Summary Cards -->
<div class="card mb-3 shadow-sm border-primary">
<div class="card-body">
<div class="row text-center">
<div id="card-all" class="col-6 col-md-3 border-end clickable-card card-active"
onclick="filterReport('ALL')">
<h6 class="text-primary fw-bold">Total Requests</h6>
<h3 id="totalRequests">0</h3>
</div>
<div id="card-approved" class="col-6 col-md-3 border-end clickable-card"
onclick="filterReport('APPROVED')">
<h6 class="text-success fw-bold">Approved</h6>
<h3 id="totalApproved">0</h3>
</div>
<div id="card-rejected" class="col-6 col-md-3 border-end clickable-card"
onclick="filterReport('REJECTED')">
<h6 class="text-danger fw-bold">Rejected</h6>
<h3 id="totalRejected">0</h3>
</div>
<div class="col-6 col-md-3">
<h6 class="text-warning fw-bold">Total Amount Earned</h6>
<h3 id="totalAmount">0</h3>
</div>
</div>
</div>
</div>
<div class="table-responsive">
<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>
<th class="text-center">Total Amount</th>
</tr>
</thead>
<tbody id="reportTableBody">
<!-- Data will be populated here -->
</tbody>
</table>
</div>
</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 class="table-responsive">
<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>Application Type</th>
<th>Event Desc</th>
<th>Event Date</th>
<th>Total Cost</th>
<th>Status</th>
<th>History</th>
<th>Uploaded Images</th>
<th>Action</th>
</tr>
</thead>
<tbody id="detailsTableBody">
<!-- Details will be populated here -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- 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">
<div class="table-responsive">
<table id="streetTable" class="table table-bordered table-striped" width="100%">
<thead class="table-dark">
<tr>
<th>S.No</th>
<th>Zone</th>
<th>Ward</th>
<th>Street Name</th>
<th>Poles</th>
<th>Statues</th>
<th>Street Cost</th>
</tr>
</thead>
<tbody id="streetTableBody">
<!-- Streets will be populated here -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- History Modal -->
<div class="modal fade" id="historyModal" tabindex="-1" aria-labelledby="historyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="historyModalLabel">Application History</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>S.No</th>
<th>Status</th>
<th>Remarks</th>
<th>Action Date</th>
<th>Updated By</th>
</tr>
</thead>
<tbody id="historyTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Documents Modal -->
<div class="modal fade" id="documentsModal" tabindex="-1" aria-labelledby="documentsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="documentsModalLabel">Uploaded Documents</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="documentsModalBody">
<!-- Images/Links will be populated here -->
</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;
let allRequests = []; // Store fetched data
const APPLICATION_TYPE_ID = 2; // 1 for Temporary, 2 for Permanent
function initDataTable() {
dataTable = $('#reportTable').DataTable({
dom: 'Bfrtip',
paging: false,
searching: false,
ordering: true,
info: false,
buttons: [
{ extend: 'excelHtml5', title: 'Permanent_Flagpole_Report', text: 'Download Excel', className: 'btn btn-success btn-sm' },
{ extend: 'pdfHtml5', title: 'Permanent_Flagpole_Report', text: 'Download PDF', className: 'btn btn-danger btn-sm', orientation: 'portrait', pageSize: 'A4' }
]
});
}
function loadReport(startDate = '', endDate = '') {
$('#pageLoader').show();
let url = '/commissionerreview/api/flagpoles/admin-reports';
if (startDate && endDate) {
url += `?startDate=${startDate}&endDate=${endDate}`;
}
fetch(url)
.then(res => res.json())
.then(json => {
$('#pageLoader').hide();
if (json.status === "success") {
if (json.count === 0) {
allRequests = [];
processAndPopulate([]);
return;
}
if (json.data) {
// Filter for Permanent Requests
allRequests = json.data.filter(req => req.application_type_id === APPLICATION_TYPE_ID);
processAndPopulate(allRequests);
}
} else {
alert("Error: " + json.message);
}
})
.catch(err => {
$('#pageLoader').hide();
console.error("Fetch Error:", err);
alert("Failed to load data");
});
}
function filterByDate() {
const fromDateStr = $('#fromDate').val();
const toDateStr = $('#toDate').val();
if (!fromDateStr || !toDateStr) {
alert("Please select both From and To dates");
return;
}
loadReport(fromDateStr, toDateStr);
$('.clickable-card').removeClass('card-active');
$('#card-all').addClass('card-active');
}
function resetDateFilter() {
$('#fromDate').val('');
$('#toDate').val('');
loadReport();
$('.clickable-card').removeClass('card-active');
$('#card-all').addClass('card-active');
}
function filterReport(type) {
$('.clickable-card').removeClass('card-active');
if (type === 'ALL') {
$('#card-all').addClass('card-active');
} else if (type === 'APPROVED') {
$('#card-approved').addClass('card-active');
} else if (type === 'REJECTED') {
$('#card-rejected').addClass('card-active');
}
let filtered = [];
if (type === 'ALL') {
filtered = allRequests;
} else if (type === 'APPROVED') {
filtered = allRequests.filter(req => determineStatus(req) === 'APPROVED');
} else if (type === 'REJECTED') {
filtered = allRequests.filter(req => determineStatus(req) === 'REJECTED');
}
processAndPopulate(filtered, false);
}
function processAndPopulate(requests, updateSummary = true) {
// Aggregate data by Zone
const zoneMap = new Map();
// Initialize for all 15 zones
for (let i = 1; i <= 15; i++) {
let zoneKey = i.toString().padStart(2, '0');
zoneMap.set(zoneKey, {
zone: zoneKey,
total_requests: 0,
approved_count: 0,
rejected_count: 0,
total_amount: 0,
requests: []
});
}
let totalReq = 0, totalApp = 0, totalRej = 0, totalAmt = 0;
requests.forEach(req => {
// Determine zone from streets array (taking first street's zone as primary)
let zone = "Unknown";
if (req.streets && req.streets.length > 0 && req.streets[0].zone) {
zone = req.streets[0].zone;
// Normalize to 2 digits string if numeric
if (/^\d+$/.test(zone)) {
zone = parseInt(zone).toString().padStart(2, '0');
}
}
if (!zoneMap.has(zone)) {
// Start with 0 values for unknown/new zones
zoneMap.set(zone, {
zone: zone,
total_requests: 0,
approved_count: 0,
rejected_count: 0,
total_amount: 0,
requests: []
});
}
let isApproved = false;
let isRejected = false;
let status = determineStatus(req);
if (status === 'APPROVED') isApproved = true;
if (status === 'REJECTED') isRejected = true;
let entry = zoneMap.get(zone);
entry.total_requests++;
entry.total_amount += (req.total_cost || 0);
entry.requests.push(req); // Store request for drilldown
if (isApproved) entry.approved_count++;
if (isRejected) entry.rejected_count++;
// Global totals
totalReq++;
totalAmt += (req.total_cost || 0);
if (isApproved) totalApp++;
if (isRejected) totalRej++;
});
if (updateSummary) {
// Update Summary Cards
$('#totalRequests').text(totalReq);
$('#totalApproved').text(totalApp);
$('#totalRejected').text(totalRej);
$('#totalAmount').text(totalAmt.toFixed(2));
}
// Populate Main Table
dataTable.clear();
// Convert map to array.
const reportData = Array.from(zoneMap.values());
// Optional: Sort by Zone ID if numeric
reportData.sort((a, b) => {
return (parseInt(a.zone) || 0) - (parseInt(b.zone) || 0);
});
reportData.forEach(row => {
dataTable.row.add([
row.zone,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Total')">${row.total_requests}</a>`,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Approved')">${row.approved_count}</a>`,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Rejected')">${row.rejected_count}</a>`,
row.total_amount.toFixed(2)
]);
});
dataTable.draw();
}
function determineStatus(req) {
// Logic to determine status from request object
// Check history for latest status
if (req.history && req.history.length > 0) {
const lastHistory = req.history[req.history.length - 1];
return lastHistory.status; // "APPROVED" / "REJECTED"
}
return "PENDING";
}
function viewDetails(zone, type) {
$('#detailsPageLoader').show();
const filtered = allRequests.filter(req => {
let reqZone = (req.streets && req.streets.length > 0) ? req.streets[0].zone : "Unknown";
// Normalize
if (/^\d+$/.test(reqZone)) {
reqZone = parseInt(reqZone).toString().padStart(2, '0');
}
if (reqZone !== zone) return false;
let status = determineStatus(req);
if (type === 'Approved' && status !== 'APPROVED') return false;
if (type === 'Rejected' && status !== 'REJECTED') return false;
return true;
});
$('#detailsModalLabel').text(`${type} Requests for Zone: ${zone}`);
// Populate Modal Table
const tbody = $('#detailsTableBody');
if ($.fn.DataTable.isDataTable('#detailsTable')) {
$('#detailsTable').DataTable().destroy();
}
tbody.empty();
filtered.forEach((row, index) => {
let status = determineStatus(row);
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_name)}</td>
<td>${formatValue(row.event_desc)}</td>
<td>${formatValue(row.event_date ? row.event_date.split('T')[0] : '')}</td>
<td>${formatValue(row.total_cost)}</td>
<td>${status}</td>
<td>
<button class="btn btn-secondary btn-sm" onclick="viewHistory('${row.refid}')">View History</button>
</td>
<td>
<button class="btn btn-primary btn-sm" onclick="viewDocuments('${row.refid}')">View Documents</button>
</td>
<td>
<button class="btn btn-info btn-sm" onclick="viewStreetDetails('${row.refid}')">
View Streets
</button>
</td>
</tr>
`);
});
new bootstrap.Modal(document.getElementById('detailsModal')).show();
var table = $('#detailsTable').DataTable({
dom: 'Bfrtip',
buttons: ['excelHtml5', 'pdfHtml5']
});
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
$('#detailsPageLoader').hide();
}
function viewStreetDetails(refId) {
const req = allRequests.find(r => r.refid == refId); // refid might be string/int
if (!req) return;
$('#streetModalLabel').text(`Street Details for Request: ${refId}`);
const tbody = $('#streetTableBody');
if ($.fn.DataTable.isDataTable('#streetTable')) {
$('#streetTable').DataTable().destroy();
}
tbody.empty();
if (req.streets && req.streets.length > 0) {
req.streets.forEach((row, index) => {
tbody.append(`
<tr>
<td>${index + 1}</td>
<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>
<td>${formatValue(row.street_cost)}</td>
</tr>
`);
});
} else {
tbody.html('<tr><td colspan="7" class="text-center">No street details found</td></tr>');
}
new bootstrap.Modal(document.getElementById('streetModal')).show();
var table = $('#streetTable').DataTable({
dom: 'Bfrtip',
buttons: ['excelHtml5', 'pdfHtml5']
});
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
}
function viewHistory(refId) {
const req = allRequests.find(r => r.refid == refId);
if (!req || !req.history) {
alert("No history found");
return;
}
$('#historyModalLabel').text(`History for Request: ${refId}`);
const tbody = $('#historyTableBody');
tbody.empty();
req.history.forEach((h, i) => {
tbody.append(`
<tr>
<td>${i + 1}</td>
<td>${h.status || '-'}</td>
<td>${h.remarks || '-'}</td>
<td>${h.action_date ? h.action_date.replace('T', ' ') : '-'}</td>
<td>${h.department_name || h.updated_by || '-'}</td>
</tr>
`);
});
new bootstrap.Modal(document.getElementById('historyModal')).show();
}
function viewDocuments(refId) {
const req = allRequests.find(r => r.refid == refId);
if (!req) return;
$('#documentsModalLabel').text(`Documents for Request: ${refId}`);
const body = $('#documentsModalBody');
body.empty();
let found = false;
// For Permanent, show all potential image fields
// Heuristic: key contains 'path', 'url', 'image', 'photo' and value looks like URL or path
Object.keys(req).forEach(key => {
const val = req[key];
// Filter for string values that look like file paths or params
if (val && typeof val === 'string' && (val.startsWith('http') || val.match(/\.(jpg|jpeg|png|pdf)$/i) || key.includes('path') || key.includes('url') || key.includes('image'))) {
// Skip some obviously non-image URL fields if any (like 'api_url' etc, but assume data fields)
// Also skip short strings that might be misidentified
if (val.length < 5) return;
found = true;
body.append(`
<div class="mb-3">
<h6>${key.replace(/_/g, ' ').toUpperCase()}</h6>
<a href="${val}" target="_blank" class="btn btn-outline-primary">View Document</a>
</div>
`);
}
});
if (!found) {
body.html('<p class="text-center text-muted">No documents found.</p>');
}
new bootstrap.Modal(document.getElementById('documentsModal')).show();
}
function formatValue(val) {
if (val === null || val === undefined || val === 'null' || val === '') {
return '-';
}
return val;
}
$(document).ready(function () {
initDataTable();
loadReport();
$('#detailsModal, #streetModal').on('shown.bs.modal', function () {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
});
</script>
</body>
</html>

676
src/main/resources/templates/modules/flagpoles/temporary_request.html

@ -0,0 +1,676 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Temporary Flagpole Request 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,
.table-dark th {
background-color: #003366 !important;
color: white;
text-align: center !important;
}
.page-header-title h4 {
margin: 20px 0;
text-align: center;
}
.clickable-card {
cursor: pointer;
transition: background-color 0.2s;
}
.clickable-card:hover {
background-color: #e2e6ea;
}
.card-active {
background-color: #e2e6ea;
border: 2px solid #0d6efd !important;
}
</style>
</head>
<body>
<div class="container mt-4">
<div class="card p-3 shadow-sm">
<div class="text-center mb-4">
<h4 class="fw-bold text-primary">Temporary Flagpole Request Report</h4>
<div class="d-flex justify-content-center align-items-center gap-2 mt-2">
<label class="fw-bold">Application Type:</label>
<select class="form-select form-select-sm" style="width: auto;"
onchange="if(this.value) window.location.href=this.value;">
<option value="/commissionerreview/flagpolereport" selected>Temporary</option>
<option value="/commissionerreview/flagpoles/permanent">Permanent</option>
</select>
</div>
</div>
<!-- Responsive Filter Section -->
<div class="row g-2 mb-3 justify-content-center justify-content-md-end align-items-end">
<div class="col-6 col-md-auto">
<label class="form-label fw-bold small">From Date</label>
<input type="date" id="fromDate" class="form-control form-control-sm">
</div>
<div class="col-6 col-md-auto">
<label class="form-label fw-bold small">To Date</label>
<input type="date" id="toDate" class="form-control form-control-sm">
</div>
<div class="col-12 col-md-auto">
<div class="d-flex gap-2">
<button class="btn btn-primary btn-sm flex-grow-1 flex-md-grow-0"
onclick="filterByDate()">Search</button>
<button class="btn btn-outline-secondary btn-sm flex-grow-1 flex-md-grow-0"
onclick="resetDateFilter()">Reset</button>
</div>
</div>
</div>
<!-- Summary Cards -->
<div class="card mb-3 shadow-sm border-primary">
<div class="card-body">
<div class="row text-center">
<div id="card-all" class="col-6 col-md-3 border-end clickable-card card-active"
onclick="filterReport('ALL')">
<h6 class="text-primary fw-bold">Total Requests</h6>
<h3 id="totalRequests">0</h3>
</div>
<div id="card-approved" class="col-6 col-md-3 border-end clickable-card"
onclick="filterReport('APPROVED')">
<h6 class="text-success fw-bold">Approved</h6>
<h3 id="totalApproved">0</h3>
</div>
<div id="card-rejected" class="col-6 col-md-3 border-end clickable-card"
onclick="filterReport('REJECTED')">
<h6 class="text-danger fw-bold">Rejected</h6>
<h3 id="totalRejected">0</h3>
</div>
<div class="col-6 col-md-3">
<h6 class="text-warning fw-bold">Total Amount Earned</h6>
<h3 id="totalAmount">0</h3>
</div>
</div>
</div>
</div>
<div class="table-responsive">
<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>
<th class="text-center">Total Amount</th>
</tr>
</thead>
<tbody id="reportTableBody">
<!-- Data will be populated here -->
</tbody>
</table>
</div>
</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 class="table-responsive">
<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>Application Type</th>
<th>Event Desc</th>
<th>Event Date</th>
<th>Total Cost</th>
<th>Status</th>
<th>History</th>
<th>Uploaded Images</th>
<th>Action</th>
</tr>
</thead>
<tbody id="detailsTableBody">
<!-- Details will be populated here -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- 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">
<div class="table-responsive">
<table id="streetTable" class="table table-bordered table-striped" width="100%">
<thead class="table-dark">
<tr>
<th>S.No</th>
<th>Zone</th>
<th>Ward</th>
<th>Street Name</th>
<th>Poles</th>
<th>Statues</th>
<th>Street Cost</th>
</tr>
</thead>
<tbody id="streetTableBody">
<!-- Streets will be populated here -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- History Modal -->
<div class="modal fade" id="historyModal" tabindex="-1" aria-labelledby="historyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="historyModalLabel">Application History</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>S.No</th>
<th>Status</th>
<th>Remarks</th>
<th>Action Date</th>
<th>Updated By</th>
</tr>
</thead>
<tbody id="historyTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Documents Modal -->
<div class="modal fade" id="documentsModal" tabindex="-1" aria-labelledby="documentsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="documentsModalLabel">Uploaded Documents</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="documentsModalBody">
<!-- Images/Links will be populated here -->
</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;
let allRequests = []; // Store fetched data
const APPLICATION_TYPE_ID = 1; // 1 for Temporary, 2 for Permanent
function initDataTable() {
dataTable = $('#reportTable').DataTable({
dom: 'Bfrtip',
paging: false,
searching: false,
ordering: true,
info: false,
buttons: [
{ extend: 'excelHtml5', title: 'Temporary_Flagpole_Report', text: 'Download Excel', className: 'btn btn-success btn-sm' },
{ extend: 'pdfHtml5', title: 'Temporary_Flagpole_Report', text: 'Download PDF', className: 'btn btn-danger btn-sm', orientation: 'portrait', pageSize: 'A4' }
]
});
}
function loadReport(startDate = '', endDate = '') {
$('#pageLoader').show();
let url = '/commissionerreview/api/flagpoles/admin-reports';
if (startDate && endDate) {
url += `?startDate=${startDate}&endDate=${endDate}`;
}
fetch(url)
.then(res => res.json())
.then(json => {
$('#pageLoader').hide();
if (json.status === "success") {
if (json.count === 0) {
allRequests = [];
processAndPopulate([]);
return;
}
if (json.data) {
// Filter for Temporary Requests
allRequests = json.data.filter(req => req.application_type_id === APPLICATION_TYPE_ID);
processAndPopulate(allRequests);
}
} else {
alert("Error: " + json.message);
}
})
.catch(err => {
$('#pageLoader').hide();
console.error("Fetch Error:", err);
alert("Failed to load data");
});
}
function filterByDate() {
const fromDateStr = $('#fromDate').val();
const toDateStr = $('#toDate').val();
if (!fromDateStr || !toDateStr) {
alert("Please select both From and To dates");
return;
}
loadReport(fromDateStr, toDateStr);
// Reset active status card to ALL
$('.clickable-card').removeClass('card-active');
$('#card-all').addClass('card-active');
}
function resetDateFilter() {
$('#fromDate').val('');
$('#toDate').val('');
loadReport();
$('.clickable-card').removeClass('card-active');
$('#card-all').addClass('card-active');
}
function filterReport(type) {
$('.clickable-card').removeClass('card-active');
if (type === 'ALL') {
$('#card-all').addClass('card-active');
} else if (type === 'APPROVED') {
$('#card-approved').addClass('card-active');
} else if (type === 'REJECTED') {
$('#card-rejected').addClass('card-active');
}
let filtered = [];
if (type === 'ALL') {
filtered = allRequests;
} else if (type === 'APPROVED') {
filtered = allRequests.filter(req => determineStatus(req) === 'APPROVED');
} else if (type === 'REJECTED') {
filtered = allRequests.filter(req => determineStatus(req) === 'REJECTED');
}
processAndPopulate(filtered, false);
}
function processAndPopulate(requests, updateSummary = true) {
// Aggregate data by Zone
const zoneMap = new Map();
// Initialize for all 15 zones
for (let i = 1; i <= 15; i++) {
let zoneKey = i.toString().padStart(2, '0');
zoneMap.set(zoneKey, {
zone: zoneKey,
total_requests: 0,
approved_count: 0,
rejected_count: 0,
total_amount: 0,
requests: []
});
}
let totalReq = 0, totalApp = 0, totalRej = 0, totalAmt = 0;
requests.forEach(req => {
// Determine zone from streets array (taking first street's zone as primary)
let zone = "Unknown";
if (req.streets && req.streets.length > 0 && req.streets[0].zone) {
zone = req.streets[0].zone;
// Normalize to 2 digits string if numeric
if (/^\d+$/.test(zone)) {
zone = parseInt(zone).toString().padStart(2, '0');
}
}
if (!zoneMap.has(zone)) {
// Start with 0 values for unknown/new zones
zoneMap.set(zone, {
zone: zone,
total_requests: 0,
approved_count: 0,
rejected_count: 0,
total_amount: 0,
requests: []
});
}
let isApproved = false;
let isRejected = false;
// Using a helper to determine status from request object
let status = determineStatus(req);
if (status === 'APPROVED') isApproved = true;
if (status === 'REJECTED') isRejected = true;
let entry = zoneMap.get(zone);
entry.total_requests++;
entry.total_amount += (req.total_cost || 0);
entry.requests.push(req); // Store request for drilldown
if (isApproved) entry.approved_count++;
if (isRejected) entry.rejected_count++;
// Global totals
totalReq++;
totalAmt += (req.total_cost || 0);
if (isApproved) totalApp++;
if (isRejected) totalRej++;
});
if (updateSummary) {
// Update Summary Cards
$('#totalRequests').text(totalReq);
$('#totalApproved').text(totalApp);
$('#totalRejected').text(totalRej);
$('#totalAmount').text(totalAmt.toFixed(2));
}
// Populate Main Table
dataTable.clear();
// Sort by Zone ? API does numeric zones usually.
// Convert map to array.
const reportData = Array.from(zoneMap.values());
// Optional: Sort by Zone ID if numeric
reportData.sort((a, b) => {
return (parseInt(a.zone) || 0) - (parseInt(b.zone) || 0);
});
reportData.forEach(row => {
dataTable.row.add([
row.zone,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Total')">${row.total_requests}</a>`,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Approved')">${row.approved_count}</a>`,
`<a href="javascript:void(0)" onclick="viewDetails('${row.zone}', 'Rejected')">${row.rejected_count}</a>`,
row.total_amount.toFixed(2)
]);
});
dataTable.draw();
}
function determineStatus(req) {
// Logic to determine status from request object
// Check history for latest status
if (req.history && req.history.length > 0) {
// Get latest history
// Assuming history is ordered? or sort by action_date if needed.
// Sample shows 14-02-2026 then 15-02-2026.
// Let's assume last in array is latest or check logic.
const lastHistory = req.history[req.history.length - 1];
return lastHistory.status; // "APPROVED" / "REJECTED"
}
return "PENDING";
}
function viewDetails(zone, type) {
$('#detailsPageLoader').show();
// Filter requests from allRequests or zoneMap
// I'll re-filter from allRequests to be safe or store in zoneMap.
// Using zoneMap data would be faster but I need to access it. AllRequests is global.
const filtered = allRequests.filter(req => {
let reqZone = (req.streets && req.streets.length > 0) ? req.streets[0].zone : "Unknown";
// Normalize
if (/^\d+$/.test(reqZone)) {
reqZone = parseInt(reqZone).toString().padStart(2, '0');
}
if (reqZone !== zone) return false;
let status = determineStatus(req);
if (type === 'Approved' && status !== 'APPROVED') return false;
if (type === 'Rejected' && status !== 'REJECTED') return false;
return true;
});
$('#detailsModalLabel').text(`${type} Requests for Zone: ${zone}`);
// Populate Modal Table
const tbody = $('#detailsTableBody');
if ($.fn.DataTable.isDataTable('#detailsTable')) {
$('#detailsTable').DataTable().destroy();
}
tbody.empty();
filtered.forEach((row, index) => {
let status = determineStatus(row);
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_name)}</td>
<td>${formatValue(row.event_desc)}</td>
<td>${formatValue(row.event_date ? row.event_date.split('T')[0] : '')}</td>
<td>${formatValue(row.total_cost)}</td>
<td>${status}</td>
<td>
<button class="btn btn-secondary btn-sm" onclick="viewHistory('${row.refid}')">View History</button>
</td>
<td>
<button class="btn btn-primary btn-sm" onclick="viewDocuments('${row.refid}')">View Documents</button>
</td>
<td>
<button class="btn btn-info btn-sm" onclick="viewStreetDetails('${row.refid}')">
View Streets
</button>
</td>
</tr>
`);
});
new bootstrap.Modal(document.getElementById('detailsModal')).show();
var table = $('#detailsTable').DataTable({
dom: 'Bfrtip',
buttons: ['excelHtml5', 'pdfHtml5']
});
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
$('#detailsPageLoader').hide();
}
function viewStreetDetails(refId) {
const req = allRequests.find(r => r.refid == refId); // refid might be string/int
if (!req) return;
$('#streetModalLabel').text(`Street Details for Request: ${refId}`);
const tbody = $('#streetTableBody');
if ($.fn.DataTable.isDataTable('#streetTable')) {
$('#streetTable').DataTable().destroy();
}
tbody.empty();
if (req.streets && req.streets.length > 0) {
req.streets.forEach((row, index) => {
tbody.append(`
<tr>
<td>${index + 1}</td>
<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>
<td>${formatValue(row.street_cost)}</td>
</tr>
`);
});
} else {
tbody.html('<tr><td colspan="7" class="text-center">No street details found</td></tr>');
}
new bootstrap.Modal(document.getElementById('streetModal')).show();
var table = $('#streetTable').DataTable({
dom: 'Bfrtip',
buttons: ['excelHtml5', 'pdfHtml5']
});
setTimeout(function () {
table.columns.adjust().draw();
}, 200);
}
function viewHistory(refId) {
const req = allRequests.find(r => r.refid == refId);
if (!req || !req.history) {
alert("No history found");
return;
}
$('#historyModalLabel').text(`History for Request: ${refId}`);
const tbody = $('#historyTableBody');
tbody.empty();
req.history.forEach((h, i) => {
tbody.append(`
<tr>
<td>${i + 1}</td>
<td>${h.status || '-'}</td>
<td>${h.remarks || '-'}</td>
<td>${h.action_date ? h.action_date.replace('T', ' ') : '-'}</td>
<td>${h.department_name || h.updated_by || '-'}</td>
</tr>
`);
});
new bootstrap.Modal(document.getElementById('historyModal')).show();
}
function viewDocuments(refId) {
const req = allRequests.find(r => r.refid == refId);
if (!req) return;
$('#documentsModalLabel').text(`Documents for Request: ${refId}`);
const body = $('#documentsModalBody');
body.empty();
let found = false;
// For Temporary, look for keys containing 'aadhaar'
Object.keys(req).forEach(key => {
if (key.toLowerCase().includes('aadhaar')) {
const val = req[key];
if (val && (val.startsWith('http') || val.match(/\.(jpg|jpeg|png|pdf)$/i))) {
found = true;
body.append(`
<div class="mb-3">
<h6>${key.replace(/_/g, ' ').toUpperCase()}</h6>
<a href="${val}" target="_blank" class="btn btn-outline-primary">View Document</a>
</div>
`);
}
}
});
if (!found) {
body.html('<p class="text-center text-muted">No Aadhaar documents found.</p>');
}
new bootstrap.Modal(document.getElementById('documentsModal')).show();
}
function formatValue(val) {
if (val === null || val === undefined || val === 'null' || val === '') {
return '-';
}
return val;
}
$(document).ready(function () {
initDataTable();
loadReport();
$('#detailsModal, #streetModal').on('shown.bs.modal', function () {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
});
</script>
</body>
</html>
Loading…
Cancel
Save