31 changed files with 6448 additions and 1117 deletions
-
19.mvn/wrapper/maven-wrapper.properties
-
10pom.xml
-
43src/main/java/com/gcc/publicgrievance/Service/ComplaintService.java
-
47src/main/java/com/gcc/publicgrievance/config/AppConfigur.java
-
14src/main/java/com/gcc/publicgrievance/configuration/AppConfig.java
-
73src/main/java/com/gcc/publicgrievance/configuration/DataSourceConfig.java
-
24src/main/java/com/gcc/publicgrievance/configuration/SecurityConfig.java
-
33src/main/java/com/gcc/publicgrievance/configuration/UserSessionInterceptor.java
-
123src/main/java/com/gcc/publicgrievance/controller/APIController.java
-
747src/main/java/com/gcc/publicgrievance/controller/ComplaintController.java
-
18src/main/java/com/gcc/publicgrievance/controller/UserController.java
-
20src/main/resources/application-dev.properties
-
24src/main/resources/application-prod.properties
-
9src/main/resources/application.properties
-
BINsrc/main/resources/static/images/message.png
-
BINsrc/main/resources/static/images/otp.png
-
BINsrc/main/resources/static/images/output-onlinepngtools.png
-
59src/main/resources/static/js/screenreader.js
-
78src/main/resources/templates/User/home.html
-
0src/main/resources/templates/User/kin.html
-
1852src/main/resources/templates/User/registercomplaints.html
-
160src/main/resources/templates/checklistofficer/checklistofficer.html
-
4src/main/resources/templates/fragments/accessibility.html
-
5src/main/resources/templates/fragments/footer.html
-
67src/main/resources/templates/fragments/header.html
-
258src/main/resources/templates/officer/checkofficerhome.html
-
528src/main/resources/templates/officer/checkofficerstatus.html
-
614src/main/resources/templates/staff/staffcheckstatus.html
-
1538src/main/resources/templates/staff/staffcomplaint.html
-
543src/main/resources/templates/staff/staffhome.html
-
13src/test/java/com/gcc/publicgrievance/PublicgrievanceApplicationTests.java
@ -1,19 +0,0 @@ |
|||
# Licensed to the Apache Software Foundation (ASF) under one |
|||
# or more contributor license agreements. See the NOTICE file |
|||
# distributed with this work for additional information |
|||
# regarding copyright ownership. The ASF licenses this file |
|||
# to you under the Apache License, Version 2.0 (the |
|||
# "License"); you may not use this file except in compliance |
|||
# with the License. You may obtain a copy of the License at |
|||
# |
|||
# https://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, |
|||
# software distributed under the License is distributed on an |
|||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|||
# KIND, either express or implied. See the License for the |
|||
# specific language governing permissions and limitations |
|||
# under the License. |
|||
wrapperVersion=3.3.2 |
|||
distributionType=only-script |
|||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip |
|||
@ -0,0 +1,47 @@ |
|||
package com.gcc.publicgrievance.config; |
|||
|
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@ConfigurationProperties(prefix = "app") |
|||
public class AppConfigur { |
|||
|
|||
private String title; |
|||
private String baseUrl; |
|||
private String getOtpUrl; |
|||
private String erpUrl; |
|||
|
|||
public String getTitle() { |
|||
return title; |
|||
} |
|||
|
|||
public void setTitle(String title) { |
|||
this.title = title; |
|||
} |
|||
|
|||
public String getBaseUrl() { |
|||
return baseUrl; |
|||
} |
|||
|
|||
public void setBaseUrl(String baseUrl) { |
|||
this.baseUrl = baseUrl; |
|||
} |
|||
|
|||
public String getGetOtpUrl() { |
|||
return getOtpUrl; |
|||
} |
|||
|
|||
public void setGetOtpUrl(String getOtpUrl) { |
|||
this.getOtpUrl = getOtpUrl; |
|||
} |
|||
|
|||
public String getErpUrl() { |
|||
return erpUrl; |
|||
} |
|||
|
|||
public void setErpUrl(String erpUrl) { |
|||
this.erpUrl = erpUrl; |
|||
} |
|||
|
|||
} |
|||
@ -1,15 +1,15 @@ |
|||
package com.gcc.publicgrievance.configuration; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.client.RestTemplate; |
|||
package com.gcc.publicgrievance.configuration; |
|||
|
|||
@Configuration |
|||
public class AppConfig { |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
@Configuration public class AppConfig { |
|||
|
|||
@Bean |
|||
public RestTemplate restTemplate() { |
|||
return new RestTemplate(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
package com.gcc.publicgrievance.configuration; |
|||
|
|||
import java.util.Properties; |
|||
|
|||
import org.hibernate.jpa.HibernatePersistenceProvider; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Primary; |
|||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; |
|||
import org.springframework.jdbc.datasource.DriverManagerDataSource; |
|||
import org.springframework.orm.jpa.JpaTransactionManager; |
|||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; |
|||
import org.springframework.transaction.PlatformTransactionManager; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import javax.sql.DataSource; |
|||
|
|||
@Configuration |
|||
@EnableTransactionManagement |
|||
public class DataSourceConfig { |
|||
|
|||
private static String host = "localhost"; |
|||
//private static String dbpassword = "gccroot"; |
|||
private static String dbpassword = "password"; |
|||
|
|||
@Configuration |
|||
@EnableTransactionManagement |
|||
@EnableJpaRepositories( |
|||
entityManagerFactoryRef = "appEntityManagerFactory", |
|||
transactionManagerRef = "appTransactionManager", |
|||
basePackages = {"com.gcc.publicgrievance"} |
|||
) |
|||
|
|||
public static class MysqlAppDataSourceConfig { |
|||
@Primary |
|||
@Bean(name = "mysqlAppDataSource") |
|||
public DataSource mysqlAppDataSource() { |
|||
DriverManagerDataSource dataSource = new DriverManagerDataSource(); |
|||
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); |
|||
dataSource.setUrl("jdbc:mysql://"+host+"/gcc_rest_services"); |
|||
dataSource.setUsername("root"); |
|||
dataSource.setPassword(dbpassword); |
|||
return dataSource; |
|||
} |
|||
|
|||
@Primary |
|||
@Bean(name="appEntityManagerFactory") |
|||
public LocalContainerEntityManagerFactoryBean entityManagerFactory( |
|||
@Qualifier("mysqlAppDataSource") DataSource dataSource) { |
|||
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); |
|||
entityManagerFactoryBean.setDataSource(dataSource); |
|||
entityManagerFactoryBean.setPackagesToScan("com.gcc.publicgrievance"); |
|||
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); |
|||
Properties properties = new Properties(); |
|||
properties.setProperty("hibernate.hbm2ddl.auto", "update"); |
|||
entityManagerFactoryBean.setJpaProperties(properties); |
|||
|
|||
return entityManagerFactoryBean; |
|||
} |
|||
|
|||
@Primary |
|||
@Bean(name = "appTransactionManager") |
|||
public PlatformTransactionManager transactionManager(@Qualifier("appEntityManagerFactory") jakarta.persistence.EntityManagerFactory entityManagerFactory) { |
|||
return new JpaTransactionManager(entityManagerFactory); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package com.gcc.publicgrievance.configuration; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
|||
import org.springframework.security.web.SecurityFilterChain; |
|||
|
|||
@Configuration |
|||
public class SecurityConfig { |
|||
|
|||
@Bean |
|||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
|||
http |
|||
.csrf(csrf -> csrf.disable()) |
|||
.authorizeHttpRequests(auth -> auth |
|||
.requestMatchers("/pgr/api/**").permitAll() |
|||
.anyRequest().permitAll() |
|||
) |
|||
.formLogin(form -> form.disable()) |
|||
.httpBasic(httpBasic -> httpBasic.disable()); |
|||
|
|||
return http.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
package com.gcc.publicgrievance.configuration; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import com.gcc.publicgrievance.config.AppConfigur; |
|||
|
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
|
|||
@Component |
|||
public class UserSessionInterceptor implements HandlerInterceptor { |
|||
|
|||
|
|||
|
|||
@Autowired |
|||
private AppConfigur appConfig; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
// Get the current request URL |
|||
String requestUrl = request.getRequestURI(); |
|||
request.setAttribute("baseURL", appConfig.getBaseUrl()); |
|||
|
|||
return true; // Allow the request to proceed |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
package com.gcc.publicgrievance.controller; |
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.IOException; |
|||
import java.io.InputStreamReader; |
|||
import java.io.OutputStream; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.net.URLEncoder; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.Date; |
|||
import java.util.Random; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RequestPart; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.springframework.web.client.RestTemplate; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; // Import Jackson's ObjectMapper |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import com.gcc.publicgrievance.config.AppConfigur; |
|||
|
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpSession; |
|||
|
|||
@RequestMapping("/pgr/api") |
|||
@RestController |
|||
public class APIController { |
|||
|
|||
@Autowired |
|||
private AppConfigur appConfig; |
|||
|
|||
@GetMapping(value = "/sendOTP") |
|||
public String sendOtp(HttpServletRequest request, |
|||
@RequestParam String mobileNo, |
|||
@RequestParam(defaultValue = "5") int otpLength, |
|||
@RequestParam(defaultValue = "false") boolean mobilenocheck) { |
|||
|
|||
String bodyMessage = ""; |
|||
|
|||
try { |
|||
// Validate Mobile Number |
|||
if (mobileNo.length() != 10) { |
|||
throw new IllegalArgumentException("Invalid Mobile Number. It must be 10 digits."); |
|||
} |
|||
|
|||
// New SMS API URL |
|||
String urlString = "https://gccservices.in/gccofficialapp/api/sendOTP?mobileNo=" + mobileNo + |
|||
"&otpLength=" + otpLength + "&mobilenocheck=" + mobilenocheck; |
|||
|
|||
System.out.println("\nurlString---: " + urlString); |
|||
|
|||
// Send the OTP using the new SMS API |
|||
RestTemplate restTemplate = new RestTemplate(); |
|||
ResponseEntity<String> response = restTemplate.exchange(urlString, HttpMethod.GET, null, String.class); |
|||
|
|||
// Log full response and status code |
|||
System.out.println("\nResponse Status Code: " + response.getStatusCode()); |
|||
System.out.println("\nResponse Body: " + response.getBody()); |
|||
|
|||
// Parse the response to extract the OTP from the API response |
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
JsonNode jsonResponse = objectMapper.readTree(response.getBody()); |
|||
String apiOtp = jsonResponse.get(0).get("OTP").asText(); // Extract OTP from the response |
|||
int statusCode = jsonResponse.get(0).get("status").asInt(); // Extract status code |
|||
String message = jsonResponse.get(0).get("message").asText(); // Extract message |
|||
|
|||
// Store the OTP in the session |
|||
HttpSession session = request.getSession(); |
|||
session.setAttribute("otp", apiOtp); // Use the OTP from the API response |
|||
|
|||
// Construct the bodyMessage with status, mobileNo, and OTP |
|||
bodyMessage = "[{\"Status\": " + statusCode + " , \"Mobile No\": " + mobileNo + " , \"OTP\": " + apiOtp + " , \"Message\": \"" + message + "\"}]"; |
|||
System.out.println("OTP sent successfully!"); |
|||
|
|||
} catch (IllegalArgumentException e) { |
|||
// Catch validation error for mobile number |
|||
System.out.println("ERROR---: " + e.getMessage()); |
|||
bodyMessage = "Error: " + e.getMessage(); |
|||
} catch (Exception e) { |
|||
// Catch general errors |
|||
System.out.println("ERROR---: " + e); |
|||
bodyMessage = "There was a problem sending OTP. Try again."; |
|||
} |
|||
|
|||
return bodyMessage; |
|||
} |
|||
|
|||
@GetMapping(value = "/verifyOTP") |
|||
public String verifyOtp(HttpServletRequest request, @RequestParam String otp) { |
|||
HttpSession session = request.getSession(); |
|||
try { |
|||
String sessionOtp = (String) session.getAttribute("otp"); // Get OTP from session |
|||
System.out.println("sessionOtp---:" + sessionOtp); |
|||
String enteredOtp = otp.trim(); // OTP entered by user |
|||
|
|||
if (sessionOtp != null && sessionOtp.equals(enteredOtp)) { |
|||
System.out.println("Entered OTP Correct"); |
|||
session.setAttribute("verifiedotp", "success"); |
|||
return "Success"; // OTP is correct |
|||
} else { |
|||
System.out.println("Incorrect OTP. Please enter correct OTP."); |
|||
session.setAttribute("verifiedotp", "error"); |
|||
return "Error"; // OTP mismatch |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error verifying OTP: " + e.getMessage()); |
|||
return "Error"; // Handle any exceptions |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
spring.application.name=publicgrievance |
|||
# Sets the session timeout to 60 seconds |
|||
#server.servlet.session.timeout=60s |
|||
|
|||
# Sets the async request timeout to 5000 milliseconds (5 seconds) |
|||
#spring.mvc.async.request-timeout=5000ms |
|||
|
|||
server.port=8072 |
|||
#server.port=8061 |
|||
|
|||
app.baseUrl=http://localhost:8072 |
|||
|
|||
#For development |
|||
#assets.baseUrl=/ |
|||
|
|||
|
|||
app.getOtpUrl=https://tmegov.onex-aura.com/api/sms? |
|||
|
|||
app.erpUrl=https://erp.chennaicorporation.gov.in/pgr/newmobileservice? |
|||
app.title=GREATER CHENNAI CORPORATION - PGR |
|||
@ -0,0 +1,24 @@ |
|||
spring.application.name=publicgrievance |
|||
# Sets the session timeout to 60 seconds |
|||
#server.servlet.session.timeout=60s |
|||
|
|||
# Sets the async request timeout to 5000 milliseconds (5 seconds) |
|||
#spring.mvc.async.request-timeout=5000ms |
|||
|
|||
server.port=8072 |
|||
#server.port=8061 |
|||
|
|||
app.baseUrl=https://gccservices.chennaicorporation.gov.in/pgr/ |
|||
|
|||
#For Production |
|||
#assets.baseUrl=/pgr |
|||
|
|||
|
|||
app.getOtpUrl=https://tmegov.onex-aura.com/api/sms? |
|||
|
|||
app.erpUrl=https://erp.chennaicorporation.gov.in/pgr/newmobileservice? |
|||
app.title=GREATER CHENNAI CORPORATION - PGR |
|||
|
|||
# Disable DataSource auto-configuration since no database is being used |
|||
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration |
|||
|
|||
@ -1,8 +1 @@ |
|||
spring.application.name=publicgrievance |
|||
# Sets the session timeout to 60 seconds |
|||
server.servlet.session.timeout=60s |
|||
|
|||
# Sets the async request timeout to 5000 milliseconds (5 seconds) |
|||
spring.mvc.async.request-timeout=5000ms |
|||
|
|||
|
|||
spring.profiles.active= prod |
|||
|
After Width: 512 | Height: 512 | Size: 16 KiB |
|
After Width: 1102 | Height: 1020 | Size: 234 KiB |
|
After Width: 512 | Height: 512 | Size: 4.6 KiB |
@ -1,33 +1,44 @@ |
|||
// screenReader.js
|
|||
// speech.js
|
|||
|
|||
let isReading = false; |
|||
let speechSynthesisUtterance; |
|||
let isSpeechEnabled = false; // Start with speech disabled
|
|||
|
|||
function readScreenContent() { |
|||
const content = document.body.innerText; |
|||
speechSynthesisUtterance = new SpeechSynthesisUtterance(content); |
|||
window.speechSynthesis.speak(speechSynthesisUtterance); |
|||
isReading = true; |
|||
} |
|||
|
|||
function stopReading() { |
|||
if (speechSynthesisUtterance) { |
|||
window.speechSynthesis.cancel(); |
|||
isReading = false; |
|||
// Function to handle the speech synthesis
|
|||
function speakText(text) { |
|||
if (isSpeechEnabled) { |
|||
const speech = new SpeechSynthesisUtterance(text); |
|||
speech.lang = 'en-US'; // Set language as needed
|
|||
window.speechSynthesis.speak(speech); |
|||
} |
|||
} |
|||
|
|||
function toggleScreenReader(buttonId) { |
|||
const button = document.getElementById(buttonId); |
|||
if (button) { |
|||
button.addEventListener('click', function() { |
|||
if (isReading) { |
|||
stopReading(); |
|||
button.innerText = "Read Screen"; // Update button text when stopped
|
|||
// Function to add event listeners to all relevant elements for speech
|
|||
function addSpeechListeners() { |
|||
const elementsToRead = document.querySelectorAll('label, button, input, h1, h2, h3, h4,h5,h6, p, .card-footer'); // Add any additional selectors as needed
|
|||
elementsToRead.forEach(function(element) { |
|||
element.addEventListener('mouseover', function() { |
|||
// Check if it's an input field and read the placeholder if it exists
|
|||
if (element.tagName.toLowerCase() === 'input') { |
|||
const placeholder = element.getAttribute('placeholder'); |
|||
if (placeholder) { |
|||
speakText(placeholder.trim()); |
|||
} |
|||
} else { |
|||
readScreenContent(); |
|||
button.innerText = "Stop Reading"; // Update button text when reading
|
|||
speakText(element.textContent.trim()); // Speak the element's text when hovered
|
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
document.addEventListener('DOMContentLoaded', function() { |
|||
addSpeechListeners(); // Add speech listeners when the document is loaded
|
|||
|
|||
// Add event listener to the toggle button
|
|||
const toggleButton = document.getElementById('toggleSpeech'); |
|||
toggleButton.addEventListener('click', function() { |
|||
isSpeechEnabled = !isSpeechEnabled; // Toggle the speech feature
|
|||
|
|||
// Update button text based on the state, preserving the image
|
|||
const buttonText = isSpeechEnabled ? 'Turn Off Speech' : 'Turn On Speech'; |
|||
toggleButton.innerHTML = `<img src="images/output-onlinepngtools.png" alt="Accessibility Icon" style="height: 30px;"> ${buttonText}`; |
|||
}); |
|||
}); |
|||
1852
src/main/resources/templates/User/registercomplaints.html
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,258 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>Main Page</title> |
|||
<!--Google Font --> |
|||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet"> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" |
|||
integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" |
|||
crossorigin="anonymous" referrerpolicy="no-referrer" /> |
|||
<!--Bootstrap CSS--> |
|||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> |
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> |
|||
<script th:src="@{/js/invertcolor.js}"></script> |
|||
<style> |
|||
body { |
|||
font-family: 'Lato', sans-serif; |
|||
background-color: white; |
|||
} |
|||
|
|||
body.inverted { |
|||
background-color: black; |
|||
color: white; |
|||
filter: invert(1); |
|||
} |
|||
|
|||
.card { |
|||
border-radius: 15px; |
|||
border: none; |
|||
padding: 20px; |
|||
background-color: #f0f4ff; |
|||
text-align: center; |
|||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100%; |
|||
/* Make sure cards take up full height of the column */ |
|||
} |
|||
|
|||
.card-body { |
|||
flex: 1; |
|||
/* Allow card-body to grow and take up available space */ |
|||
} |
|||
|
|||
.card-img { |
|||
width: 40px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.card-title { |
|||
font-size: 1.2rem; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.card-text { |
|||
color: #666; |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.card-footer { |
|||
font-weight: bold; |
|||
color: #007bff; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.card-footer i { |
|||
margin-left: 5px; |
|||
} |
|||
|
|||
.row { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.col-md-4 { |
|||
display: flex; |
|||
/* Ensure columns use Flexbox to distribute space evenly */ |
|||
padding: 0 15px; |
|||
/* Adjust padding to fit full width */ |
|||
} |
|||
|
|||
.container { |
|||
max-width: 1200px; |
|||
} |
|||
|
|||
.container .row .col-md-4 { |
|||
padding: 0 15px; |
|||
/* Adjust padding to fit full width */ |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.card { |
|||
flex-grow: 1; |
|||
/* Ensure all cards have the same height */ |
|||
} |
|||
|
|||
.container .row .col-md-12 { |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.card-link { |
|||
text-decoration: none; |
|||
/* Remove the underline from the link */ |
|||
color: inherit; |
|||
/* Inherit the text color */ |
|||
display: block; |
|||
/* Make the link cover the entire card */ |
|||
} |
|||
|
|||
.card-link:hover .card { |
|||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); |
|||
/* Optional: add hover effect */ |
|||
} |
|||
|
|||
/* Style for reader button */ |
|||
|
|||
|
|||
#reader-btn.active { |
|||
background-color: #28a745; |
|||
} |
|||
|
|||
/*header css*/ |
|||
.header { |
|||
background-color: #3b4858; |
|||
padding: 10px 20px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
color: white; |
|||
} |
|||
|
|||
.header .logo-left img { |
|||
height: 60px; |
|||
} |
|||
|
|||
.header .title { |
|||
text-align: center; |
|||
flex-grow: 1; |
|||
} |
|||
|
|||
.header .title h2 { |
|||
margin: 0; |
|||
font-size: 1.5rem; |
|||
} |
|||
|
|||
.header .logos-right img { |
|||
height: 60px; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
/*header buttons*/ |
|||
.header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
/* Space out the items within the header */ |
|||
} |
|||
|
|||
.header-buttons { |
|||
padding-left: 5px; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 10px; |
|||
/* Adjust spacing between buttons */ |
|||
} |
|||
|
|||
#reader-btn, |
|||
#invertColorBtn { |
|||
padding: 5px 10px; |
|||
font-size: 14px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
/*for radio button style*/ |
|||
.radio-group { |
|||
display: flex; |
|||
align-items: center; |
|||
/* Ensures all items are aligned to the center */ |
|||
} |
|||
|
|||
.radio-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-right: 20px; |
|||
/* Adds space between each radio item */ |
|||
} |
|||
|
|||
.radio-item input[type="radio"] { |
|||
margin-right: 10px; |
|||
/* Adds space between the radio button and its label */ |
|||
vertical-align: middle; |
|||
/* Ensures vertical alignment */ |
|||
} |
|||
|
|||
.radio-item label { |
|||
margin: 0; |
|||
/* Removes any default margins that might affect alignment */ |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
|
|||
<body> |
|||
<div th:insert="~{fragments/accessibility :: accessibility}"></div> |
|||
|
|||
<!-- Insert the header fragment --> |
|||
<div th:insert="fragments/header :: headerFragment"></div> |
|||
|
|||
|
|||
|
|||
<!-- Cards Section --> |
|||
<!-- Cards Section --> |
|||
<div class="container d-flex justify-content-center align-items-center" style="height: 100vh;"> |
|||
<!--card to register complaint--> |
|||
<div class="col-md-12"> |
|||
<a href="/pgr/registercomplaints" class="card-link"> |
|||
<div class="card content" style="margin-bottom: 12%;"> |
|||
<div class="card-body"> |
|||
<img src="images/register.png" alt="register" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Register Your<br> Complaints</h5> |
|||
<p class="card-text">Register Complaints<br> Via Internet</p> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
Get Started <i class="fa fa-arrow-right"></i> |
|||
</div> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<div th:insert="fragments/footer :: footerFragment"></div> |
|||
|
|||
<script th:src="@{/js/screenreader.js}"></script> |
|||
|
|||
<!-- Replace Slim jQuery with Full jQuery --> |
|||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> |
|||
|
|||
<!-- Popper.js (for Bootstrap tooltips, popovers, etc.) --> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script> |
|||
|
|||
<!-- Bootstrap JS --> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Font Awesome for Icons --> |
|||
<script src="https://kit.fontawesome.com/a076d05399.js"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,528 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>CheckList User</title> |
|||
<!-- Google Font --> |
|||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet"> |
|||
<!-- Bootstrap CSS --> |
|||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> |
|||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" /> |
|||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> |
|||
<style> |
|||
/*rating style for css*/ |
|||
.rating { |
|||
direction: rtl; |
|||
display: inline-flex; |
|||
font-size: 24px; |
|||
} |
|||
|
|||
.rating input { |
|||
display: none; |
|||
} |
|||
|
|||
.rating label { |
|||
color: #ddd; |
|||
cursor: pointer; |
|||
margin: 0; |
|||
} |
|||
|
|||
.rating input:checked~label { |
|||
color: #414f69; |
|||
} |
|||
|
|||
.rating label:hover, |
|||
.rating label:hover~label { |
|||
color: #414f69; |
|||
} |
|||
|
|||
/* Style for resizing content */ |
|||
.resizable-content { |
|||
font-size: medium; |
|||
transition: font-size 0.3s ease; |
|||
} |
|||
|
|||
|
|||
.comment-table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
} |
|||
|
|||
.comment-table td { |
|||
padding: 0.5rem; |
|||
border: 1px solid #ddd; |
|||
/* Light border color */ |
|||
} |
|||
|
|||
.comment-table td:first-child { |
|||
width: 25%; |
|||
/* Adjust as needed for label column width */ |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.comment-table td:nth-child(2) { |
|||
width: 75%; |
|||
/* Adjust as needed for value column width */ |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<!-- Insert the header fragment --> |
|||
|
|||
<div th:insert="~{fragments/header :: headerFragment}"></div> |
|||
|
|||
<div class="container mt-4"> |
|||
<!-- Loader GIF --> |
|||
<div id="loader" style="display: none; text-align: center;"> |
|||
<img src="images/Thin broken ring.gif" alt="Loading..." /> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<div class="mt-1"> |
|||
<div class="row align-items-center justify-content-center"> |
|||
<h4 class="title resizable-content" style="color: #414f69;">Check Complaint Status</h4> |
|||
</div> |
|||
</div> |
|||
<!-- Card Section --> |
|||
<div class="card p-3 resizable-content" style="margin-bottom: 8px;color: #414f69;"> |
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">1. Complaining Person's Details |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
Complainant ID: |
|||
<input type="text" id="complaintid" class="form-control" value="" disabled> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
Complainant Mobile Number: |
|||
<input type="text" id="complaint-mobile-number" class="form-control" value="" disabled> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
Complainant Name: |
|||
<input type="text" id="complaint-customer-name" class="form-control" value="" disabled> |
|||
</div> |
|||
|
|||
<!--<div class="col-md-6"> |
|||
Gender: |
|||
<select class="form-control" id="gender" disabled> |
|||
<option value="0">Select Gender</option> |
|||
<option value="male">Male</option> |
|||
<option value="female">Female</option> |
|||
<option value="transgender">Transgender</option> |
|||
</select> |
|||
</div>--> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">2. Complaint Location Details |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
Complainant Street: |
|||
<input type="text" id="street" class="form-control" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Zone: |
|||
<input type="text" class="form-control" id="zone" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Ward: |
|||
<input type="text" class="form-control" id="ward" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Area: |
|||
<input type="text" class="form-control" id="area" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Locality: |
|||
<input type="text" class="form-control" id="location" disabled> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">3. Complaint Type</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
Complainant Type: |
|||
<input type="text" class="form-control" id="complaintType" disabled> |
|||
</div> |
|||
|
|||
<!--<div class="col-md-4"> |
|||
Complainant Sub-Type: |
|||
<input type="text" class="form-control" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Title: |
|||
<input type="text" class="form-control" disabled> |
|||
</div>--> |
|||
<div class="col-md-12"> |
|||
Complainant Description: |
|||
<input type="text" class="form-control" id="complaintdesc" disabled> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Complaint Action</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
|
|||
|
|||
<div class="col-md-4"> |
|||
Complaint Closed Date: |
|||
|
|||
<input type="text" class="form-control" id="commentDate" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Official Name: |
|||
<input type="text" class="form-control" id="commentuser" disabled> |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
Status: |
|||
<input type="text" class="form-control" id="statusshow" |
|||
style="background-color: #414f69;color: white;" disabled> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Complaint Action History</div> |
|||
<div class="card-body"> |
|||
<div id="comments-container"></div> <!-- Container for comments --> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Update Complaint</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
Change Complaint Status: |
|||
<select class="form-control" name="complaintStatusID" id="complaintStatus"> |
|||
<option value="0">Choose</option> |
|||
<option value="8">WITHDRAW</option> |
|||
<option value="9">REOPENED</option> |
|||
|
|||
</select> |
|||
</div> |
|||
|
|||
<div class="col-md-6" id="img" style="display: none;"> |
|||
Photo: |
|||
<input type="file" class="form-control" id="photo"> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
Details of Complaint: |
|||
<textarea class="form-control" id="othercomplaint-details" rows="3" |
|||
placeholder="Enter complaint details" maxlength="400" required></textarea> |
|||
</div> |
|||
</div> |
|||
<div class="row align-items-center justify-content-center" style="margin-top: 5px;"> |
|||
<button class="btn btn-sm" style="background-color: #414f69;color: white;">Submit</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
|
|||
</div> |
|||
|
|||
|
|||
|
|||
<!-- Bootstrap JS --> |
|||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> |
|||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> |
|||
|
|||
<script> |
|||
|
|||
</script> |
|||
|
|||
|
|||
<!-- JavaScript to handle button clicks --> |
|||
<script> |
|||
|
|||
// /api/getComplaintDetailsbyId |
|||
$(document).ready(function () { |
|||
var urlParams = new URLSearchParams(window.location.search); |
|||
var complaintNo = urlParams.get('complaintno'); |
|||
|
|||
if (complaintNo) { |
|||
$('#loader').show(); |
|||
|
|||
$.ajax({ |
|||
url: '/pgr/api/getComplaintDetailsbyId', // Replace with your actual API endpoint |
|||
type: 'GET', |
|||
data: {complaintid: complaintNo}, |
|||
success: function (response) { |
|||
console.log('API response:', response); |
|||
$('#loader').hide(); |
|||
|
|||
if (Array.isArray(response) && response.length > 0) { |
|||
var complaintListHistory = response[0].ComplaintListHistory; |
|||
|
|||
if (Array.isArray(complaintListHistory) && complaintListHistory.length > 0) { |
|||
var complaintDetails = complaintListHistory[0]; |
|||
|
|||
// Decode HTML entities before populating fields |
|||
function decodeHtmlEntities(str) { |
|||
var txt = document.createElement('textarea'); |
|||
txt.innerHTML = str; |
|||
return txt.value; |
|||
} |
|||
|
|||
// Populate other form fields |
|||
document.getElementById("complaintid").value = complaintDetails.complaintid || ''; |
|||
document.getElementById("complaint-mobile-number").value = complaintDetails.CustomerMobile || ''; |
|||
document.getElementById("complaint-customer-name").value = complaintDetails.CustomerName || ''; |
|||
document.getElementById("street").value = complaintDetails.Street || ''; |
|||
document.getElementById("zone").value = complaintDetails.Zone || ''; |
|||
document.getElementById("ward").value = complaintDetails.Division || ''; |
|||
document.getElementById("area").value = complaintDetails.Area || ''; |
|||
document.getElementById("location").value = complaintDetails.Location || ''; |
|||
document.getElementById("complaintType").value = complaintDetails.complainttype || ''; |
|||
|
|||
// Decode and populate complaint description |
|||
document.getElementById("complaintdesc").value = decodeHtmlEntities(complaintDetails.complaintdetails || ''); |
|||
|
|||
document.getElementById("commentDate").value = complaintDetails.complaintcloseddate || ''; |
|||
document.getElementById("commentuser").value = complaintDetails.Officialname || ''; |
|||
document.getElementById("statusshow").value = complaintDetails.complaintcurrentstatus || ''; |
|||
|
|||
// Handle nested Comments array |
|||
if (Array.isArray(complaintDetails.Comments) && complaintDetails.Comments.length > 0) { |
|||
var commentsContainer = $('#comments-container'); |
|||
commentsContainer.empty(); // Clear the container |
|||
|
|||
var comments = complaintDetails.Comments; |
|||
var lastComment = comments[comments.length - 1]; |
|||
|
|||
// Decode HTML entities for last comment |
|||
function decodeHtmlEntities(str) { |
|||
var txt = document.createElement('textarea'); |
|||
txt.innerHTML = str; |
|||
return txt.value; |
|||
} |
|||
|
|||
// Handle last comment |
|||
var userImageHtml = ''; |
|||
if (Array.isArray(lastComment.userimages) && lastComment.userimages.length > 0) { |
|||
var imageUrl = lastComment.userimages[0].Comp_Image; |
|||
if (imageUrl && imageUrl !== 'null') { |
|||
userImageHtml = ` |
|||
<div class="col-md-12"> |
|||
<img src="${imageUrl}" alt="User Image" class="img-fluid"> |
|||
</div>`; |
|||
} |
|||
} |
|||
|
|||
var lastCommentHtml = ` |
|||
<div class="row mb-3"> |
|||
<table class="comment-table"> |
|||
<tr> |
|||
<td style="color:#3c4959">User:</td> |
|||
<td>${lastComment.user || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Message:</td> |
|||
<td>${lastComment.message || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Comment Date:</td> |
|||
<td>${lastComment.comment_date || ''}</td> |
|||
</tr> |
|||
${userImageHtml} |
|||
</table> |
|||
</div> |
|||
<hr>`; |
|||
|
|||
commentsContainer.append(lastCommentHtml); |
|||
|
|||
// Add dropdown button for other comments |
|||
if (comments.length > 1) { |
|||
var dropdownHtml = ` |
|||
<button id="show-all-comments" class="btn mt-3" style="margin-bottom: 10px;background-color: #3c4959; |
|||
color: white;">Show All Comments</button> |
|||
<div id="all-comments" style="display:none;">`; |
|||
|
|||
// Add all comments to dropdown |
|||
comments.forEach(function (comment, index) { |
|||
if (index < comments.length - 1) { // Skip the last comment |
|||
var userImageHtml = ''; |
|||
if (Array.isArray(comment.userimages) && comment.userimages.length > 0) { |
|||
var imageUrl = comment.userimages[0].Comp_Image; |
|||
if (imageUrl && imageUrl !== 'null') { |
|||
userImageHtml = ` |
|||
<div class="col-md-12"> |
|||
<img src="${imageUrl}" alt="User Image" class="img-fluid"> |
|||
</div>`; |
|||
} |
|||
} |
|||
|
|||
dropdownHtml += ` |
|||
<div class="row mb-3"> |
|||
<table class="comment-table"> |
|||
<tr> |
|||
<td style="color:#3c4959">User:</td> |
|||
<td>${comment.user || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Message:</td> |
|||
<td>${comment.message || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Comment Date:</td> |
|||
<td>${comment.comment_date || ''}</td> |
|||
</tr> |
|||
${userImageHtml} |
|||
</table> |
|||
</div> |
|||
<hr>`; |
|||
} |
|||
}); |
|||
|
|||
dropdownHtml += '</div>'; |
|||
|
|||
commentsContainer.append(dropdownHtml); |
|||
|
|||
// Toggle display of all comments |
|||
$('#show-all-comments').click(function () { |
|||
$('#all-comments').toggle(); |
|||
$(this).text($(this).text() === 'Show All Comments' ? 'Hide Comments' : 'Show All Comments'); |
|||
}); |
|||
} |
|||
} else { |
|||
$('#comments-container').html('<p>No comments found.</p>'); |
|||
} |
|||
} else { |
|||
alert('No complaint details found in ComplaintListHistory.'); |
|||
} |
|||
} else { |
|||
alert('Unexpected response structure.'); |
|||
} |
|||
}, |
|||
error: function (xhr, status, error) { |
|||
console.log('Error:', xhr.responseText); |
|||
alert('An error occurred while fetching complaint details.'); |
|||
$('#loader').hide(); |
|||
} |
|||
}); |
|||
} else { |
|||
alert('No complaint number provided.'); |
|||
} |
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
$(document).ready(function () { |
|||
$('#complaintStatus').change(function () { |
|||
if ($(this).val() == '9') { // Show the image field if 'REOPENED' is selected |
|||
$('#img').show(); |
|||
} else { |
|||
$('#img').hide(); |
|||
} |
|||
}); |
|||
|
|||
$('.btn').click(function (e) { |
|||
e.preventDefault(); |
|||
|
|||
var complaintId = $('#complaintid').val(); |
|||
var complaintStatus = $('#complaintStatus').val(); |
|||
var message = $('#othercomplaint-details').val(); |
|||
|
|||
// Validate "Details of Complaint" field |
|||
if (message.trim() === "") { |
|||
Swal.fire({ |
|||
title: 'Validation Error!', |
|||
text: 'Please enter the details of the complaint.', |
|||
icon: 'warning', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
if (complaintStatus == '0') { |
|||
Swal.fire({ |
|||
title: 'Validation Error!', |
|||
text: 'Please select a valid complaint status.', |
|||
icon: 'warning', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
var formData = new FormData(); |
|||
formData.append("ComplaintId", complaintId); |
|||
formData.append("Message", message); |
|||
formData.append("Status", complaintStatus); |
|||
|
|||
var imageFile = $('#photo')[0].files[0]; |
|||
if (imageFile) { |
|||
formData.append("Comp_Image", imageFile); |
|||
} |
|||
|
|||
$.ajax({ |
|||
url: '/publicgrievance/updateComplaint', |
|||
type: 'POST', |
|||
data: formData, |
|||
processData: false, |
|||
contentType: false, |
|||
success: function (response) { |
|||
console.log(imageFile,complaintId,message,complaintStatus); |
|||
Swal.fire({ |
|||
title: 'Success!', |
|||
text: 'Complaint updated successfully.', |
|||
icon: 'success', |
|||
confirmButtonText: 'OK' |
|||
}).then((result) => { |
|||
if (result.isConfirmed) { |
|||
window.location.href = 'home'; |
|||
} |
|||
}); |
|||
}, |
|||
error: function (xhr, status, error) { |
|||
console.error('Error updating complaint:', error); |
|||
let errorMessage = xhr.responseText ? xhr.responseText : 'An error occurred while updating the complaint.'; |
|||
Swal.fire({ |
|||
title: 'Error!', |
|||
text: errorMessage, |
|||
icon: 'error', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
</script> |
|||
<script th:src="@{/js/screenreader.js}"></script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,614 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>CheckList User</title> |
|||
<!-- Google Font --> |
|||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet"> |
|||
<!-- Bootstrap CSS --> |
|||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> |
|||
|
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> |
|||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" /> |
|||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> |
|||
<style> |
|||
/*rating style for css*/ |
|||
.rating { |
|||
direction: rtl; |
|||
display: inline-flex; |
|||
font-size: 24px; |
|||
} |
|||
|
|||
.rating input { |
|||
display: none; |
|||
} |
|||
|
|||
.rating label { |
|||
color: #ddd; |
|||
cursor: pointer; |
|||
margin: 0; |
|||
} |
|||
|
|||
.rating input:checked~label { |
|||
color: #414f69; |
|||
} |
|||
|
|||
.rating label:hover, |
|||
.rating label:hover~label { |
|||
color: #414f69; |
|||
} |
|||
|
|||
/* Style for resizing content */ |
|||
.resizable-content { |
|||
font-size: medium; |
|||
transition: font-size 0.3s ease; |
|||
} |
|||
|
|||
|
|||
.comment-table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
} |
|||
|
|||
.comment-table td { |
|||
padding: 0.5rem; |
|||
border: 1px solid #ddd; |
|||
/* Light border color */ |
|||
} |
|||
|
|||
.comment-table td:first-child { |
|||
width: 25%; |
|||
/* Adjust as needed for label column width */ |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.comment-table td:nth-child(2) { |
|||
width: 75%; |
|||
/* Adjust as needed for value column width */ |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<!-- Insert the header fragment --> |
|||
|
|||
<div th:insert="~{fragments/header :: headerFragment}"></div> |
|||
|
|||
<div class="container mt-4"> |
|||
<!-- Loader GIF --> |
|||
<div id="loader" style="display: none; text-align: center;"> |
|||
<img src="images/Thin broken ring.gif" alt="Loading..." /> |
|||
</div> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
<div class="mt-1"> |
|||
<div class="row align-items-center justify-content-center"> |
|||
<h4 class="title resizable-content" style="color: #414f69;">Check Complaint Status</h4> |
|||
</div> |
|||
</div> |
|||
<!-- Card Section --> |
|||
<div class="card p-3 resizable-content" style="margin-bottom: 8px;color: #414f69;"> |
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">1. Complaining Person's Details |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
<label>Complainant ID:</label> |
|||
<input type="text" id="complaintid" class="form-control" value="" disabled> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<label>Complainant Mobile Number:</label> |
|||
<input type="text" id="complaint-mobile-number" class="form-control" value="" disabled> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<label>Complainant Name:</label> |
|||
<input type="text" id="complaint-customer-name" class="form-control" value="" disabled> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">2. Complaint Location Details |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Street:</label> |
|||
<input type="text" id="street" class="form-control" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Zone:</label> |
|||
<input type="text" class="form-control" id="zone" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Ward:</label> |
|||
<input type="text" class="form-control" id="ward" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Area:</label> |
|||
<input type="text" class="form-control" id="area" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Locality:</label> |
|||
<input type="text" class="form-control" id="location" disabled> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">3. Complaint Type</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
<label>Complainant Type:</label> |
|||
<input type="text" class="form-control" id="complaintType" disabled> |
|||
</div> |
|||
|
|||
<!--<div class="col-md-4"> |
|||
Complainant Sub-Type: |
|||
<input type="text" class="form-control" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
Complainant Title: |
|||
<input type="text" class="form-control" disabled> |
|||
</div>--> |
|||
<div class="col-md-12"> |
|||
<label>Complainant Description:</label> |
|||
<input type="text" class="form-control" id="complaintdesc" disabled> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Complaint Action</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
|
|||
|
|||
<div class="col-md-4"> |
|||
<label>Complaint Closed Date:</label> |
|||
|
|||
<input type="text" class="form-control" id="commentDate" disabled> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<label>Official Name:</label> |
|||
<input type="text" class="form-control" id="commentuser" disabled> |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
<label>Status:</label> |
|||
<input type="text" class="form-control" id="statusshow" |
|||
style="background-color: #414f69;color: white;" disabled> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Complaint Action History</div> |
|||
<div class="card-body"> |
|||
<div id="comments-container"></div> <!-- Container for comments --> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<div class="card mb-3 resizable-content"> |
|||
<div class="card-header text-white" style="background-color: #414f69;">Update Complaint</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-6"> |
|||
<label>Change Complaint Status:</label> |
|||
<select class="form-control" name="complaintStatusID" id="complaintStatus"> |
|||
|
|||
</select> |
|||
</div> |
|||
|
|||
<div class="col-md-6" id="img" style="display: none;"> |
|||
<label>Photo:</label> |
|||
<input type="file" class="form-control" id="photo"> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
<label>Details of Complaint:</label> |
|||
<textarea class="form-control" id="othercomplaint-details" rows="3" |
|||
placeholder="Enter complaint details" maxlength="400" required></textarea> |
|||
</div> |
|||
</div> |
|||
<div class="row align-items-center justify-content-center" style="margin-top: 5px;"> |
|||
<button class="btn btn-sm" style="background-color: #414f69;color: white;">Submit</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
|
|||
<!--loader--> |
|||
<div id="loaders" style="display: none; text-align: center; margin-bottom: 50px;"> |
|||
<img src="images/Thin broken ring.gif" alt="Loading..." /> |
|||
</div> |
|||
<!--loader--> |
|||
|
|||
</div> |
|||
|
|||
|
|||
|
|||
<!-- Bootstrap JS --> |
|||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> |
|||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> |
|||
|
|||
<script> |
|||
|
|||
</script> |
|||
|
|||
|
|||
<!-- JavaScript to handle button clicks --> |
|||
<script> |
|||
|
|||
// /api/getComplaintDetailsbyId |
|||
$(document).ready(function () { |
|||
var urlParams = new URLSearchParams(window.location.search); |
|||
var complaintNo = urlParams.get('complaintno'); |
|||
|
|||
if (complaintNo) { |
|||
$('#loader').show(); |
|||
|
|||
$.ajax({ |
|||
url: '/pgr/api/getComplaintDetailsbyId', // Replace with your actual API endpoint |
|||
type: 'GET', |
|||
data: {complaintid: complaintNo}, |
|||
success: function (response) { |
|||
console.log('API response:', response); |
|||
$('#loader').hide(); |
|||
|
|||
if (Array.isArray(response) && response.length > 0) { |
|||
var complaintListHistory = response[0].ComplaintListHistory; |
|||
|
|||
if (Array.isArray(complaintListHistory) && complaintListHistory.length > 0) { |
|||
var complaintDetails = complaintListHistory[0]; |
|||
|
|||
// Decode HTML entities before populating fields |
|||
function decodeHtmlEntities(str) { |
|||
var txt = document.createElement('textarea'); |
|||
txt.innerHTML = str; |
|||
return txt.value; |
|||
} |
|||
|
|||
// Populate other form fields |
|||
document.getElementById("complaintid").value = complaintDetails.complaintid || ''; |
|||
document.getElementById("complaint-mobile-number").value = complaintDetails.CustomerMobile || ''; |
|||
document.getElementById("complaint-customer-name").value = complaintDetails.CustomerName || ''; |
|||
document.getElementById("street").value = complaintDetails.Street || ''; |
|||
document.getElementById("zone").value = complaintDetails.Zone || ''; |
|||
document.getElementById("ward").value = complaintDetails.Division || ''; |
|||
document.getElementById("area").value = complaintDetails.Area || ''; |
|||
document.getElementById("location").value = complaintDetails.Location || ''; |
|||
document.getElementById("complaintType").value = complaintDetails.complainttype || ''; |
|||
|
|||
// Decode and populate complaint description |
|||
document.getElementById("complaintdesc").value = decodeHtmlEntities(complaintDetails.complaintdetails || ''); |
|||
|
|||
document.getElementById("commentDate").value = complaintDetails.complaintcloseddate || ''; |
|||
document.getElementById("commentuser").value = complaintDetails.Officialname || ''; |
|||
let complaintCurrentStatus = complaintDetails.complaintcurrentstatus || ''; |
|||
|
|||
// Get the dropdown element |
|||
let complaintStatusDropdown = document.getElementById("complaintStatus"); |
|||
|
|||
// Function to add options to the select element |
|||
function addOption(text, value) { |
|||
let option = document.createElement("option"); |
|||
option.text = text; |
|||
option.value = value; |
|||
complaintStatusDropdown.add(option); |
|||
} |
|||
|
|||
// Check the current status and add the respective options |
|||
if (complaintCurrentStatus === 'REGISTERED') { |
|||
addOption('REGISTERED', '2'); |
|||
addOption('WITHDRAWN', '8'); |
|||
} else if (complaintCurrentStatus === 'WITHDRAWN') { |
|||
addOption('WITHDRAWN', '8'); |
|||
addOption('REOPENED', '9'); |
|||
} |
|||
else if (complaintCurrentStatus === 'CLOSE') { |
|||
addOption('CLOSE', '10'); |
|||
addOption('REOPENED', '9'); |
|||
} |
|||
else if (complaintCurrentStatus === 'REOPENED') { |
|||
addOption('REOPENED', '9'); |
|||
addOption('WITHDRAWN', '8'); |
|||
} |
|||
else if (complaintCurrentStatus === 'FINAL_CLOSURE') { |
|||
addOption('FINAL_CLOSURE', '11'); |
|||
addOption('REOPENED', '9'); |
|||
} |
|||
|
|||
document.getElementById("statusshow").value = complaintDetails.complaintcurrentstatus || ''; |
|||
|
|||
// Handle nested Comments array |
|||
if (Array.isArray(complaintDetails.Comments) && complaintDetails.Comments.length > 0) { |
|||
var commentsContainer = $('#comments-container'); |
|||
commentsContainer.empty(); // Clear the container |
|||
|
|||
var comments = complaintDetails.Comments; |
|||
var lastComment = comments[comments.length - 1]; |
|||
|
|||
// Decode HTML entities for last comment |
|||
function decodeHtmlEntities(str) { |
|||
var txt = document.createElement('textarea'); |
|||
txt.innerHTML = str; |
|||
return txt.value; |
|||
} |
|||
|
|||
// Convert image URL to Base64 |
|||
function convertImageToBase64(url, callback) { |
|||
var xhr = new XMLHttpRequest(); |
|||
xhr.onload = function () { |
|||
var reader = new FileReader(); |
|||
reader.onloadend = function () { |
|||
callback(reader.result); |
|||
}; |
|||
reader.readAsDataURL(xhr.response); |
|||
}; |
|||
xhr.open('GET', url); |
|||
xhr.responseType = 'blob'; |
|||
xhr.send(); |
|||
} |
|||
|
|||
// Compress the Base64 image using canvas |
|||
function compressImage(base64Str, callback, quality = 0.7, maxWidth = 800) { |
|||
var img = new Image(); |
|||
img.onload = function () { |
|||
var canvas = document.createElement('canvas'); |
|||
var ctx = canvas.getContext('2d'); |
|||
|
|||
var width = img.width; |
|||
var height = img.height; |
|||
|
|||
// Adjust the image size for compression |
|||
if (width > maxWidth) { |
|||
height *= maxWidth / width; |
|||
width = maxWidth; |
|||
} |
|||
|
|||
canvas.width = width; |
|||
canvas.height = height; |
|||
|
|||
// Draw the image onto the canvas |
|||
ctx.drawImage(img, 0, 0, width, height); |
|||
|
|||
// Convert the canvas back to Base64 with the specified quality (compression) |
|||
var compressedBase64 = canvas.toDataURL('image/jpeg', quality); |
|||
callback(compressedBase64); |
|||
}; |
|||
img.src = base64Str; |
|||
} |
|||
|
|||
// Handle last comment |
|||
var userImageHtml = ''; |
|||
if (Array.isArray(lastComment.userimages) && lastComment.userimages.length > 0) { |
|||
var imageUrl = lastComment.userimages[0].Comp_Image; |
|||
if (imageUrl && imageUrl !== 'null') { |
|||
convertImageToBase64(imageUrl, function (base64Image) { |
|||
compressImage(base64Image, function (compressedImage) { |
|||
userImageHtml = ` |
|||
<div class="col-md-12"> |
|||
<img src="${compressedImage}" alt="User Image" class="img-fluid"> |
|||
</div>`; |
|||
commentsContainer.append(lastCommentHtml); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
var lastCommentHtml = ` |
|||
<div class="row mb-3"> |
|||
<table class="comment-table"> |
|||
<tr> |
|||
<td style="color:#3c4959">User:</td> |
|||
<td>${lastComment.user || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Message:</td> |
|||
<td>${lastComment.message || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Comment Date:</td> |
|||
<td>${lastComment.comment_date || ''}</td> |
|||
</tr> |
|||
${userImageHtml} |
|||
</table> |
|||
</div> |
|||
<hr>`; |
|||
|
|||
// Add the comment section with the compressed Base64 image |
|||
commentsContainer.append(lastCommentHtml); |
|||
|
|||
// Add dropdown button for other comments |
|||
if (comments.length > 1) { |
|||
var dropdownHtml = ` |
|||
<button id="show-all-comments" class="btn mt-3" style="margin-bottom: 10px;background-color: #3c4959; color: white;">Show All Comments</button> |
|||
<div id="all-comments" style="display:none;">`; |
|||
|
|||
// Add all comments to dropdown |
|||
comments.forEach(function (comment, index) { |
|||
if (index < comments.length - 1) { // Skip the last comment |
|||
var userImageHtml = ''; |
|||
if (Array.isArray(comment.userimages) && comment.userimages.length > 0) { |
|||
var imageUrl = comment.userimages[0].Comp_Image; |
|||
if (imageUrl && imageUrl !== 'null') { |
|||
convertImageToBase64(imageUrl, function (base64Image) { |
|||
compressImage(base64Image, function (compressedImage) { |
|||
userImageHtml = ` |
|||
<div class="col-md-12"> |
|||
<img src="${compressedImage}" alt="User Image" class="img-fluid"> |
|||
</div>`; |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
dropdownHtml += ` |
|||
<div class="row mb-3"> |
|||
<table class="comment-table"> |
|||
<tr> |
|||
<td style="color:#3c4959">User:</td> |
|||
<td>${comment.user || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Message:</td> |
|||
<td>${comment.message || ''}</td> |
|||
</tr> |
|||
<tr> |
|||
<td style="color:#3c4959">Comment Date:</td> |
|||
<td>${comment.comment_date || ''}</td> |
|||
</tr> |
|||
${userImageHtml} |
|||
</table> |
|||
</div> |
|||
<hr>`; |
|||
} |
|||
}); |
|||
|
|||
dropdownHtml += '</div>'; |
|||
|
|||
commentsContainer.append(dropdownHtml); |
|||
|
|||
// Toggle display of all comments |
|||
$('#show-all-comments').click(function () { |
|||
$('#all-comments').toggle(); |
|||
$(this).text($(this).text() === 'Show All Comments' ? 'Hide Comments' : 'Show All Comments'); |
|||
}); |
|||
} |
|||
} else { |
|||
$('#comments-container').html('<p>No comments found.</p>'); |
|||
} |
|||
} else { |
|||
alert('No complaint details found in ComplaintListHistory.'); |
|||
} |
|||
} else { |
|||
alert('Unexpected response structure.'); |
|||
} |
|||
}, |
|||
error: function (xhr, status, error) { |
|||
console.log('Error:', xhr.responseText); |
|||
alert('An error occurred while fetching complaint details.'); |
|||
$('#loader').hide(); |
|||
} |
|||
}); |
|||
} else { |
|||
alert('No complaint number provided.'); |
|||
} |
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
$(document).ready(function () { |
|||
$('#complaintStatus').change(function () { |
|||
if ($(this).val() == '9') { // Show the image field if 'REOPENED' is selected |
|||
$('#img').show(); |
|||
} else { |
|||
$('#img').hide(); |
|||
} |
|||
}); |
|||
|
|||
$('.btn').click(function (e) { |
|||
e.preventDefault(); |
|||
|
|||
var complaintId = $('#complaintid').val(); |
|||
var complaintStatus = $('#complaintStatus').val(); |
|||
var message = $('#othercomplaint-details').val(); |
|||
|
|||
// Validate "Details of Complaint" field |
|||
if (message.trim() === "") { |
|||
Swal.fire({ |
|||
title: 'Validation Error!', |
|||
text: 'Please enter the details of the complaint.', |
|||
icon: 'warning', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
if (complaintStatus == '0') { |
|||
Swal.fire({ |
|||
title: 'Validation Error!', |
|||
text: 'Please select a valid complaint status.', |
|||
icon: 'warning', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
var formData = new FormData(); |
|||
formData.append("ComplaintId", complaintId); |
|||
formData.append("Message", message); |
|||
formData.append("Status", complaintStatus); |
|||
|
|||
var imageFile = $('#photo')[0].files[0]; |
|||
if (imageFile) { |
|||
formData.append("Comp_Image", imageFile); |
|||
} |
|||
$('#loaders').show(); |
|||
$.ajax({ |
|||
url: '/updateComplaint', |
|||
type: 'POST', |
|||
data: formData, |
|||
processData: false, |
|||
contentType: false, |
|||
success: function (response) { |
|||
$('#loaders').hide(); |
|||
console.log(imageFile, complaintId, message, complaintStatus); |
|||
Swal.fire({ |
|||
title: 'Success!', |
|||
text: 'Complaint updated successfully.', |
|||
icon: 'success', |
|||
confirmButtonText: 'OK' |
|||
}).then((result) => { |
|||
if (result.isConfirmed) { |
|||
window.location.href = 'home'; |
|||
} |
|||
}); |
|||
}, |
|||
error: function (xhr, status, error) { |
|||
console.error('Error updating complaint:', error); |
|||
$('#loaders').hide(); |
|||
let errorMessage = xhr.responseText ? xhr.responseText : 'An error occurred while updating the complaint.'; |
|||
Swal.fire({ |
|||
title: 'Error!', |
|||
text: errorMessage, |
|||
icon: 'error', |
|||
confirmButtonText: 'OK' |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
1538
src/main/resources/templates/staff/staffcomplaint.html
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,543 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>Main Page</title> |
|||
<!--Google Font --> |
|||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet"> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" |
|||
integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" |
|||
crossorigin="anonymous" referrerpolicy="no-referrer" /> |
|||
<!--Bootstrap CSS--> |
|||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> |
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> |
|||
<script th:src="@{/js/invertcolor.js}"></script> |
|||
<style> |
|||
body { |
|||
font-family: 'Lato', sans-serif; |
|||
background-color: white; |
|||
} |
|||
|
|||
body.inverted { |
|||
background-color: black; |
|||
color: white; |
|||
filter: invert(1); |
|||
} |
|||
|
|||
.card { |
|||
border-radius: 15px; |
|||
border: none; |
|||
padding: 20px; |
|||
background-color: #f0f4ff; |
|||
text-align: center; |
|||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100%; |
|||
/* Make sure cards take up full height of the column */ |
|||
} |
|||
|
|||
.card-body { |
|||
flex: 1; |
|||
/* Allow card-body to grow and take up available space */ |
|||
} |
|||
|
|||
.card-img { |
|||
width: 40px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.card-title { |
|||
font-size: 1.2rem; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.card-text { |
|||
color: #666; |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.card-footer { |
|||
font-weight: bold; |
|||
color: #007bff; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.card-footer i { |
|||
margin-left: 5px; |
|||
} |
|||
|
|||
.row { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.col-md-4 { |
|||
display: flex; |
|||
/* Ensure columns use Flexbox to distribute space evenly */ |
|||
padding: 0 15px; |
|||
/* Adjust padding to fit full width */ |
|||
} |
|||
|
|||
.container { |
|||
max-width: 1200px; |
|||
} |
|||
|
|||
.container .row .col-md-4 { |
|||
padding: 0 15px; |
|||
/* Adjust padding to fit full width */ |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.card { |
|||
flex-grow: 1; |
|||
/* Ensure all cards have the same height */ |
|||
} |
|||
|
|||
.container .row .col-md-12 { |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.card-link { |
|||
text-decoration: none; |
|||
/* Remove the underline from the link */ |
|||
color: inherit; |
|||
/* Inherit the text color */ |
|||
display: block; |
|||
/* Make the link cover the entire card */ |
|||
} |
|||
|
|||
.card-link:hover .card { |
|||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); |
|||
/* Optional: add hover effect */ |
|||
} |
|||
|
|||
/* Style for reader button */ |
|||
|
|||
|
|||
#reader-btn.active { |
|||
background-color: #28a745; |
|||
} |
|||
|
|||
/*header css*/ |
|||
.header { |
|||
background-color: #3b4858; |
|||
padding: 10px 20px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
color: white; |
|||
} |
|||
|
|||
.header .logo-left img { |
|||
height: 60px; |
|||
} |
|||
|
|||
.header .title { |
|||
text-align: center; |
|||
flex-grow: 1; |
|||
} |
|||
|
|||
.header .title h2 { |
|||
margin: 0; |
|||
font-size: 1.5rem; |
|||
} |
|||
|
|||
.header .logos-right img { |
|||
height: 60px; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
/*header buttons*/ |
|||
.header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
/* Space out the items within the header */ |
|||
} |
|||
|
|||
.header-buttons { |
|||
padding-left: 5px; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 10px; |
|||
/* Adjust spacing between buttons */ |
|||
} |
|||
|
|||
#reader-btn, |
|||
#invertColorBtn { |
|||
padding: 5px 10px; |
|||
font-size: 14px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
/*for radio button style*/ |
|||
.radio-group { |
|||
display: flex; |
|||
align-items: center; |
|||
/* Ensures all items are aligned to the center */ |
|||
} |
|||
|
|||
.radio-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-right: 20px; |
|||
/* Adds space between each radio item */ |
|||
} |
|||
|
|||
.radio-item input[type="radio"] { |
|||
margin-right: 10px; |
|||
/* Adds space between the radio button and its label */ |
|||
vertical-align: middle; |
|||
/* Ensures vertical alignment */ |
|||
} |
|||
|
|||
.radio-item label { |
|||
margin: 0; |
|||
/* Removes any default margins that might affect alignment */ |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
|
|||
<body> |
|||
<div th:insert="~{fragments/accessibility :: accessibility}"></div> |
|||
|
|||
|
|||
<!-- <div class="header"> |
|||
|
|||
<div class="logo-left"> |
|||
<img src="images/gcc1.png" alt="Greater Chennai Corporation Logo"> |
|||
</div> |
|||
<div class="header-buttons"> |
|||
<button id="reader-btn" onclick="toggleReader()">Enable Reader</button> |
|||
<button id="invertColorBtn" onclick="invertColors()">Invert Colors</button> |
|||
</div> |
|||
<div class="title"> |
|||
<h2>Public Grievance and Redressal System</h2> |
|||
</div> |
|||
<div class="logos-right"> |
|||
<img src="images/tamil.jpg" alt="Tamil Nadu Logo"> |
|||
<img src="images/bt-logo.png" style="width: 100px;height: 50px;" alt="bonton Logo"> |
|||
</div> |
|||
</div>--> |
|||
|
|||
|
|||
|
|||
<!-- Insert the header fragment --> |
|||
<div th:insert="~{fragments/header :: headerFragment}"></div> |
|||
<!--<button id="reader-btn" onclick="toggleReader()">Enable Reader</button>--> |
|||
|
|||
|
|||
<!-- Cards Section --> |
|||
<div class="container mt-3"> |
|||
<div id="loader" style="display: none; text-align: center;"> |
|||
<img src="images/Thin broken ring.gif" alt="Loading..." /> |
|||
</div> |
|||
<!--<button id="invertColorBtn" onclick="invertColors()">Invert Colors</button>--> |
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
<a href="staffcomplaint" class="card-link"> |
|||
<div class="card content"> |
|||
<div class="card-body"> |
|||
<img src="images/register.png" alt="register" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Register Your<br> Complaints</h5> |
|||
<p class="card-text">Register Complaints<br> Via Internet</p> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
Get Started <i class="fa fa-arrow-right"></i> |
|||
</div> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
<div class="card readable"> |
|||
<div class="card-body"> |
|||
<img src="images/phone-call.png" alt="Phone" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Register Complaint via Phone</h5> |
|||
<p class="card-text">Call 1913 to register your complaint.</p> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
Call Now <i class="fa fa-arrow-right"></i> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
<div class="card readable"> |
|||
<div class="card-body"> |
|||
<img src="images/contract.png" alt="Paper" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Complaint via Paper Form</h5> |
|||
<p class="card-text">Send your Grievance to<br><strong>The Commissioner,<br>Greater Chennai |
|||
Corporation,<br>Ripon Building, EVR Salai, Chennai - 600003.</strong></p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<!-- Form to Check by Complaint Number --> |
|||
<div class="col-md-12"> |
|||
<div class="card readable"> |
|||
<div class="card-body"> |
|||
<img src="images/search.png" alt="Status" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Check Your Complaint Status</h5> |
|||
<div class="form-group"> |
|||
<input type="radio" id="c_number" name="check_method" value="c_number" checked |
|||
onchange="toggleInput()"> |
|||
<label class="readable">Check By Complaint Number</label> |
|||
|
|||
<input type="radio" id="m_number" name="check_method" value="m_number" |
|||
onchange="toggleInput()"> |
|||
<label class="readable">Check By Mobile Number</label> |
|||
</div> |
|||
|
|||
<!-- Input for Complaint Number --> |
|||
<div class="form-group" id="complaint-number-input"> |
|||
<input type="text" class="form-control" id="complaint-number" |
|||
placeholder="Enter Complaint Number"> |
|||
</div> |
|||
|
|||
<!-- Input for Mobile Number (hidden by default) --> |
|||
<div class="form-group" id="complaint-mobile-input" style="display: none;"> |
|||
<input type="text" class="form-control" id="complaint-mobile" |
|||
placeholder="Enter Mobile Number"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card-footer"> |
|||
<!-- Button for Complaint Number --> |
|||
<button class="btn btn-danger" id="check-now-btn">Check Now <i |
|||
class="fa fa-arrow-right"></i></button> |
|||
|
|||
<!-- Button for Mobile Number (hidden by default) --> |
|||
<button class="btn btn-primary readable" id="check-mobile-btn" style="display: none;">Check Now <i |
|||
class="fa fa-arrow-right"></i></button> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<!-- Form to Check by Mobile Number --> |
|||
<!--<div class="col-md-6"> |
|||
<div class="card readable"> |
|||
<div class="card-body"> |
|||
<img src="images/search.png" alt="Status" class="card-img"> |
|||
<div class="card-content"> |
|||
<h5 class="card-title">Check Your Complaint Status</h5> |
|||
<div class="form-group"> |
|||
<label class="readable">Check By Mobile Number</label> |
|||
</div> |
|||
<div class="form-group"> |
|||
<input type="text" class="form-control" id="complaint-mobile" |
|||
placeholder="Enter Mobile Number"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<button class="btn btn-primary readable" id="check-mobile-btn">Check Now <i |
|||
class="fa fa-arrow-right"></i></button> |
|||
</div> |
|||
</div> |
|||
</div>--> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
</div> |
|||
|
|||
|
|||
|
|||
<div class="modal fade" id="complaintDetailsModal" tabindex="-1" role="dialog" |
|||
aria-labelledby="complaintDetailsLabel" aria-hidden="true"> |
|||
<div class="modal-dialog modal-lg" role="document"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header" style="background-color: #3c4959;color: white;"> |
|||
<h5 class="modal-title" id="complaintDetailsLabel">Complaint Details</h5> |
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="color: white;"> |
|||
<span aria-hidden="true">×</span> |
|||
</button> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<table class="table"> |
|||
<thead style="background-color: #3c4959;color: white;"> |
|||
<tr> |
|||
<th>Complaint Number</th> |
|||
<th>Complaint Type</th> |
|||
<th>View</th> <!-- New column for the eye icon --> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<!-- Dynamic rows will be appended here by JavaScript --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-secondary" data-dismiss="modal" |
|||
style="background-color:#3c4959 ;">Close</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
<div th:insert="~{fragments/footer :: footerFragment}"></div> |
|||
|
|||
<!-- Replace Slim jQuery with Full jQuery --> |
|||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> |
|||
|
|||
<!-- Popper.js (for Bootstrap tooltips, popovers, etc.) --> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script> |
|||
|
|||
<!-- Bootstrap JS --> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> |
|||
|
|||
<!-- Font Awesome for Icons --> |
|||
<script src="https://kit.fontawesome.com/a076d05399.js"></script> |
|||
|
|||
<script> |
|||
|
|||
|
|||
$(document).ready(function () { |
|||
// Check by complaint number |
|||
$('#check-now-btn').click(function () { |
|||
var complaintNumber = $('#complaint-number').val(); |
|||
if (complaintNumber) { |
|||
window.location.href = 'checklistofficer?complaintno=' + encodeURIComponent(complaintNumber); |
|||
} else { |
|||
alert('Please enter a complaint number.'); |
|||
} |
|||
}); |
|||
|
|||
|
|||
|
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
function toggleInput() { |
|||
const checkByComplaint = document.getElementById('c_number').checked; |
|||
const complaintInput = document.getElementById('complaint-number-input'); |
|||
const mobileInput = document.getElementById('complaint-mobile-input'); |
|||
const checkNowBtn = document.getElementById('check-now-btn'); |
|||
const checkMobileBtn = document.getElementById('check-mobile-btn'); |
|||
|
|||
if (checkByComplaint) { |
|||
complaintInput.style.display = 'block'; |
|||
checkNowBtn.style.display = 'block'; |
|||
mobileInput.style.display = 'none'; |
|||
checkMobileBtn.style.display = 'none'; |
|||
} else { |
|||
complaintInput.style.display = 'none'; |
|||
checkNowBtn.style.display = 'none'; |
|||
mobileInput.style.display = 'block'; |
|||
checkMobileBtn.style.display = 'block'; |
|||
} |
|||
} |
|||
|
|||
$(document).ready(function () { |
|||
// Trigger modal and load complaint list based on mobile number |
|||
$('#check-mobile-btn').on('click', function () { |
|||
const mobileNumber = $('#complaint-mobile').val(); // Get the entered mobile number |
|||
|
|||
if (mobileNumber === "") { |
|||
alert("Please enter a mobile number."); |
|||
return; |
|||
} |
|||
$('#loader').show(); |
|||
// AJAX request to fetch complaint details by mobile number |
|||
$.ajax({ |
|||
url: '/pgr/api/getComplaintDetailsByMobile', // Replace with your correct API path |
|||
method: 'GET', |
|||
data: {mobileNo: mobileNumber}, |
|||
success: function (response) { |
|||
$('#loader').hide(); |
|||
const complaintList = response[0]?.ComplaintListHistory; // Assuming data is inside ComplaintListHistory |
|||
|
|||
// Clear the table before adding new rows |
|||
const tableBody = $('#complaintDetailsModal tbody'); |
|||
tableBody.empty(); |
|||
|
|||
// Check if there are complaints |
|||
if (complaintList && complaintList.length > 0) { |
|||
// Loop through each complaint and add to the table |
|||
complaintList.forEach(function (complaint) { |
|||
const complaintId = complaint.complaintid || 'N/A'; // Fallback to N/A if complaintid is null |
|||
const complaintType = complaint.complainttype || 'N/A'; |
|||
|
|||
const newRow = ` |
|||
<tr> |
|||
<td>${complaintId}</td> |
|||
<td>${complaintType}</td> |
|||
|
|||
<td> |
|||
<button class="btn btn-link view-complaint" data-complaintid="${complaintId}"> |
|||
<i style="color:#3c4959" class="fa fa-eye"></i> |
|||
</button> |
|||
</td> |
|||
</tr>`; |
|||
tableBody.append(newRow); // Append the new row to the table |
|||
}); |
|||
} else { |
|||
// No complaints found for the mobile number |
|||
tableBody.append('<tr><td colspan="2">No complaints found for this mobile number.</td></tr>'); |
|||
} |
|||
|
|||
// Display the modal after fetching the data |
|||
$('#complaintDetailsModal').modal('show'); |
|||
}, |
|||
error: function (error) { |
|||
console.log('Error fetching complaints:', error); |
|||
alert('There was an error fetching the complaint details. Please try again.'); |
|||
$('#loader').hide(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
// Event delegation to handle click on dynamically generated eye icons |
|||
$(document).on('click', '.view-complaint', function () { |
|||
var complaintId = $(this).data('complaintid'); |
|||
if (complaintId) { |
|||
// Redirect to the complaint details page using complaintId |
|||
window.location.href = 'checklistofficer?complaintno=' + encodeURIComponent(complaintId); |
|||
} else { |
|||
alert('Complaint ID not found.'); |
|||
} |
|||
}); |
|||
|
|||
|
|||
}); |
|||
|
|||
</script> |
|||
<script th:src="@{/js/screenreader.js}"></script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,13 +0,0 @@ |
|||
package com.gcc.publicgrievance; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
|
|||
@SpringBootTest |
|||
class PublicgrievanceApplicationTests { |
|||
|
|||
@Test |
|||
void contextLoads() { |
|||
} |
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue