Browse Source

added greencommittee controller,service and attendance controller ,service and html pages

master
yoganandhan 8 months ago
parent
commit
6407670ddd
  1. 29
      src/main/java/com/gcc/commissionerreview/configuration/DataSourceConfig.java
  2. 71
      src/main/java/com/gcc/commissionerreview/controller/AttendanceController.java
  3. 116
      src/main/java/com/gcc/commissionerreview/controller/GreenCommitteeApicontroller.java
  4. 136
      src/main/java/com/gcc/commissionerreview/service/AttendanceService.java
  5. 392
      src/main/java/com/gcc/commissionerreview/service/GreenCommitteeService.java
  6. 407
      src/main/resources/templates/modules/attendance-dashboard.html
  7. 737
      src/main/resources/templates/modules/greencommittee/gcreport.html

29
src/main/java/com/gcc/commissionerreview/configuration/DataSourceConfig.java

@ -124,7 +124,34 @@ public class DataSourceConfig {
public DataSource mysqlFlagPoleManagerSystemDataSource() { public DataSource mysqlFlagPoleManagerSystemDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource(); DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://" + host + "/gcc_flag_pole07022026");
dataSource.setUrl("jdbc:mysql://" + host + "/gcc_flag_pole");
dataSource.setUsername("root");
dataSource.setPassword(dbpassword);
return dataSource;
}
////////////////////////////// NULM Attendance ////////////////////////
@Bean(name = "mysqlNULMDataSource")
public DataSource mysqlNULMDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + host + "/gcc_nulm");
config.setUsername("root");
config.setPassword(dbpassword);
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setAutoCommit(true);
return new HikariDataSource(config);
}
////////////////////////////// Green Committee /////////////////
@Bean(name = "mysqlGreenCommitteeDataSource")
public DataSource mysqlGreenCommitteeDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://" + host + "/green_committee");
dataSource.setUsername("root"); dataSource.setUsername("root");
dataSource.setPassword(dbpassword); dataSource.setPassword(dbpassword);
return dataSource; return dataSource;

71
src/main/java/com/gcc/commissionerreview/controller/AttendanceController.java

@ -0,0 +1,71 @@
package com.gcc.commissionerreview.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
import com.gcc.commissionerreview.service.AttendanceService;
@RestController
@RequestMapping("/commissionerreview/api/attendanceController")
public class AttendanceController {
@Autowired
AttendanceService attendanceService;
@GetMapping("/abstractCounts")
public List<Map<String, Object>> getAbstractCounts() {
return attendanceService.getAbstractCounts();
}
@GetMapping("/zoneSummary")
public List<Map<String, Object>> getZoneSummary(@RequestParam(name = "empType") String empType,
@RequestParam(name = "fromDate", required = false) String fromDate,
@RequestParam(name = "toDate", required = false) String toDate) {
List<Map<String, Object>> result = attendanceService.getZoneSummary(empType, fromDate, toDate);
System.out.println("Zone Summary Result for "+empType+ ":"+ result);
return result;
// return attendanceService.getZoneSummary(empType, fromDate, toDate);
}
@GetMapping("/zoneSummaryPark")
public List<Map<String, Object>> getZoneSummaryPark(
@RequestParam(name = "fromDate", required = false) String fromDate,
@RequestParam(name = "toDate", required = false) String toDate) {
List<Map<String, Object>> result = attendanceService.getZoneSummary("Park", fromDate, toDate);
System.out.println("Zone Summary Result for Park: " + result);
return result;
}
@GetMapping("/zoneSummaryNULM")
public List<Map<String, Object>> getZoneSummaryNULM(
@RequestParam(name = "fromDate", required = false) String fromDate,
@RequestParam(name = "toDate", required = false) String toDate) {
List<Map<String, Object>> result = attendanceService.getZoneSummary("NULM", fromDate, toDate);
System.out.println("Zone Summary Result for NULM: " + result);
return result;
}
@GetMapping("/detailed")
public List<Map<String, Object>> getDetailed(@RequestParam(name = "empType") String empType,
@RequestParam(name = "category") String category,
@RequestParam(name = "fromDate", required = false) String fromDate,
@RequestParam(name = "toDate", required = false) String toDate,
@RequestParam(name = "zone", required = false) Integer zone) {
List<Map<String, Object>> result = attendanceService.getDetailedList(empType, category, fromDate, toDate, zone);
System.out.println("Detailed Result: " + result);
return result;
// return attendanceService.getDetailedList(empType, category, fromDate, toDate, zone);
}
}

116
src/main/java/com/gcc/commissionerreview/controller/GreenCommitteeApicontroller.java

@ -0,0 +1,116 @@
package com.gcc.commissionerreview.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.gcc.commissionerreview.service.GreenCommitteeService;
@RestController
@RequestMapping("/commissionerreview/api/greencommittee")
public class GreenCommitteeApicontroller {
@Autowired
private GreenCommitteeService gcservice;
@GetMapping("/getZoneReport")
public Map<String, Object> getabscount(@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate)
{
Map<String, Object> response = new HashMap<>();
try {
List<Map<String, Object>> data = gcservice.getabscount(startDate, endDate);
response.put("data", data);
response.put("message", "Zone-wise abstract report");
response.put("status", "Success");
} catch (Exception e) {
e.printStackTrace();
response.put("data", Collections.emptyList());
response.put("message", "Failed to fetch abstract report");
response.put("status", "Error");
}
return response;
}
@GetMapping("/getWardDetailsByZone")
public Map<String, Object> getWardDetailsByZone(@RequestParam(required = true) String zone,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate)
{
Map<String, Object> response = new HashMap<>();
try {
List<Map<String, Object>> data = gcservice.getWardDetailsByZone(zone,startDate,endDate);
response.put("data", data);
response.put("message", "Ward-wise abstract report");
response.put("status", "Success");
} catch (Exception e) {
e.printStackTrace();
response.put("data", Collections.emptyList());
response.put("message", "Failed to fetch abstract report");
response.put("status", "Error");
}
return response;
}
@GetMapping("/getPendingDetails")
public Map<String, Object> getPendingDetails(
@RequestParam String zone,
@RequestParam(required = false) String ward,
@RequestParam String type,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
Map<String, Object> response = new HashMap<>();
try {
List<String> refIds =gcservice.getPendingRefIds(zone, ward, type,startDate, endDate);
List<Map<String, Object>> dataList = new ArrayList<>();
for (String refId : refIds) {
Map<String, Object> fullData = gcservice.getfulldetailsdata(refId);
if (fullData != null && !fullData.isEmpty()) {
dataList.add((Map<String, Object>) fullData.get("data"));
}
}
response.put("data", dataList);
response.put("message", "Complaint List");
response.put("status", "Success");
} catch (Exception e) {
e.printStackTrace();
response.put("data", Collections.emptyList());
response.put("message", "Failed to fetch complaints");
response.put("status", "Error");
}
return response;
}
}

136
src/main/java/com/gcc/commissionerreview/service/AttendanceService.java

@ -0,0 +1,136 @@
package com.gcc.commissionerreview.service;
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;
import java.sql.Date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@Service
public class AttendanceService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
public void setFeedbackDataSource(@Qualifier("mysqlNULMDataSource") DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final DateTimeFormatter INPUT_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy", Locale.ENGLISH);
public List<Map<String, Object>> getAbstractCounts() {
String sql = "SELECT emp_type, count(enrollment_id) as totalCount FROM enrollment_table "
+ "where isactive =1 and isdelete = 0 and zone != 0 group by emp_type";
return jdbcTemplate.queryForList(sql);
}
public List<Map<String, Object>> getZoneSummary(String empType, String fromDateStr, String toDateStr) {
LocalDate fromDate;
LocalDate toDate;
if (fromDateStr == null || fromDateStr.trim().isEmpty()) {
fromDate = LocalDate.now();
} else {
fromDate = LocalDate.parse(fromDateStr, INPUT_FORMAT);
}
if (toDateStr == null || toDateStr.trim().isEmpty()) {
toDate = LocalDate.now();
} else {
toDate = LocalDate.parse(toDateStr, INPUT_FORMAT);
}
// Convert to datetime range (start of fromDate to end of toDate)
String fromDateTime = fromDate + " 00:00:00";
String toDateTime = toDate + " 23:59:59";
System.out.println("EmpType: " + empType + ", From: " + fromDateTime + ", To: " + toDateTime);
String sql = "SELECT et.zone, count(distinct et.enrollment_id) as TotalCount, "
+ "count(distinct case when att.indatetime >= ? and att.indatetime <= ? then att.enrollment_id end) as AttendanceMarked, "
+ "count(distinct case when att.oddatetime >= ? and att.oddatetime <= ? then att.enrollment_id end) as ODCount, "
+ "count(distinct case when att.leavedatetime >= ? and att.leavedatetime <= ? then att.enrollment_id end) as LeaveCount "
+ "FROM enrollment_table et "
+ "left join attendance att on et.enrollment_id = att.enrollment_id "
+ "where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 "
+ "group by et.zone order by et.zone";
return jdbcTemplate.queryForList(sql, new Object[] { fromDateTime, toDateTime, fromDateTime, toDateTime, fromDateTime, toDateTime, empType });
}
public List<Map<String, Object>> getDetailedList(String empType, String category, String fromDateStr, String toDateStr, Integer zone) {
LocalDate fromDate;
LocalDate toDate;
if (fromDateStr == null || fromDateStr.trim().isEmpty()) {
fromDate = LocalDate.now();
} else {
fromDate = LocalDate.parse(fromDateStr, INPUT_FORMAT);
}
if (toDateStr == null || toDateStr.trim().isEmpty()) {
toDate = LocalDate.now();
} else {
toDate = LocalDate.parse(toDateStr, INPUT_FORMAT);
}
// Convert to datetime range
String fromDateTime = fromDate + " 00:00:00";
String toDateTime = toDate + " 23:59:59";
// determine which datetime column to filter/order by
String dateColumn;
String filterDateColumn;
switch (category == null ? "present" : category.toLowerCase()) {
case "od":
dateColumn = "att.oddatetime";
filterDateColumn = "att.oddatetime";
break;
case "leave":
dateColumn = "att.leavedatetime";
filterDateColumn = "att.leavedatetime";
break;
default:
dateColumn = "att.indatetime";
filterDateColumn = "att.indatetime";
}
// Base query - simplified and optimized
String sql = "SELECT et.enrollment_id, et.name, et.designation, et.department, et.incharge_name, et.incharge_phoneno, et.emp_id, "
+ "DATE_FORMAT(att.indatetime, '%d-%m-%Y %h:%i %p') as intime, att.inphoto as inphoto, att.inby, "
+ "DATE_FORMAT(att.outdatetime, '%d-%m-%Y %h:%i %p') as outtime, att.outphoto as outphoto, att.outby, "
+ "DATE_FORMAT(att.oddatetime, '%d-%m-%Y %h:%i %p') as odtime, att.odby, et.zone, "
+ "DATE_FORMAT(att.leavedatetime, '%d-%m-%Y %h:%i %p') as leavedatetime, att.leaveby "
+ "FROM enrollment_table et "
+ "inner join attendance att on et.enrollment_id = att.enrollment_id "
+ " and " + filterDateColumn + " >= ? and " + filterDateColumn + " <= ? "
+ "where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 "
+ " and " + filterDateColumn + " is not null ";
java.util.List<Object> params = new java.util.ArrayList<>();
params.add(fromDateTime);
params.add(toDateTime);
params.add(empType);
if (zone != null) {
sql += " and et.zone = ? ";
params.add(zone);
}
sql += " order by " + dateColumn + " desc ";
System.out.println("EmpType: " + empType + ", Category: " + category + ", From: " + fromDateTime + ", To: " + toDateTime + ", Zone: " + zone);
return jdbcTemplate.queryForList(sql, params.toArray());
}
}

392
src/main/java/com/gcc/commissionerreview/service/GreenCommitteeService.java

@ -0,0 +1,392 @@
package com.gcc.commissionerreview.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import com.gcc.commissionerreview.configuration.AppConfig;
@Service
public class GreenCommitteeService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private Environment environment;
@Autowired
private AppConfig appConfig;
@Autowired
@Value("${fileBaseUrl}")
private String fileBaseUrl;
public GreenCommitteeService (Environment environment) {
this.environment = environment;
this.fileBaseUrl = environment.getProperty("fileBaseUrl");
}
@Autowired
public void setFeedbackDataSource(@Qualifier("mysqlGreenCommitteeDataSource") DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getabscount(String startDate, String endDate) {
String baseSql =
"SELECT " +
" zm.zone_name, " +
" COALESCE(COUNT(DISTINCT rd.ref_id), 0) AS total_requests, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='NGO' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id=id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS ngo_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='GCC' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id=id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS gcc_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='TNFD' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id=id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS tnfd_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND ( " +
" SELECT COUNT(DISTINCT inspection_by) " +
" FROM inspection_data id " +
" WHERE id.ref_id = rd.ref_id AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id=id.reinspection_id) ) " +
" ) = 3 " +
" THEN 1 ELSE 0 END), 0) AS committee_pending, " +
" COALESCE(SUM(CASE WHEN rd.is_withdraw = 1 THEN 1 ELSE 0 END), 0) AS withdrawn, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" THEN 1 ELSE 0 END), 0) AS completed " +
"FROM zone_master zm " +
"LEFT JOIN reg_details rd " +
" ON rd.zone = zm.zone_name " +
" AND rd.is_active = 1 ";
StringBuilder sql = new StringBuilder(baseSql);
List<Object> params = new ArrayList<>();
if (startDate != null && !startDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) >= ? ");
params.add(startDate);
}
if (endDate != null && !endDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) <= ? ");
params.add(endDate);
}
sql.append(
" WHERE zm.is_active = 1 " +
" AND zm.is_delete = 0 " +
" GROUP BY zm.zone_name " +
" ORDER BY zm.zone_name "
);
return jdbcTemplate.queryForList(sql.toString(), params.toArray());
}
public List<Map<String, Object>> getWardDetailsByZone(
String zone,
String startDate,
String endDate) {
String baseSql =
"SELECT " +
" wm.ward_name, " +
" COALESCE(COUNT(DISTINCT rd.ref_id), 0) AS total_requests, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='NGO' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS ngo_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='GCC' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS gcc_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='TNFD' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
" ) " +
" THEN 1 ELSE 0 END), 0) AS tnfd_pending, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IS NOT NULL " +
" AND rd.is_withdraw = 0 " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
" AND ( " +
" SELECT COUNT(DISTINCT inspection_by) " +
" FROM inspection_data id " +
" WHERE id.ref_id = rd.ref_id AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
" ) = 3 " +
" THEN 1 ELSE 0 END), 0) AS committee_pending, " +
" COALESCE(SUM(CASE WHEN rd.is_withdraw=1 THEN 1 ELSE 0 END), 0) AS withdrawn, " +
" COALESCE(SUM(CASE " +
" WHEN rd.ref_id IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
" THEN 1 ELSE 0 END), 0) AS completed " +
"FROM ward_master wm " +
"JOIN zone_master zm ON zm.id = wm.zone_id AND zm.zone_name = ? " +
"LEFT JOIN reg_details rd " +
" ON rd.ward = wm.ward_name " +
" AND rd.is_active = 1 ";
StringBuilder sql = new StringBuilder(baseSql);
List<Object> params = new ArrayList<>();
params.add(zone);
if (startDate != null && !startDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) >= ? ");
params.add(startDate);
}
if (endDate != null && !endDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) <= ? ");
params.add(endDate);
}
sql.append(
" WHERE wm.is_active=1 AND wm.is_delete=0 " +
" GROUP BY wm.zone_id, wm.ward_name " +
" ORDER BY wm.ward_name "
);
return jdbcTemplate.queryForList(sql.toString(), params.toArray());
}
public List<String> getPendingRefIds(
String zone,
String ward,
String type,
String startDate,
String endDate) {
String baseSql;
switch (type.toUpperCase()) {
case "NGO":
baseSql =
"SELECT rd.ref_id FROM reg_details rd " +
"WHERE rd.zone=? " +
"AND rd.is_active=1 AND rd.is_withdraw=0 " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
"AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='NGO' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
")";
break;
case "GCC":
baseSql =
"SELECT rd.ref_id FROM reg_details rd " +
"WHERE rd.zone=? " +
"AND rd.is_active=1 AND rd.is_withdraw=0 " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
"AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='GCC' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
")";
break;
case "TNFD":
baseSql =
"SELECT rd.ref_id FROM reg_details rd " +
"WHERE rd.zone=? " +
"AND rd.is_active=1 AND rd.is_withdraw=0 " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM committee_data WHERE isactive=1) " +
"AND rd.ref_id NOT IN (SELECT ref_id FROM reinspection WHERE isactive=1) " +
"AND rd.ref_id NOT IN ( " +
" SELECT id.ref_id FROM inspection_data id " +
" WHERE id.inspection_by='TNFD' AND id.isactive=1 " +
" AND ( (rd.reinspection_id IS NULL AND id.reinspection_id IS NULL) " +
" OR (rd.reinspection_id = id.reinspection_id) ) " +
")";
break;
case "WITHDRAW":
baseSql =
"SELECT rd.ref_id FROM reg_details rd " +
"WHERE rd.zone=? AND rd.is_withdraw=1";
break;
default:
return Collections.emptyList();
}
StringBuilder sql = new StringBuilder(baseSql);
List<Object> params = new ArrayList<>();
params.add(zone);
if (ward != null && !ward.isEmpty()) {
sql.append(" AND rd.ward = ? ");
params.add(ward);
}
if (startDate != null && !startDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) >= ? ");
params.add(startDate);
}
if (endDate != null && !endDate.isEmpty()) {
sql.append(" AND DATE(rd.cdate) <= ? ");
params.add(endDate);
}
return jdbcTemplate.query(
sql.toString(),
params.toArray(),
(rs, rowNum) -> rs.getString("ref_id")
);
}
public Map<String, Object> getfulldetailsdata(String refId) {
Map<String, Object> response = new HashMap<>();
// 1 — Application details
Map<String, Object> app = jdbcTemplate.queryForMap(
"SELECT *,DATE_FORMAT(cdate, '%d-%m-%Y %l:%i %p') as r_Cdate,IFNULL(reinspection_id, '') AS com_reinspection_id FROM reg_details WHERE ref_id=? AND is_active=1",
refId
);
// 2 — Complaint details (grouped)
List<Map<String, Object>> complaints = jdbcTemplate.queryForList(
"SELECT cn.name AS nature, SUM(cd.no_of_trees) AS count " +
"FROM complaint_details cd JOIN comp_nature cn ON cn.id=cd.comp_nature_id " +
"WHERE cd.ref_id=? GROUP BY cn.name", refId
);
String compDetails = complaints.stream()
.map(c -> c.get("nature") + "-" + c.get("count"))
.collect(Collectors.joining(","));
// 3 — Complaint images
List<Map<String, Object>> imagePaths =
jdbcTemplate.queryForList(
"SELECT img_path FROM img_uploads WHERE ref_id=? AND is_active=1",
new Object[]{refId}
).stream()
.map(row -> {
Map<String, Object> img = new HashMap<>();
img.put(
"img_path",
fileBaseUrl + "/gccofficialapp/files" + row.get("img_path").toString()
);
return img;
})
.collect(Collectors.toList());
app.put("complaints", complaints);
app.put("comp_details", compDetails);
app.put("image_paths", imagePaths);
response.put("data", app);
return response;
}
}

407
src/main/resources/templates/modules/attendance-dashboard.html

@ -0,0 +1,407 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Attendance Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.spinner-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
}
.spinner-border {
width: 3rem;
height: 3rem;
color: #007bff;
}
.zone-summary-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.zone-summary-row {
grid-template-columns: 1fr;
}
}
.zone-summary-card {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
background-color: #f9f9f9;
}
.disabled-count {
color: #6c757d;
cursor: default;
text-decoration: none;
display: inline-block;
padding: 0 4px;
}
</style>
</head>
<body class="p-3">
<div class="container">
<h1 class="mb-4">Attendance Dashboard</h1>
<div class="row mb-3">
<div class="col-md-3">
<label class="form-label">From</label>
<input id="fromDate" type="date" class="form-control" />
</div>
<div class="col-md-3">
<label class="form-label">To</label>
<input id="toDate" type="date" class="form-control" />
</div>
<div class="col-md-6 d-flex align-items-end">
<div>
<button id="loadSummaryBtn" class="btn btn-secondary">Load</button>
</div>
</div>
</div>
<h4>Abstract Counts</h4>
<div id="abstractCounts" class="mb-4"></div>
<h4>Zone Summary</h4>
<div class="zone-summary-row">
<div class="zone-summary-card">
<h5>Park</h5>
<div id="zoneSummaryPark"></div>
</div>
<div class="zone-summary-card">
<h5>NULM</h5>
<div id="zoneSummaryNulm"></div>
</div>
</div>
</div>
<script>
(function () {
const apiBase = '/commissionerreview/api/attendanceController';
function pad(n) { return n < 10 ? '0' + n : n; }
// convert Date -> yyyy-mm-dd for input[type=date]
function toIsoYyyyMmDd(date) {
return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate());
}
// convert input value (either yyyy-mm-dd or dd-mm-yyyy) -> dd-mm-yyyy for API
function toApiDdMmYyyy(val) {
if (!val) return val;
// if already in dd-mm-yyyy (contains two '-') and first part length 2
const parts = val.split('-');
if (parts.length === 3) {
if (parts[0].length === 4) {
// yyyy-mm-dd -> dd-mm-yyyy
return pad(parseInt(parts[2], 10)) + '-' + pad(parseInt(parts[1], 10)) + '-' + parts[0];
} else {
// assume already dd-mm-yyyy
return val;
}
}
return val;
}
function setDefaults() {
const today = new Date();
const iso = toIsoYyyyMmDd(today);
document.getElementById('fromDate').value = iso;
document.getElementById('toDate').value = iso;
}
function setDateLimits() {
const from = document.getElementById('fromDate');
const to = document.getElementById('toDate');
const todayIso = toIsoYyyyMmDd(new Date());
from.max = todayIso;
to.max = todayIso;
if (from.value) to.min = from.value;
from.addEventListener('change', () => {
const fv = from.value;
if (fv) {
to.min = fv;
if (to.value && to.value < fv) to.value = fv;
} else {
to.min = '';
}
});
to.addEventListener('change', () => {
if (from.value && to.value && to.value < from.value) {
to.value = from.value;
}
});
}
async function loadAbstractCounts() {
const target = document.getElementById('abstractCounts');
target.innerHTML = createLoader();
try {
const res = await fetch(apiBase + '/abstractCounts');
const data = await res.json();
if (!Array.isArray(data)) { target.innerText = JSON.stringify(data); return; }
let html = '<div class="row">';
data.forEach(item => {
html += `<div class="col-md-3"><div class="card p-2 mb-2"><strong>${item.emp_type}</strong><div>${item.totalCount}</div></div></div>`;
});
html += '</div>';
target.innerHTML = html;
} catch (e) { target.innerHTML = `<div class="alert alert-danger">Error: ${e}</div>`; }
}
function createLoader() {
return '<div class="spinner-container"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
}
function renderTable(target, data, empType) {
if (!Array.isArray(data)) { target.innerText = JSON.stringify(data); return; }
if (data.length === 0) { target.innerHTML = '<div class="alert alert-info">No records</div>'; return; }
const rows = data.slice().sort((a,b) => String(a.zone||'').localeCompare(String(b.zone||''), undefined, {numeric: true}));
let html = '<table class="table table-sm table-striped"><thead><tr><th>Zone</th><th>Total</th><th>Present</th><th>OD</th><th>Leave</th></tr></thead><tbody>';
rows.forEach(r => {
const zone = r.zone;
const total = Number(r.TotalCount) || 0;
const present = Number(r.AttendanceMarked) || 0;
const od = Number(r.ODCount) || 0;
const leave = Number(r.LeaveCount) || 0;
html += `<tr><td>${zone}</td>`;
html += `<td>${total}</td>`;
html += `<td>` + (present > 0
? `<a href="#" class="zone-count" data-zone="${zone}" data-emptype="${empType}" data-category="present">${present}</a>`
: `<span class="disabled-count">${present}</span>`) + `</td>`;
html += `<td>` + (od > 0
? `<a href="#" class="zone-count" data-zone="${zone}" data-emptype="${empType}" data-category="od">${od}</a>`
: `<span class="disabled-count">${od}</span>`) + `</td>`;
html += `<td>` + (leave > 0
? `<a href="#" class="zone-count" data-zone="${zone}" data-emptype="${empType}" data-category="leave">${leave}</a>`
: `<span class="disabled-count">${leave}</span>`) + `</td>`;
html += '</tr>';
});
html += '</tbody></table>';
target.innerHTML = html;
}
async function loadZoneSummary() {
const rawFrom = document.getElementById('fromDate').value;
const rawTo = document.getElementById('toDate').value;
const fromDate = toApiDdMmYyyy(rawFrom);
const toDate = toApiDdMmYyyy(rawTo);
const parkTarget = document.getElementById('zoneSummaryPark');
const nulmTarget = document.getElementById('zoneSummaryNulm');
// Show loaders
parkTarget.innerHTML = createLoader();
nulmTarget.innerHTML = createLoader();
try {
// Load Park independently
fetch(`${apiBase}/zoneSummaryPark?fromDate=${encodeURIComponent(fromDate)}&toDate=${encodeURIComponent(toDate)}`)
.then(res => {
if (!res.ok) {
console.error('Park zoneSummary error', res.status);
parkTarget.innerHTML = `<div class="alert alert-danger">Error loading Park summary (${res.status})</div>`;
return Promise.reject(new Error('Park fetch failed'));
}
return res.json();
})
.then(parkData => {
console.log('Park data loaded:', parkData);
renderTable(parkTarget, parkData, 'Park');
})
.catch(e => {
console.error('Park error:', e);
parkTarget.innerHTML = `<div class="alert alert-danger">Error: ${e.message}</div>`;
});
// Load NULM independently
fetch(`${apiBase}/zoneSummaryNULM?fromDate=${encodeURIComponent(fromDate)}&toDate=${encodeURIComponent(toDate)}`)
.then(res => {
if (!res.ok) {
console.error('NULM zoneSummary error', res.status);
nulmTarget.innerHTML = `<div class="alert alert-danger">Error loading NULM summary (${res.status})</div>`;
return Promise.reject(new Error('NULM fetch failed'));
}
return res.json();
})
.then(nulmData => {
console.log('NULM data loaded:', nulmData);
renderTable(nulmTarget, nulmData, 'NULM');
})
.catch(e => {
console.error('NULM error:', e);
nulmTarget.innerHTML = `<div class="alert alert-danger">Error: ${e.message}</div>`;
});
} catch (e) {
parkTarget.innerHTML = `<div class="alert alert-danger">Error: ${e}</div>`;
nulmTarget.innerHTML = `<div class="alert alert-danger">Error: ${e}</div>`;
}
}
// loadDetailed may be called with explicit empType/category/zone (when clicking a zone count)
async function loadDetailed(empType, category, zoneVal) {
// show details in modal
const rawFrom = document.getElementById('fromDate').value;
const rawTo = document.getElementById('toDate').value;
const fromDate = toApiDdMmYyyy(rawFrom);
const toDate = toApiDdMmYyyy(rawTo);
const modalEl = document.getElementById('detailModal');
const modalBody = document.getElementById('detailModalBody');
const modalTitle = document.getElementById('detailModalLabel');
if (!empType || !category || !zoneVal) {
modalTitle.innerText = 'Details';
modalBody.innerHTML = '<div class="text-warning">Missing parameters. Click a count to view details.</div>';
new bootstrap.Modal(modalEl).show();
return;
}
modalTitle.innerText = `${empType} — Zone ${zoneVal} — ${category.toUpperCase()} (${rawFrom} → ${rawTo})`;
modalBody.innerHTML = createLoader();
// Show modal immediately with loader
new bootstrap.Modal(modalEl).show();
try {
let url = `${apiBase}/detailed?empType=${encodeURIComponent(empType)}&category=${encodeURIComponent(category)}&fromDate=${encodeURIComponent(fromDate)}&toDate=${encodeURIComponent(toDate)}`;
url += `&zone=${encodeURIComponent(zoneVal)}`;
const res = await fetch(url);
if (!res.ok) {
const text = await res.text().catch(() => '');
modalBody.innerHTML = `<div class="alert alert-danger">Error ${res.status}: ${text}</div>`;
return;
}
const data = await res.json();
if (!Array.isArray(data)) { modalBody.innerText = JSON.stringify(data); return; }
if (data.length === 0) { modalBody.innerHTML = '<div class="alert alert-info">No records</div>'; return; }
// Define columns to display based on category
let displayColumns = [];
const catLower = (category || 'present').toLowerCase();
if (catLower === 'present') {
displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'intime', 'inphoto', 'outtime', 'outphoto'];
} else if (catLower === 'leave') {
displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'leavedatetime', 'leaveby'];
} else if (catLower === 'od') {
displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'odtime', 'odby'];
} else {
displayColumns = Object.keys(data[0]);
}
// Filter data to include only displayColumns, handle case-insensitive matching
const filteredData = data.map(row => {
const filtered = {};
displayColumns.forEach(col => {
// Try exact match first, then case-insensitive
const key = Object.keys(row).find(k => k === col || k.toLowerCase() === col.toLowerCase());
if (key) {
filtered[col] = row[key];
}
});
return filtered;
});
function prettyName(key) {
const map = {
enrollment_id: 'Enrollment ID',
name: 'Name',
designation: 'Designation',
department: 'Department',
intime: 'In Time',
inphoto: 'In Photo',
outtime: 'Out Time',
outphoto: 'Out Photo',
leavedatetime: 'Leave DateTime',
leaveby: 'Leave By',
odtime: 'OD DateTime',
odby: 'OD By',
};
if (map[key]) return map[key];
return key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
}
let html = '<div class="table-responsive"><table class="table table-sm table-bordered"><thead><tr>';
// add serial number header
html += '<th>S.No</th>';
displayColumns.forEach(k => {
html += `<th>${prettyName(k)}</th>`;
});
html += '</tr></thead><tbody>';
filteredData.forEach((row, idx) => {
html += '<tr>';
// serial number cell
html += `<td>${idx + 1}</td>`;
displayColumns.forEach(k => {
const val = row[k];
// Handle photo columns - show as thumbnail if it's an image
if ((k === 'inphoto' || k === 'outphoto') && val) {
html += `<td><img src="${val}" alt="photo" style="max-width: 100px; max-height: 100px; cursor: pointer;" onclick="this.style.maxWidth='500px'; this.style.maxHeight='500px';"></td>`;
} else {
html += `<td>${val != null ? val : '-'}</td>`;
}
});
html += '</tr>';
});
html += '</tbody></table></div>';
modalBody.innerHTML = html;
} catch (e) {
modalBody.innerHTML = `<div class="alert alert-danger">Error: ${e}</div>`;
}
}
document.addEventListener('DOMContentLoaded', () => {
setDefaults();
document.getElementById('loadSummaryBtn').addEventListener('click', () => {
const rawFrom = document.getElementById('fromDate').value;
const rawTo = document.getElementById('toDate').value;
if (rawFrom && rawTo && rawTo < rawFrom) {
alert('To date must be the same as or later than From date');
return;
}
loadAbstractCounts(); loadZoneSummary();
});
// delegate clicks on zone counts to load detailed view
document.addEventListener('click', (ev) => {
const a = ev.target.closest && ev.target.closest('.zone-count');
if (!a) return;
ev.preventDefault();
const zone = a.getAttribute('data-zone');
const empType = a.getAttribute('data-emptype');
const category = a.getAttribute('data-category');
// call detailed loader with explicit params
loadDetailed(empType, category, zone);
});
// set date constraints and load initial
setDateLimits();
// load initial
loadAbstractCounts();
loadZoneSummary();
});
})();
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Detail modal -->
<div class="modal fade" id="detailModal" tabindex="-1" aria-labelledby="detailModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="detailModalLabel">Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="detailModalBody">
Loading...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</body>
</html>

737
src/main/resources/templates/modules/greencommittee/gcreport.html

@ -0,0 +1,737 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Green Committe 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;
}
.dataTables_wrapper .dt-buttons {
margin-bottom: 10px;
}
.dataTables_filter {
margin-bottom: 25px; /* adjust as needed */
}
.page-header-title h4 {
margin: 20px 0;
text-align: center;
}
#zoneDetailsTable_wrapper .dataTables_scrollBody {
border: none !important;
}
#zoneDetailsTable td, #zoneDetailsTable th {
vertical-align: middle;
text-align: center;
white-space: nowrap;
}
.filter-btn {
height: 38px;
font-weight: 600;
}
.page-header-title {
margin-bottom: 25px;
}
#pendingModal .modal-body{
min-height: 60vh;
max-height: 70vh;
overflow-y: auto;
}
/* Thumbnail styling */
.complaint-thumb {
width: 110px;
height: 110px;
object-fit: cover;
margin: 5px;
cursor: pointer;
transition: transform 0.2s ease-in-out, box-shadow 0.2s;
}
.complaint-thumb:hover {
transform: scale(1.05);
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
/* Image grid alignment */
.image-grid {
gap: 8px;
}
</style>
</head>
<body>
<div class="page-header-title">
<h4>Green Committee - Zonewise Report</h4>
</div>
<div class="container-fluid mb-4">
<div class="row justify-content-center align-items-end gx-3">
<!-- Start Date -->
<div class="col-lg-3 col-md-4 col-sm-6 mb-2">
<label class="font-weight-bold mb-1">From Date</label>
<input type="date" id="startDate" class="form-control" onclick="this.showPicker()">
</div>
<!-- End Date -->
<div class="col-lg-3 col-md-4 col-sm-6 mb-2">
<label class="font-weight-bold mb-1">To Date</label>
<input type="date" id="endDate" class="form-control" onclick="this.showPicker()">
</div>
<!-- Buttons -->
<div class="col-lg-3 col-md-4 col-sm-12 mb-2">
<label class="font-weight-bold mb-1 d-none d-md-block">&nbsp;</label>
<div class="d-flex gap-2">
<button
class="btn btn-success flex-fill"
onclick="loadZoneReport()">
Apply Filter
</button>
<button
class="btn btn-outline-secondary flex-fill"
onclick="resetvalues()">
Reset
</button>
</div>
</div>
</div>
</div>
<div id="opSummaryTable" style="display: block;" class="container">
<table id="ZoneOpDatatable" class="table table-striped table-bordered display nowrap w-100">
<thead>
<tr>
<th class="text-center">Zones</th>
<th class="text-center">Total Request</th>
<th class="text-center">NGO Pending</th>
<th class="text-center">GCC Pending</th>
<th class="text-center">TNFD Pending</th>
<th class="text-center">Committee Pending</th>
<th class="text-center">Withdraw</th>
<th class="text-center">Completed</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr class="font-weight-bold bg-light">
<th class="text-center">TOTAL</th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
</tr>
</tfoot>
</table>
</div>
<div class="modal fade" id="zoneDetailsModal" tabindex="-1" aria-labelledby="zoneDetailsLabel" aria-hidden="true" data-bs-keyboard="false" data-bs-backdrop="static">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="zoneModalTitle">Zone Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<table id="WardDatatable" class="table table-striped table-bordered display nowrap w-100">
<thead>
<tr>
<th class="text-center">Wards</th>
<th class="text-center">Total Request</th>
<th class="text-center">NGO Pending</th>
<th class="text-center">GCC Pending</th>
<th class="text-center">TNFD Pending</th>
<th class="text-center">Committee Pending</th>
<th class="text-center">Withdraw</th>
<th class="text-center">Completed</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="pendingModal" tabindex="-1" aria-labelledby="DetailsLabel" aria-hidden="true" data-bs-keyboard="false" data-bs-backdrop="static">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 id="pendingModalTitle"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="pendingModalBody"></div>
</div>
</div>
</div>
<div class="modal fade" id="imagePreviewModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-body text-center p-2">
<img id="previewImage" src="" class="img-fluid rounded">
</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>
<!-- Add the 'Please wait' text below the spinner -->
<p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p>
</div>
</div>
<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/dist/js/bootstrap.bundle.min.js"></script>
<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>
<!-- SweetAlert -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
let CURRENT_ZONE = null;
function resetvalues(){
location.reload();
}
function openImagePreview(src) {
$('#previewImage').attr('src', src);
$('#imagePreviewModal').modal('show');
}
function loadZoneReport(){
const startDate = $('#startDate').val(); // yyyy-mm-dd or ""
const endDate = $('#endDate').val();
$('#pageLoader').show();
$.ajax({
url: '/commissionerreview/api/greencommittee/getZoneReport',
method: 'GET',
data: {
startDate: startDate,
endDate: endDate
},
success: function(response) {
// Safely extract data whether it's an array or an object
var res = Array.isArray(response) ? response[0] : response;
console.log("Response:", res);
$('#pageLoader').hide();
if (res.status === "Success") {
if (Array.isArray(res.data) && res.data.length > 0) {
console.log("Details Array:", response);
let table = $('#ZoneOpDatatable').DataTable({
destroy: true,
autoWidth: true,
info: true,
data: res.data, // using response directly
columns: [
{
data: 'zone_name',
render: d => `<a href="#" onclick="openZoneModal('${d}')">${d}</a>`
},
{ data: 'total_requests', className: 'text-center' },
{
data: 'ngo_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'NGO', 'ZONE')
},
{
data: 'gcc_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'GCC', 'ZONE')
},
{
data: 'tnfd_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'TNFD', 'ZONE')
},
{
data: 'committee_pending',
className: 'text-center'
},
{
data: 'withdrawn',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'WITHDRAW', 'ZONE')
},
{ data: 'completed', className: 'text-center' }
],
dom: 'frtip',
lengthMenu: [
[50, 100, 200, 500, -1],
['50 rows', '100 rows', '200 rows', 'Show all']
],
footerCallback: footerSumCallback,
scrollY: 'auto',
scrollX: true,
responsive: false,
searching: false,
ordering: false
});
}
else if(Array.isArray(res.data) && res.data.length === 0) {
$('#pageLoader').hide();
//alert("No data available.");
Swal.fire({
icon: 'info',
title: 'No Data',
text: 'No data available.',
showConfirmButton: true,
timer: 3000
});
}
else {
$('#pageLoader').hide();
//alert("Error: " + response.message);
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Error',
showConfirmButton: true,
timer: 3000
});
}
}
else {
$('#pageLoader').hide();
//alert("Error: " + response.message);
Swal.fire({
icon: 'error',
title: 'Error',
text: res.message,
showConfirmButton: true,
timer: 3000
});
}
},
error: function(xhr, status, error) {
$('#pageLoader').hide();
// alert("Request failed: " + error);
Swal.fire({
icon: 'error',
title: 'Error',
text: error,
showConfirmButton: true,
timer: 3000
});
}
});
}
function openZoneModal(zoneCode) {
CURRENT_ZONE = zoneCode;
console.log("Opening modal for zone:", zoneCode);
const startDate = $('#startDate').val(); // yyyy-mm-dd or ""
const endDate = $('#endDate').val();
$('#pageLoader').show();
// Set data in modal (example)
$('#zoneModalTitle').text('Zone ' + zoneCode + ' Details');
$.ajax({
url: '/commissionerreview/api/greencommittee/getWardDetailsByZone',
method: 'GET',
data:{zone:zoneCode,
startDate: startDate,
endDate: endDate},
success: function(response) {
var res = Array.isArray(response) ? response[0] : response;
console.log("Response:", res);
$('#pageLoader').hide();
if (res.status === "Success") {
if (Array.isArray(res.data) && res.data.length > 0){
console.log("Details Array:", response);
let table = $('#WardDatatable').DataTable({
destroy: true,
autoWidth: true,
info: true,
data: res.data, // using response directly
columns: [
{ data: 'ward_name', className: 'text-center' },
{ data: 'total_requests', className: 'text-center' },
{
data: 'ngo_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'NGO', 'WARD')
},
{
data: 'gcc_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'GCC', 'WARD')
},
{
data: 'tnfd_pending',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'TNFD', 'WARD')
},
{
data: 'committee_pending',
className: 'text-center'
},
{
data: 'withdrawn',
className: 'text-center',
render: (d, t, r) => renderPendingCell(d, r, 'WITHDRAW', 'WARD')
},
{ data: 'completed', className: 'text-center' }
],
footerCallback: footerSumCallback,
dom: 'frtip',
lengthMenu: [
[50, 100, 200, 500, -1],
['50 rows', '100 rows', '200 rows', 'Show all']
],
scrollY: '400px',
scrollX: true,
responsive: false,
searching: false,
ordering: false
});
$('#zoneDetailsModal').modal('show');
}
else if (Array.isArray(res.data) && res.data.length === 0){
$('#pageLoader').hide();
//alert("No data available.");
Swal.fire({
icon: 'info',
title: 'No Data',
text: 'No data available.',
showConfirmButton: true,
timer: 3000
});
}
else{
$('#pageLoader').hide();
//alert("Error: " + response.message);
Swal.fire({
icon: 'error',
title: 'Error',
text: res.message,
showConfirmButton: true,
timer: 3000
});
}
}
else {
$('#pageLoader').hide();
//alert("Error: " + response.message);
Swal.fire({
icon: 'error',
title: 'Error',
text: res.message,
showConfirmButton: true,
timer: 3000
});
}
},
error: function(xhr, status, error) {
$('#pageLoader').hide();
//alert("Request failed: " + error);
Swal.fire({
icon: 'error',
title: 'Error',
text: error,
showConfirmButton: true,
timer: 3000
});
}
});
}
function footerSumCallback(row, data) {
let api = this.api();
const sum = idx =>
api.column(idx).data()
.reduce((a, b) => Number(a) + Number(b || 0), 0);
for (let i = 1; i <= 7; i++) {
$(api.column(i).footer()).html(sum(i));
}
}
function openPendingModal(zone, ward, type, startDate, endDate, level) {
console.log("data="+zone+","+ward+","+startDate+","+endDate);
$('#pendingModal').modal('show');
$('#pendingModalTitle').text(
`${type} Pending - Zone ${zone}${ward ? ', Ward ' + ward : ''}`
);
$('#pageLoader').show();
$.ajax({
url: '/commissionerreview/api/greencommittee/getPendingDetails',
method: 'GET',
data: {
zone: zone,
ward: ward || null,
type: type,
startDate: startDate || null,
endDate: endDate || null
},
success: function (res) {
$('#pageLoader').hide();
if (res.status === 'Success' && res.data.length > 0) {
renderComplaintCards(res.data);
} else {
$('#pendingModalBody').html(
`<p class="text-center text-muted">No complaints found</p>`
);
}
},
error: function () {
$('#pageLoader').hide();
Swal.fire('Error', 'Failed to load details', 'error');
}
});
}
function renderComplaintCards(data) {
let html = '';
data.forEach(item => {
// ✅ Name fallback logic
const displayName =
item.p_name && item.p_name.trim() !== ''
? item.p_name
: [item.dept_name, item.designation].filter(Boolean).join(' - ') || '-';
html += `
<div class="card mb-3 shadow-sm">
<div class="card-header bg-info text-white font-weight-bold">
${item.ref_id} — ${item.r_Cdate}
</div>
<div class="card-body">
<div class="row align-items-start">
<!-- LEFT -->
<div class="col-md-8">
<p><strong>Name:</strong> ${displayName}</p>
<p><strong>Mobile:</strong> ${item.ph_no}</p>
<p>
<strong>Zone:</strong> ${item.zone}
<span class="mx-3">|</span>
<strong>Ward:</strong> ${item.ward}
<span class="mx-3">|</span>
<strong>Total Trees:</strong> ${item.total_trees}
</p>
<p><strong>Address:</strong> ${item.address}</p>
<p><strong>Remarks:</strong> ${item.remarks}</p>
<p><strong>Complaints:</strong> ${item.comp_details}</p>
</div>
<!-- RIGHT (Images) -->
<div class="col-md-4">
<div class="d-flex flex-wrap image-grid">
${item.image_paths.map(img => `
<img
src="${img.img_path}"
class="img-thumbnail complaint-thumb"
onclick="openImagePreview('${img.img_path}')"
>
`).join('')}
</div>
</div>
</div>
</div>
</div>`;
});
$('#pendingModalBody').html(html);
}
function renderPendingCell(value, row, type, level) {
if (!value || value === 0) {
return `<span class="text-muted">${value}</span>`;
}
const zone = row.zone_name || CURRENT_ZONE;
const ward = row.ward_name || '';
const startDate = $('#startDate').val() || '';
const endDate = $('#endDate').val() || '';
return `
<a href="javascript:void(0)"
class="text-primary font-weight-bold"
onclick="openPendingModal(
'${zone}',
'${ward}',
'${type}',
'${startDate}',
'${endDate}',
'${level}'
)">
${value}
</a>`;
}
$(document).ready(function () {
loadZoneReport();
// Set today's date as max
const today = new Date().toISOString().split('T')[0];
$('#startDate, #endDate').attr('max', today);
// When start date changes → set min for end date
$('#startDate').on('change', function () {
const startDate = $(this).val();
$('#endDate').attr('min', startDate);
// If endDate already selected but invalid → clear it
if ($('#endDate').val() && $('#endDate').val() < startDate) {
$('#endDate').val('');
}
});
// When end date changes → validate against start date
$('#endDate').on('change', function () {
const startDate = $('#startDate').val();
const endDate = $(this).val();
if (startDate && endDate < startDate) {
Swal.fire({
icon: 'warning',
title: 'Invalid Date Range',
text: 'End date cannot be before start date'
});
$(this).val('');
}
});
$('#zoneDetailsModal').on('shown.bs.modal', function () {
if ($.fn.DataTable.isDataTable('#WardDatatable')) {
let questionDatatable = $('#WardDatatable').DataTable();
questionDatatable.columns.adjust().draw();
}
});
});
</script>
</body>
</html>
Loading…
Cancel
Save