Browse Source

changes in toner management and rental management project api's and UI

master
yoganandhank 1 week ago
parent
commit
324075f7d6
  1. 94
      src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java
  2. 57
      src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java
  3. 10
      src/main/resources/templates/gccrentalmanagement/dashboard.html
  4. 409
      src/main/resources/templates/gccrentalmanagement/login.html
  5. 687
      src/main/resources/templates/gcctonermanagement/upload.html

94
src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java

@ -158,6 +158,15 @@ public class TonerController {
return tonerService.getActiveZones(); return tonerService.getActiveZones();
} }
@GetMapping("/api/vendors")
@ResponseBody
public List<Map<String, Object>> getVendors(HttpSession session) {
if (session.getAttribute("toner_usrName") == null) {
return java.util.Collections.emptyList();
}
return tonerService.getActiveVendors();
}
@PostMapping("/api/inward") @PostMapping("/api/inward")
@ResponseBody @ResponseBody
public Map<String, Object> uploadInward( public Map<String, Object> uploadInward(
@ -389,6 +398,91 @@ public class TonerController {
return response; return response;
} }
@PostMapping("/api/request/update-group")
@ResponseBody
public Map<String, Object> updateRequestGroup(
@RequestParam("itcellRefNo") String itcellRefNo,
@RequestParam(value = "newItcellRefNo", required = false) String newItcellRefNo,
@RequestParam("deptId") String deptId,
@RequestParam(value = "zoneId", required = false) String zoneId,
@RequestParam(value = "requestDate", required = false) String requestDate,
@RequestParam(value = "itemsJson", required = false) String itemsJson,
@RequestParam(value = "userRequestFile", required = false) MultipartFile userRequestFile,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String username = (String) session.getAttribute("toner_usrName");
if (username == null) {
response.put("success", false);
response.put("error", "Unauthorized access. Please log in.");
return response;
}
String updatedBy = appUserService.getUserIdByName(username);
if (updatedBy == null)
updatedBy = username;
if (itcellRefNo == null || itcellRefNo.trim().isEmpty()) {
response.put("success", false);
response.put("error", "IT Cell Reference No. is required.");
return response;
}
if (deptId == null || deptId.trim().isEmpty()) {
response.put("success", false);
response.put("error", "Department is required.");
return response;
}
if (userRequestFile != null && !userRequestFile.isEmpty() && !isPdfFile(userRequestFile)) {
response.put("success", false);
response.put("error", "User Request file must be a PDF.");
return response;
}
try {
List<Map<String, Object>> items = null;
if (itemsJson != null && !itemsJson.trim().isEmpty()) {
ObjectMapper mapper = new ObjectMapper();
items = mapper.readValue(itemsJson, new TypeReference<List<Map<String, Object>>>() {
});
}
File uploadDir = new File(tonerUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
String urPath = null;
if (userRequestFile != null && !userRequestFile.isEmpty()) {
urPath = saveUploadedFile(userRequestFile, "user_request", uploadDir);
}
String safeZoneId = (zoneId != null && !zoneId.trim().isEmpty()) ? zoneId.trim() : null;
boolean updated = tonerService.updateUserRequestGroup(
itcellRefNo.trim(),
(newItcellRefNo != null && !newItcellRefNo.trim().isEmpty()) ? newItcellRefNo.trim()
: itcellRefNo.trim(),
deptId.trim(), safeZoneId, requestDate, urPath, items, updatedBy);
if (updated) {
response.put("success", true);
response.put("message", "User request details updated successfully for all toners.");
} else {
if (urPath != null) {
deleteFileIfExists(urPath, uploadDir);
}
response.put("success", false);
response.put("error", "Failed to update request details in database.");
}
} catch (Exception e) {
response.put("success", false);
response.put("error", "An error occurred: " + e.getMessage());
e.printStackTrace();
}
return response;
}
@PostMapping("/api/outward") @PostMapping("/api/outward")
@ResponseBody @ResponseBody
public Map<String, Object> uploadOutward( public Map<String, Object> uploadOutward(

57
src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java

@ -136,6 +136,15 @@ public class TonerService {
return map; return map;
} }
public List<Map<String, Object>> getActiveVendors() {
try {
return jdbcTemplate.queryForList(
"SELECT id, vendor_name FROM payment_vendors_list WHERE is_active = 1 AND is_delete = 0 ORDER BY vendor_name ASC");
} catch (Exception e) {
return new ArrayList<>();
}
}
private Map<String, String> getWorkorderFileNumberMap() { private Map<String, String> getWorkorderFileNumberMap() {
Map<String, String> map = new HashMap<>(); Map<String, String> map = new HashMap<>();
try { try {
@ -770,6 +779,54 @@ public class TonerService {
username); username);
} }
@Transactional
public boolean updateUserRequestGroup(String itcellRefNo, String newItcellRefNo, String deptId, String zoneId,
String requestDate, String newUploadPath, List<Map<String, Object>> items, String username) {
Timestamp now = new Timestamp(System.currentTimeMillis());
try {
String safeZoneId = (zoneId != null && !zoneId.trim().isEmpty() && !"null".equalsIgnoreCase(zoneId))
? zoneId.trim()
: null;
String finalRequestDate = (requestDate != null && !requestDate.trim().isEmpty()) ? requestDate.trim()
: null;
String finalItcell = (newItcellRefNo != null && !newItcellRefNo.trim().isEmpty())
? newItcellRefNo.trim()
: itcellRefNo.trim();
if (newUploadPath != null && !newUploadPath.trim().isEmpty()) {
String sql = "UPDATE toner_department_requested_details SET " +
"itcell_ref_no = ?, dept_id = ?, zone_id = ?, request_date = ?, user_request_upload = ?, updated_date = ?, updated_by = ? "
+
"WHERE TRIM(itcell_ref_no) = TRIM(?)";
jdbcTemplate.update(sql, finalItcell, deptId.trim(), safeZoneId, finalRequestDate, newUploadPath, now,
username, itcellRefNo.trim());
} else {
String sql = "UPDATE toner_department_requested_details SET " +
"itcell_ref_no = ?, dept_id = ?, zone_id = ?, request_date = ?, updated_date = ?, updated_by = ? "
+
"WHERE TRIM(itcell_ref_no) = TRIM(?)";
jdbcTemplate.update(sql, finalItcell, deptId.trim(), safeZoneId, finalRequestDate, now, username,
itcellRefNo.trim());
}
// Update individual item quantities if provided
if (items != null && !items.isEmpty()) {
String updateItemSql = "UPDATE toner_department_requested_details SET qty_requested = ?, updated_date = ?, updated_by = ? WHERE id = ?";
for (Map<String, Object> item : items) {
Integer id = parseIntegerSafely(item.get("id"));
Integer qty = parseIntegerSafely(item.get("qty"));
if (id != null && qty != null && qty > 0) {
jdbcTemplate.update(updateItemSql, String.valueOf(qty), now, username, id);
}
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public List<Map<String, Object>> getAllUserRequests() { public List<Map<String, Object>> getAllUserRequests() {
try { try {
String sql = "SELECT * FROM toner_department_requested_details WHERE (is_active IS NULL OR is_active = 1) AND (is_delete IS NULL OR is_delete = 0) ORDER BY id DESC"; String sql = "SELECT * FROM toner_department_requested_details WHERE (is_active IS NULL OR is_active = 1) AND (is_delete IS NULL OR is_delete = 0) ORDER BY id DESC";

10
src/main/resources/templates/gccrentalmanagement/dashboard.html

@ -835,12 +835,14 @@
<div class="brand-titles"> <div class="brand-titles">
<h1>Greater Chennai Corporation</h1> <h1>Greater Chennai Corporation</h1>
<p>பெருநகர சென்னை மாநகராட்சி &bull; IT Cell</p> <p>பெருநகர சென்னை மாநகராட்சி &bull; IT Cell</p>
<div class="system-pill">
<i class="fa-solid fa-boxes-packing"></i>
<span>Rental Asset & Material Management Hub</span>
</div>
</div> </div>
</div> </div>
<div class="system-pill">
<i class="fa-solid fa-boxes-packing"></i>
<span style="font-size: 1.05rem; font-weight: 800; color: #ffffff;">Rental Asset & Material Management
Hub</span>
</div>
<div class="user-controls"> <div class="user-controls">
<div class="user-chip"> <div class="user-chip">

409
src/main/resources/templates/gccrentalmanagement/login.html

@ -13,355 +13,51 @@
<!-- Google Fonts --> <!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link
href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&family=Poppins:wght@300;400;500;600;700&display=swap"
rel="stylesheet">
<!-- Bootstrap CSS -->
<link rel="stylesheet" th:href="@{/assets/bootstrap-5.3.2/css/bootstrap.min.css}" type="text/css">
<!-- Include CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/login-form-styles.css}">
<!-- Awesome Font --> <!-- Awesome Font -->
<link th:href="@{/assets/font-awesome-6.5.2/css/all.min.css}" rel="stylesheet" type="text/css"> <link th:href="@{/assets/font-awesome-6.5.2/css/all.min.css}" rel="stylesheet" type="text/css">
<style>
:root {
--primary-brand: #0d4b68;
--primary-dark: #072e41;
--accent-cyan: #06b6d4;
--accent-amber: #f59e0b;
--gradient-bg: linear-gradient(135deg, #072e41 0%, #0d4b68 50%, #155e75 100%);
--card-bg: rgba(255, 255, 255, 0.96);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background: var(--gradient-bg);
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
position: relative;
overflow-x: hidden;
}
/* Decorative ambient glow circles */
.ambient-glow {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.25;
pointer-events: none;
z-index: 0;
}
.glow-1 {
width: 350px;
height: 350px;
background: #06b6d4;
top: -50px;
left: -50px;
}
.glow-2 {
width: 400px;
height: 400px;
background: #f59e0b;
bottom: -80px;
right: -80px;
}
.login-card {
position: relative;
z-index: 1;
width: 100%;
max-width: 460px;
background: var(--card-bg);
backdrop-filter: blur(12px);
border-radius: 24px;
padding: 40px 36px;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.2);
animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(25px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.header-branding {
text-align: center;
margin-bottom: 28px;
}
.logo-wrap {
width: 84px;
height: 84px;
margin: 0 auto 16px;
border-radius: 20px;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 20px rgba(13, 75, 104, 0.15);
padding: 10px;
transition: transform 0.3s ease;
}
.logo-wrap:hover {
transform: scale(1.05) rotate(2deg);
}
.logo-wrap img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.brand-title-en {
font-family: 'Outfit', sans-serif;
font-size: 1.15rem;
font-weight: 800;
letter-spacing: 0.8px;
color: #0d4b68;
text-transform: uppercase;
margin-bottom: 2px;
}
.brand-title-ta {
font-size: 0.82rem;
color: #475569;
font-weight: 600;
margin-bottom: 12px;
}
.badge-module {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
background: linear-gradient(135deg, #e0f2fe, #e2e8f0);
color: #0369a1;
border: 1px solid #bae6fd;
border-radius: 30px;
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-module i {
color: #0284c7;
}
.form-title {
font-family: 'Outfit', sans-serif;
font-size: 1.4rem;
font-weight: 700;
color: #0f172a;
margin: 18px 0 6px;
text-align: center;
}
.form-subtitle {
font-size: 0.85rem;
color: #64748b;
text-align: center;
margin-bottom: 24px;
}
.form-floating-custom {
position: relative;
margin-bottom: 20px;
}
.form-floating-custom .input-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
font-size: 1.1rem;
transition: color 0.3s ease;
pointer-events: none;
}
.form-floating-custom input {
width: 100%;
height: 52px;
padding: 12px 16px 12px 46px;
font-size: 0.92rem;
color: #1e293b;
background: #f8fafc;
border: 1.5px solid #e2e8f0;
border-radius: 12px;
outline: none;
transition: all 0.3s ease;
}
.form-floating-custom input:focus {
background: #ffffff;
border-color: #0284c7;
box-shadow: 0 0 0 4px rgba(2, 132, 199, 0.12);
}
.form-floating-custom input:focus + .input-icon {
color: #0284c7;
}
.btn-submit {
width: 100%;
height: 52px;
background: linear-gradient(135deg, #0d4b68 0%, #0284c7 100%);
color: #ffffff;
border: none;
border-radius: 12px;
font-family: 'Outfit', sans-serif;
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.5px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
transition: all 0.3s ease;
box-shadow: 0 8px 20px rgba(13, 75, 104, 0.25);
margin-top: 10px;
}
.btn-submit:hover:not(:disabled) {
background: linear-gradient(135deg, #093448 0%, #0369a1 100%);
box-shadow: 0 12px 25px rgba(13, 75, 104, 0.35);
transform: translateY(-2px);
}
.btn-submit:disabled {
opacity: 0.7;
cursor: not-allowed;
}
#error-box {
background: #fef2f2;
border: 1px solid #fecaca;
color: #b91c1c;
padding: 12px 16px;
border-radius: 10px;
font-size: 0.84rem;
font-weight: 500;
margin-bottom: 20px;
display: none;
align-items: center;
gap: 10px;
animation: shake 0.4s ease;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.system-badge-footer {
margin-top: 25px;
text-align: center;
font-size: 0.76rem;
color: #94a3b8;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.system-badge-footer span {
display: inline-block;
width: 6px;
height: 6px;
background: #22c55e;
border-radius: 50%;
box-shadow: 0 0 8px #22c55e;
}
.back-portal-link {
display: block;
text-align: center;
margin-top: 18px;
color: #0284c7;
text-decoration: none;
font-size: 0.85rem;
font-weight: 600;
transition: color 0.2s;
}
.back-portal-link:hover {
color: #0369a1;
text-decoration: underline;
}
</style>
</head> </head>
<body> <body>
<div class="ambient-glow glow-1"></div>
<div class="ambient-glow glow-2"></div>
<div class="login-card">
<div class="header-branding">
<div class="logo-wrap">
<img th:src="@{/assets/header-images/gcc1.png}" alt="Greater Chennai Corporation Logo">
</div>
<h2 class="brand-title-en">Greater Chennai Corporation</h2>
<p class="brand-title-ta">பெருநகர சென்னை மாநகராட்சி</p>
<div class="badge-module">
<i class="fa-solid fa-boxes-packing"></i>
<span>Rental Management System</span>
</div>
<div class="container">
<img th:src="@{/assets/header-images/gcc1.png}" alt="Greater Chennai Corporation" class="small-logo">
<h3>GREATER CHENNAI CORPORATION</h3>
<h4>பெருநகர சென்னை மாநகராட்சி</h4>
<br>
<h2>GCC IT Cell Rental Management System</h2>
<br>
<h2>Login</h2>
<div id="error-box"
style="color: #c0392b; font-weight: 500; font-size: 0.85rem; margin-bottom: 1.5rem; text-align: center; display: none;">
<i class="fa-solid fa-circle-exclamation"></i> <span id="error-text">Invalid username or password!</span>
</div> </div>
<h3 class="form-title">Portal Sign In</h3>
<p class="form-subtitle">Work Orders • Requests • Challans • Issue & Return</p>
<div id="error-box">
<i class="fa-solid fa-circle-exclamation fs-5"></i>
<span id="error-text">Invalid username or password!</span>
</div>
<form id="rentalLoginForm" onsubmit="handleRentalLogin(event)">
<div class="form-floating-custom">
<input type="text" name="usrName" id="usrName" placeholder="Enter Username / Employee ID" required autocomplete="username">
<i class="fa-solid fa-user input-icon"></i>
<form id="loginForm" onsubmit="handleRentalLogin(event)">
<div class="input-field">
<input type="text" name="usrName" id="usrName" placeholder="Enter Username" required
autocomplete="username">
<div class="underline"></div>
</div> </div>
<div class="form-floating-custom">
<input type="password" name="usrPass" id="usrPass" placeholder="Enter Password" required autocomplete="current-password">
<i class="fa-solid fa-lock input-icon"></i>
<div class="input-field">
<input type="password" name="usrPass" id="usrPass" placeholder="Enter Password" required
autocomplete="current-password">
<div class="underline"></div>
</div> </div>
<button type="submit" id="submit-btn" class="btn-submit">
<span>Sign In to Rental System</span>
<i class="fa-solid fa-arrow-right-to-bracket"></i>
</button>
<input type="submit" value="Login" id="submit-btn">
</form> </form>
<a th:href="@{/}" class="back-portal-link">
<i class="fa-solid fa-arrow-left me-1"></i> Return to Main GCC Home
</a>
<div class="system-badge-footer">
<span></span>
<span>GCC IT Cell Secured System &bull; Active Version 2.0</span>
</div>
</div> </div>
<!-- Footer Fragment -->
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
<!-- Custom JS --> <!-- Custom JS -->
<script th:inline="javascript"> <script th:inline="javascript">
const contextPath = /*[[@{/}]]*/ '/'; const contextPath = /*[[@{/}]]*/ '/';
@ -377,7 +73,7 @@
errorBox.style.display = "none"; errorBox.style.display = "none";
submitBtn.disabled = true; submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Authenticating...';
submitBtn.value = 'Authenticating...';
const username = document.getElementById("usrName").value.trim(); const username = document.getElementById("usrName").value.trim();
const password = document.getElementById("usrPass").value; const password = document.getElementById("usrPass").value;
@ -386,6 +82,8 @@
urlParams.append('usrName', username); urlParams.append('usrName', username);
urlParams.append('usrPass', password); urlParams.append('usrPass', password);
console.log(username, password);
// Fetch CSRF tokens if available // Fetch CSRF tokens if available
const csrfToken = document.querySelector("meta[name='_csrf']")?.getAttribute("content"); const csrfToken = document.querySelector("meta[name='_csrf']")?.getAttribute("content");
const csrfHeader = document.querySelector("meta[name='_csrf_header']")?.getAttribute("content"); const csrfHeader = document.querySelector("meta[name='_csrf_header']")?.getAttribute("content");
@ -402,28 +100,29 @@
headers: headers, headers: headers,
body: urlParams body: urlParams
}) })
.then(response => response.json())
.then(data => {
if (data.result) {
sessionStorage.setItem("rental_usrName", data.usrName);
sessionStorage.setItem("rental_usrRole", data.usrRole || "USER");
window.location.href = contextPath + 'gccrentalmanagement/dashboard';
} else {
.then(response => response.json())
.then(data => {
console.log(data);
if (data.result) {
sessionStorage.setItem("rental_usrName", data.usrName);
sessionStorage.setItem("rental_usrRole", data.usrRole || "USER");
window.location.href = contextPath + 'gccrentalmanagement/dashboard';
} else {
submitBtn.disabled = false;
submitBtn.value = 'Login';
errorText.textContent = data.error || "Authentication failed.";
errorBox.style.display = "block";
}
})
.catch(err => {
console.error("Login Error:", err);
submitBtn.disabled = false; submitBtn.disabled = false;
submitBtn.innerHTML = '<span>Sign In to Rental System</span> <i class="fa-solid fa-arrow-right-to-bracket"></i>';
errorText.textContent = data.error || "Authentication failed. Check your username and password.";
errorBox.style.display = "flex";
}
})
.catch(err => {
console.error("Login Error:", err);
submitBtn.disabled = false;
submitBtn.innerHTML = '<span>Sign In to Rental System</span> <i class="fa-solid fa-arrow-right-to-bracket"></i>';
errorText.textContent = "Network error. Please try again later.";
errorBox.style.display = "flex";
});
submitBtn.value = 'Login';
errorText.textContent = "Network error. Please try again later.";
errorBox.style.display = "block";
});
} }
</script> </script>
</body> </body>
</html>
</html>

687
src/main/resources/templates/gcctonermanagement/upload.html
File diff suppressed because it is too large
View File

Loading…
Cancel
Save