32 changed files with 2279 additions and 2 deletions
-
BINgcc_manpower_uploads/workorderfiles/1011 WO.pdf
-
BINgcc_manpower_uploads/workorderfiles/485 - 2025 WO 1.pdf
-
BINgcc_manpower_uploads/workorderfiles/618 Extension WO.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C111802025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C114642025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C11702026.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C12182025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C124102025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C12472026.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C126592025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C12852026.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C132302025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C136782025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C137392025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C139982025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C145902023.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C14852025.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C15962026.pdf
-
BINgcc_manpower_uploads/workorderfiles/I.T.C.C.No.C16182025.pdf
-
BINgcc_manpower_uploads/workorderfiles/WO (4).pdf
-
7pom.xml
-
2src/main/java/com/gcc_manpower/GccManpowerApplication.java
-
10src/main/java/com/gcc_manpower/config/DataSourceConfig.java
-
8src/main/java/com/gcc_manpower/config/WebConfig.java
-
192src/main/java/com/gcc_manpower/controller/GccServiceDocumentController.java
-
180src/main/java/com/gcc_manpower/service/GccServiceDocumentService.java
-
98src/main/java/com/gcc_manpower/service/VideoCompressionService.java
-
4src/main/resources/application-local.properties
-
4src/main/resources/application-prod.properties
-
22src/main/resources/templates/fragments/documentsheader.html
-
637src/main/resources/templates/gccservicedocuments/upload.html
-
1115src/main/resources/templates/gccservicedocuments/view.html
@ -0,0 +1,192 @@ |
|||||
|
package com.gcc_manpower.controller; |
||||
|
|
||||
|
import com.gcc_manpower.service.GccServiceDocumentService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.stereotype.Controller; |
||||
|
import org.springframework.ui.Model; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import jakarta.servlet.http.HttpSession; |
||||
|
import java.io.File; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
@Controller |
||||
|
@RequestMapping("/gccservicedocuments") |
||||
|
public class GccServiceDocumentController { |
||||
|
|
||||
|
@Autowired |
||||
|
private GccServiceDocumentService documentService; |
||||
|
|
||||
|
@Autowired |
||||
|
private com.gcc_manpower.service.VideoCompressionService videoCompressionService; |
||||
|
|
||||
|
@Value("${document.upload.path:gccservicedocuments}") |
||||
|
private String documentUploadPathConfig; |
||||
|
|
||||
|
@GetMapping({ "", "/" }) |
||||
|
public String viewPortal(Model model, HttpSession session) { |
||||
|
// String usrName = (String) session.getAttribute("usrName"); |
||||
|
// String usrRole = (String) session.getAttribute("usrRole"); |
||||
|
|
||||
|
// boolean isLoggedIn = (usrName != null); |
||||
|
// boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); |
||||
|
|
||||
|
model.addAttribute("isLoggedIn", true); |
||||
|
model.addAttribute("isAdmin", true); |
||||
|
model.addAttribute("usrName", "Guest"); |
||||
|
model.addAttribute("curPage", "gccservicedocuments"); |
||||
|
|
||||
|
return "gccservicedocuments/upload"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/view") |
||||
|
public String viewReportPage(Model model, HttpSession session) { |
||||
|
// String usrName = (String) session.getAttribute("usrName"); |
||||
|
// if (usrName == null) { |
||||
|
// return "redirect:/login"; |
||||
|
// } |
||||
|
// String usrRole = (String) session.getAttribute("usrRole"); |
||||
|
// boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); |
||||
|
|
||||
|
model.addAttribute("isLoggedIn", true); |
||||
|
model.addAttribute("isAdmin", true); |
||||
|
model.addAttribute("usrName", "Guest"); |
||||
|
model.addAttribute("curPage", "gccservicedocuments_upload"); |
||||
|
|
||||
|
return "gccservicedocuments/view"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/api/list") |
||||
|
@ResponseBody |
||||
|
public List<Map<String, Object>> listDocuments( |
||||
|
@RequestParam(value = "category", required = false) String category, |
||||
|
@RequestParam(value = "fromDate", required = false) String fromDate, |
||||
|
@RequestParam(value = "toDate", required = false) String toDate) { |
||||
|
return documentService.searchDocuments(category, fromDate, toDate); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/api/categories") |
||||
|
@ResponseBody |
||||
|
public List<String> getCategories() { |
||||
|
return documentService.getAllCategories(); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/api/file-types") |
||||
|
@ResponseBody |
||||
|
public List<Map<String, Object>> getFileTypes() { |
||||
|
return documentService.getAllFileTypes(); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/api/upload") |
||||
|
@ResponseBody |
||||
|
public Map<String, Object> uploadFile( |
||||
|
@RequestParam("title") String title, |
||||
|
@RequestParam("category") String category, |
||||
|
@RequestParam("fileType") String fileType, |
||||
|
@RequestParam("file") MultipartFile file, |
||||
|
HttpSession session) { |
||||
|
|
||||
|
Map<String, Object> response = new HashMap<>(); |
||||
|
// String usrName = (String) session.getAttribute("usrName"); |
||||
|
// if (usrName == null) { |
||||
|
// response.put("success", false); |
||||
|
// response.put("error", "Unauthorized access! Please log in."); |
||||
|
// return response; |
||||
|
// } |
||||
|
|
||||
|
if (file.isEmpty()) { |
||||
|
response.put("success", false); |
||||
|
response.put("error", "Please select a file to upload."); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// Clean category name for folder names (letters, numbers, underscores, dashes, |
||||
|
// spaces) |
||||
|
String cleanCategory = category.trim().replaceAll("[^a-zA-Z0-9-_ ]", "").trim(); |
||||
|
if (cleanCategory.isEmpty()) { |
||||
|
cleanCategory = "General"; |
||||
|
} |
||||
|
|
||||
|
// Build the folder directory path: [documentUploadPathConfig]/[cleanCategory]/ |
||||
|
File uploadDir = new File(documentUploadPathConfig, cleanCategory).getAbsoluteFile(); |
||||
|
if (!uploadDir.exists()) { |
||||
|
uploadDir.mkdirs(); |
||||
|
} |
||||
|
|
||||
|
// Generate unique filename to avoid overwrites |
||||
|
String originalFilename = file.getOriginalFilename(); |
||||
|
String nameWithoutExt = ""; |
||||
|
String ext = ""; |
||||
|
if (originalFilename != null && originalFilename.contains(".")) { |
||||
|
int dotIdx = originalFilename.lastIndexOf("."); |
||||
|
nameWithoutExt = originalFilename.substring(0, dotIdx); |
||||
|
ext = originalFilename.substring(dotIdx); |
||||
|
} else { |
||||
|
nameWithoutExt = originalFilename != null ? originalFilename : "file"; |
||||
|
} |
||||
|
|
||||
|
nameWithoutExt = nameWithoutExt.replaceAll("[^a-zA-Z0-9-_]", "_"); |
||||
|
String savedFilename = nameWithoutExt + "_" + System.currentTimeMillis() + ext; |
||||
|
|
||||
|
File destFile = new File(uploadDir, savedFilename).getAbsoluteFile(); |
||||
|
file.transferTo(destFile); |
||||
|
|
||||
|
// Save Web URL in database: |
||||
|
// "/gccservicedocuments/files/[category]/[savedFilename]" |
||||
|
String fileWebUrl = "/gccservicedocuments/files/" + cleanCategory + "/" + savedFilename; |
||||
|
|
||||
|
boolean saved = documentService.saveDocument( |
||||
|
title.trim(), |
||||
|
cleanCategory, |
||||
|
fileType, |
||||
|
fileWebUrl, |
||||
|
originalFilename); |
||||
|
|
||||
|
if (saved) { |
||||
|
if ("video".equalsIgnoreCase(fileType)) { |
||||
|
videoCompressionService.compressVideoAsync(destFile); |
||||
|
} |
||||
|
response.put("success", true); |
||||
|
response.put("message", "File uploaded and registered successfully."); |
||||
|
} else { |
||||
|
// Remove saved file on DB insert failure |
||||
|
destFile.delete(); |
||||
|
response.put("success", false); |
||||
|
response.put("error", "Failed to record document details in the database."); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
response.put("success", false); |
||||
|
response.put("error", "File upload failed: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/api/delete") |
||||
|
@ResponseBody |
||||
|
public Map<String, Object> deleteFile(@RequestParam("id") Long id, HttpSession session) { |
||||
|
Map<String, Object> response = new HashMap<>(); |
||||
|
// String usrName = (String) session.getAttribute("usrName"); |
||||
|
// if (usrName == null) { |
||||
|
// response.put("success", false); |
||||
|
// response.put("error", "Unauthorized access! Please log in."); |
||||
|
// return response; |
||||
|
// } |
||||
|
|
||||
|
boolean deleted = documentService.deleteDocument(id); |
||||
|
if (deleted) { |
||||
|
response.put("success", true); |
||||
|
response.put("message", "File record removed successfully."); |
||||
|
} else { |
||||
|
response.put("success", false); |
||||
|
response.put("error", "Failed to delete the file record."); |
||||
|
} |
||||
|
|
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,180 @@ |
|||||
|
package com.gcc_manpower.service; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Qualifier; |
||||
|
import org.springframework.jdbc.core.JdbcTemplate; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import javax.sql.DataSource; |
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
@Service |
||||
|
public class GccServiceDocumentService { |
||||
|
|
||||
|
private JdbcTemplate jdbcTemplate; |
||||
|
|
||||
|
@Autowired |
||||
|
public void setDataSource(@Qualifier("mysqlServiceDocumentsDataSource") DataSource dataSource) { |
||||
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
||||
|
} |
||||
|
|
||||
|
private int getOrCreateCategory(String categoryName) { |
||||
|
// Try parsing as ID first |
||||
|
try { |
||||
|
int id = Integer.parseInt(categoryName.trim()); |
||||
|
String checkIdSql = "SELECT id FROM category_list WHERE id = ? AND is_delete = 0"; |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(checkIdSql, Integer.class, id); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (NumberFormatException e) { |
||||
|
// Not a number, fallback to name matching |
||||
|
} |
||||
|
|
||||
|
String selectSql = "SELECT id FROM category_list WHERE category_name = ? AND is_delete = 0"; |
||||
|
try { |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(selectSql, Integer.class, categoryName.trim()); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error checking category: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
// Insert new category |
||||
|
String insertSql = "INSERT INTO category_list (category_name, is_active, is_delete) VALUES (?, 1, 0)"; |
||||
|
try { |
||||
|
jdbcTemplate.update(insertSql, categoryName.trim()); |
||||
|
// Fetch the generated ID |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(selectSql, Integer.class, categoryName.trim()); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error creating category: " + e.getMessage()); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
private int getOrCreateFileType(String fileTypeName) { |
||||
|
// Try parsing as ID first |
||||
|
try { |
||||
|
int id = Integer.parseInt(fileTypeName.trim()); |
||||
|
String checkIdSql = "SELECT id FROM upload_file_types WHERE id = ? AND is_delete = 0"; |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(checkIdSql, Integer.class, id); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (NumberFormatException e) { |
||||
|
// Not a number, fallback to name matching |
||||
|
} |
||||
|
|
||||
|
String selectSql = "SELECT id FROM upload_file_types WHERE file_type_name = ? AND is_delete = 0"; |
||||
|
try { |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(selectSql, Integer.class, fileTypeName.trim()); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error checking file type: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
// Insert new file type |
||||
|
String insertSql = "INSERT INTO upload_file_types (file_type_name, is_active, is_delete) VALUES (?, 1, 0)"; |
||||
|
try { |
||||
|
jdbcTemplate.update(insertSql, fileTypeName.trim()); |
||||
|
// Fetch the generated ID |
||||
|
List<Integer> ids = jdbcTemplate.queryForList(selectSql, Integer.class, fileTypeName.trim()); |
||||
|
if (!ids.isEmpty()) { |
||||
|
return ids.get(0); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error creating file type: " + e.getMessage()); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public boolean saveDocument(String title, String category, String fileType, String filePath, |
||||
|
String originalFilename) { |
||||
|
int categoryId = getOrCreateCategory(category); |
||||
|
int fileTypeId = getOrCreateFileType(fileType); |
||||
|
|
||||
|
String sql = "INSERT INTO documents_uploads (title, category, file_type, file_path, original_filename, is_active, is_delete) " |
||||
|
+ |
||||
|
"VALUES (?, ?, ?, ?, ?, 1, 0)"; |
||||
|
try { |
||||
|
int rows = jdbcTemplate.update(sql, title, String.valueOf(categoryId), String.valueOf(fileTypeId), filePath, |
||||
|
originalFilename); |
||||
|
return rows > 0; |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error saving service document: " + e.getMessage()); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public List<Map<String, Object>> searchDocuments(String category, String fromDate, String toDate) { |
||||
|
StringBuilder sql = new StringBuilder( |
||||
|
"SELECT d.id, d.title, c.category_name AS category, f.file_type_name AS file_type, d.file_path, d.original_filename, d.upload_date " |
||||
|
+ |
||||
|
"FROM documents_uploads d " + |
||||
|
"LEFT JOIN category_list c ON d.category = c.id " + |
||||
|
"LEFT JOIN upload_file_types f ON d.file_type = f.id " + |
||||
|
"WHERE d.is_active = 1 AND d.is_delete = 0"); |
||||
|
List<Object> params = new ArrayList<>(); |
||||
|
|
||||
|
if (category != null && !category.trim().isEmpty()) { |
||||
|
sql.append(" AND c.category_name = ?"); |
||||
|
params.add(category.trim()); |
||||
|
} |
||||
|
if (fromDate != null && !fromDate.trim().isEmpty()) { |
||||
|
sql.append(" AND DATE(d.upload_date) >= ?"); |
||||
|
params.add(fromDate.trim()); |
||||
|
} |
||||
|
if (toDate != null && !toDate.trim().isEmpty()) { |
||||
|
sql.append(" AND DATE(d.upload_date) <= ?"); |
||||
|
params.add(toDate.trim()); |
||||
|
} |
||||
|
|
||||
|
sql.append(" ORDER BY d.category "); |
||||
|
|
||||
|
try { |
||||
|
return jdbcTemplate.queryForList(sql.toString(), params.toArray()); |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error searching service documents: " + e.getMessage()); |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean deleteDocument(Long id) { |
||||
|
String sql = "UPDATE documents_uploads SET is_active = 0, is_delete = 1 WHERE id = ?"; |
||||
|
try { |
||||
|
int rows = jdbcTemplate.update(sql, id); |
||||
|
return rows > 0; |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error deleting service document ID " + id + ": " + e.getMessage()); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public List<String> getAllCategories() { |
||||
|
String sql = "SELECT category_name FROM category_list WHERE is_active = 1 AND is_delete = 0 ORDER BY category_name"; |
||||
|
try { |
||||
|
return jdbcTemplate.queryForList(sql, String.class); |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error fetching document categories: " + e.getMessage()); |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public List<Map<String, Object>> getAllFileTypes() { |
||||
|
String sql = "SELECT id, file_type_name FROM upload_file_types WHERE is_active = 1 AND is_delete = 0 ORDER BY file_type_name"; |
||||
|
try { |
||||
|
return jdbcTemplate.queryForList(sql); |
||||
|
} catch (Exception e) { |
||||
|
System.err.println("Error fetching file types: " + e.getMessage()); |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
package com.gcc_manpower.service; |
||||
|
|
||||
|
import org.springframework.scheduling.annotation.Async; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import ws.schild.jave.Encoder; |
||||
|
import ws.schild.jave.MultimediaObject; |
||||
|
import ws.schild.jave.encode.AudioAttributes; |
||||
|
import ws.schild.jave.encode.EncodingAttributes; |
||||
|
import ws.schild.jave.encode.VideoAttributes; |
||||
|
import ws.schild.jave.info.AudioInfo; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.util.logging.Level; |
||||
|
import java.util.logging.Logger; |
||||
|
|
||||
|
@Service |
||||
|
public class VideoCompressionService { |
||||
|
|
||||
|
private static final Logger LOGGER = Logger.getLogger(VideoCompressionService.class.getName()); |
||||
|
|
||||
|
@Async |
||||
|
public void compressVideoAsync(File originalFile) { |
||||
|
if (originalFile == null || !originalFile.exists() || !originalFile.isFile()) { |
||||
|
LOGGER.warning("Invalid file provided for compression."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
String originalPath = originalFile.getAbsolutePath(); |
||||
|
long originalSize = originalFile.length(); |
||||
|
LOGGER.info("Starting background video compression for file: " + originalPath + " (Size: " + (originalSize / (1024 * 1024)) + " MB)"); |
||||
|
|
||||
|
// Create a temporary file in the same directory for the compressed output |
||||
|
File tempFile = new File(originalFile.getParentFile(), "temp_" + System.currentTimeMillis() + "_" + originalFile.getName()); |
||||
|
|
||||
|
try { |
||||
|
// Read input file details |
||||
|
MultimediaObject mediaObject = new MultimediaObject(originalFile); |
||||
|
AudioInfo audioInfo = mediaObject.getInfo().getAudio(); |
||||
|
|
||||
|
// Set up video encoding attributes |
||||
|
VideoAttributes video = new VideoAttributes(); |
||||
|
video.setCodec("h264"); // Standard widely supported web codec |
||||
|
video.setBitRate(1500000); // Target 1.5 Mbps for compression |
||||
|
video.setFrameRate(24); |
||||
|
|
||||
|
EncodingAttributes attrs = new EncodingAttributes(); |
||||
|
attrs.setOutputFormat("mp4"); |
||||
|
attrs.setVideoAttributes(video); |
||||
|
|
||||
|
// Set up audio encoding attributes if audio stream exists |
||||
|
if (audioInfo != null) { |
||||
|
AudioAttributes audio = new AudioAttributes(); |
||||
|
audio.setCodec("aac"); |
||||
|
audio.setBitRate(128000); // Target 128 kbps audio |
||||
|
audio.setChannels(2); |
||||
|
audio.setSamplingRate(44100); |
||||
|
attrs.setAudioAttributes(audio); |
||||
|
} |
||||
|
|
||||
|
// Encode/Compress |
||||
|
Encoder encoder = new Encoder(); |
||||
|
encoder.encode(mediaObject, tempFile, attrs); |
||||
|
|
||||
|
// Replace original file with compressed file if successful |
||||
|
if (tempFile.exists() && tempFile.length() > 0) { |
||||
|
long compressedSize = tempFile.length(); |
||||
|
|
||||
|
// Delete original file |
||||
|
boolean deletedOriginal = originalFile.delete(); |
||||
|
if (!deletedOriginal) { |
||||
|
LOGGER.warning("Failed to delete the original uncompressed file. Trying to force replace."); |
||||
|
} |
||||
|
|
||||
|
// Rename temp file to original file name |
||||
|
boolean renamed = tempFile.renameTo(originalFile); |
||||
|
if (renamed) { |
||||
|
long sizeReduction = originalSize - compressedSize; |
||||
|
LOGGER.info("Compression complete! Saved to: " + originalPath); |
||||
|
LOGGER.info(String.format("File size reduced by %.2f%% (from %d MB to %d MB)", |
||||
|
((double) sizeReduction / originalSize) * 100, |
||||
|
originalSize / (1024 * 1024), |
||||
|
compressedSize / (1024 * 1024))); |
||||
|
} else { |
||||
|
LOGGER.severe("Failed to rename temp compressed file to original filename: " + originalPath); |
||||
|
} |
||||
|
} else { |
||||
|
LOGGER.severe("Compression failed: Temp file was not created or is empty."); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
LOGGER.log(Level.SEVERE, "Exception occurred during video compression for file: " + originalPath, e); |
||||
|
// Cleanup temp file in case of failure |
||||
|
if (tempFile.exists()) { |
||||
|
tempFile.delete(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
<th:block th:fragment="documentsheader"> |
||||
|
<!-- Awesome Font --> |
||||
|
<link rel="stylesheet" th:href="@{/assets/font-awesome-6.5.2/css/all.min.css}" type="text/css"> |
||||
|
<!-- Bootstrap CSS --> |
||||
|
<link rel="stylesheet" th:href="@{/assets/bootstrap-5.3.2/css/bootstrap.min.css}" type="text/css"> |
||||
|
<!-- CSS for Header --> |
||||
|
<link rel="stylesheet" th:href="@{/assets/css/header-styles.css}" type="text/css"> |
||||
|
<div class="header"> |
||||
|
<div class="logo-left"> |
||||
|
<img th:src="@{/assets/header-images/gcc1.png}" alt="Greater Chennai Corporation Logo"> |
||||
|
<div class="logo-text"> |
||||
|
<h2>GREATER</h2> |
||||
|
<h2>CHENNAI</h2> |
||||
|
<h2>CORPORATION</h2> |
||||
|
<p>பெருநகர சென்னை மாநகராட்சி</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="title"> |
||||
|
<h2>GCC Training Material Services Details</h2> |
||||
|
</div> |
||||
|
</div> |
||||
|
</th:block> |
||||
@ -0,0 +1,637 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html xmlns:th="http://www.thymeleaf.org" lang="en"> |
||||
|
|
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>GCC Documents Upload Portal</title> |
||||
|
|
||||
|
<!-- CSRF Tokens --> |
||||
|
<meta th:if="${_csrf != null}" name="_csrf" th:content="${_csrf.token}" /> |
||||
|
<meta th:if="${_csrf != null}" name="_csrf_header" th:content="${_csrf.headerName}" /> |
||||
|
|
||||
|
<!-- Header Fragment --> |
||||
|
<th:block th:replace="~{fragments/documentsheader :: documentsheader}"></th:block> |
||||
|
|
||||
|
<!-- Google Fonts --> |
||||
|
<link |
||||
|
href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Poppins:wght@300;400;500;600;700&display=swap" |
||||
|
rel="stylesheet"> |
||||
|
|
||||
|
<!-- Styles --> |
||||
|
<style> |
||||
|
body { |
||||
|
background-color: #f4f7f6; |
||||
|
font-family: 'Poppins', sans-serif; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.main-container { |
||||
|
max-width: 800px; |
||||
|
margin: 40px auto; |
||||
|
padding: 0 20px; |
||||
|
} |
||||
|
|
||||
|
.page-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
.page-title { |
||||
|
font-family: 'Outfit', sans-serif; |
||||
|
font-size: 2rem; |
||||
|
font-weight: 700; |
||||
|
color: #0f5c78; |
||||
|
margin: 0; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 12px; |
||||
|
} |
||||
|
|
||||
|
.btn-back { |
||||
|
background-color: #ffffff; |
||||
|
color: #475569; |
||||
|
font-weight: 600; |
||||
|
border: 1.5px solid #cbd5e1; |
||||
|
padding: 10px 20px; |
||||
|
border-radius: 30px; |
||||
|
transition: all 0.3s ease; |
||||
|
text-decoration: none; |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
gap: 8px; |
||||
|
} |
||||
|
|
||||
|
.btn-back:hover { |
||||
|
background-color: #f1f5f9; |
||||
|
color: #0f5c78; |
||||
|
border-color: #0f5c78; |
||||
|
} |
||||
|
|
||||
|
.upload-card { |
||||
|
background: #ffffff; |
||||
|
border-radius: 20px; |
||||
|
border: 1px solid rgba(15, 92, 120, 0.08); |
||||
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.04); |
||||
|
padding: 40px; |
||||
|
} |
||||
|
|
||||
|
.form-label { |
||||
|
font-size: 0.9rem; |
||||
|
font-weight: 600; |
||||
|
color: #475569; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.form-control-custom { |
||||
|
height: 48px; |
||||
|
border-radius: 10px; |
||||
|
border: 1.5px solid #e2e8f0; |
||||
|
padding: 10px 16px; |
||||
|
font-size: 0.95rem; |
||||
|
font-weight: 500; |
||||
|
color: #334155; |
||||
|
background-color: #f8fafc; |
||||
|
transition: all 0.3s ease; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.form-control-custom:focus { |
||||
|
outline: none; |
||||
|
border-color: #0f5c78; |
||||
|
background-color: #ffffff; |
||||
|
box-shadow: 0 0 0 3px rgba(15, 92, 120, 0.15); |
||||
|
} |
||||
|
|
||||
|
.form-group { |
||||
|
margin-bottom: 24px; |
||||
|
} |
||||
|
|
||||
|
.category-select-group { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 12px; |
||||
|
} |
||||
|
|
||||
|
/* Drag & Drop Zone */ |
||||
|
.dropzone-container { |
||||
|
border: 2px dashed #cbd5e1; |
||||
|
border-radius: 16px; |
||||
|
padding: 40px 20px; |
||||
|
text-align: center; |
||||
|
background-color: #f8fafc; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s ease; |
||||
|
position: relative; |
||||
|
margin-bottom: 24px; |
||||
|
} |
||||
|
|
||||
|
.dropzone-container:hover, |
||||
|
.dropzone-container.dragover { |
||||
|
border-color: #0f5c78; |
||||
|
background-color: rgba(15, 92, 120, 0.03); |
||||
|
} |
||||
|
|
||||
|
.dropzone-icon { |
||||
|
font-size: 3rem; |
||||
|
color: #94a3b8; |
||||
|
margin-bottom: 16px; |
||||
|
transition: color 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.dropzone-container:hover .dropzone-icon { |
||||
|
color: #0f5c78; |
||||
|
} |
||||
|
|
||||
|
.dropzone-text { |
||||
|
font-size: 1rem; |
||||
|
color: #475569; |
||||
|
font-weight: 500; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.dropzone-hint { |
||||
|
font-size: 0.8rem; |
||||
|
color: #94a3b8; |
||||
|
} |
||||
|
|
||||
|
.file-input-hidden { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
opacity: 0; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.selected-file-banner { |
||||
|
background-color: #f0fdf4; |
||||
|
border: 1px solid #bbf7d0; |
||||
|
border-radius: 10px; |
||||
|
padding: 12px 18px; |
||||
|
margin-bottom: 24px; |
||||
|
display: none; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.selected-file-details { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 12px; |
||||
|
color: #166534; |
||||
|
font-size: 0.9rem; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.btn-remove-file { |
||||
|
background: none; |
||||
|
border: none; |
||||
|
color: #b91c1c; |
||||
|
font-size: 1.1rem; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
/* Progress Bar */ |
||||
|
.progress-container { |
||||
|
display: none; |
||||
|
margin-bottom: 24px; |
||||
|
} |
||||
|
|
||||
|
.progress-bar-wrapper { |
||||
|
background-color: #e2e8f0; |
||||
|
height: 8px; |
||||
|
border-radius: 10px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.progress-bar-fill { |
||||
|
background: linear-gradient(90deg, #0f5c78, #1781a6); |
||||
|
height: 100%; |
||||
|
width: 0%; |
||||
|
transition: width 0.1s ease; |
||||
|
} |
||||
|
|
||||
|
.progress-text { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
font-size: 0.85rem; |
||||
|
font-weight: 600; |
||||
|
color: #64748b; |
||||
|
margin-bottom: 6px; |
||||
|
} |
||||
|
|
||||
|
.btn-submit-upload { |
||||
|
width: 100%; |
||||
|
height: 50px; |
||||
|
border-radius: 12px; |
||||
|
background: linear-gradient(135deg, #0f5c78, #1781a6); |
||||
|
color: #ffffff; |
||||
|
font-weight: 600; |
||||
|
font-size: 1rem; |
||||
|
border: none; |
||||
|
box-shadow: 0 4px 15px rgba(15, 92, 120, 0.2); |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s ease; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
gap: 8px; |
||||
|
} |
||||
|
|
||||
|
.btn-submit-upload:hover { |
||||
|
background: linear-gradient(135deg, #1781a6, #0f5c78); |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 6px 20px rgba(15, 92, 120, 0.3); |
||||
|
} |
||||
|
|
||||
|
.btn-submit-upload:disabled { |
||||
|
background: #cbd5e1; |
||||
|
color: #94a3b8; |
||||
|
box-shadow: none; |
||||
|
cursor: not-allowed; |
||||
|
transform: none; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
|
||||
|
<!-- Navigation Fragment --> |
||||
|
<!-- <th:block th:replace="~{fragments/navbar :: navbar(curPage=${curPage}, isAdmin=${isAdmin})}"></th:block> --> |
||||
|
|
||||
|
<div class="main-container"> |
||||
|
|
||||
|
<!-- Header --> |
||||
|
<div class="page-header"> |
||||
|
<h1 class="page-title"> |
||||
|
<i class="fa-solid fa-cloud-arrow-up text-primary"></i> |
||||
|
Upload Documents |
||||
|
</h1> |
||||
|
<a th:href="@{/gccservicedocuments/view}" class="btn-back"> |
||||
|
<i class="fa-solid fa-arrow-left"></i> |
||||
|
Back to View Report |
||||
|
</a> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Upload Card --> |
||||
|
<div class="upload-card"> |
||||
|
<form id="upload-form" onsubmit="handleUpload(event)"> |
||||
|
|
||||
|
<!-- Title --> |
||||
|
<div class="form-group"> |
||||
|
<label class="form-label" for="title-input">Document / Video Title *</label> |
||||
|
<input type="text" id="title-input" required class="form-control-custom" |
||||
|
placeholder="Enter a descriptive title"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- File Type & Category Row --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6 form-group"> |
||||
|
<label class="form-label" for="type-select">File Type *</label> |
||||
|
<select id="type-select" required class="form-control-custom" onchange="adjustFileAcceptance()"> |
||||
|
<option value="">- Select File Type -</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-6 form-group category-select-group"> |
||||
|
<label class="form-label" for="category-select">Category *</label> |
||||
|
<select id="category-select" required class="form-control-custom" |
||||
|
onchange="toggleCustomCategoryInput()"> |
||||
|
<option value="">- Select Category -</option> |
||||
|
<option value="__NEW__">+ Add Custom Category...</option> |
||||
|
</select> |
||||
|
|
||||
|
<!-- Dynamic Custom Category Write-In --> |
||||
|
<input type="text" id="custom-category-input" class="form-control-custom" style="display: none;" |
||||
|
placeholder="Enter custom category name"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Dropzone Area --> |
||||
|
<div class="dropzone-container" id="dropzone"> |
||||
|
<i class="fa-solid fa-cloud-arrow-up dropzone-icon"></i> |
||||
|
<p class="dropzone-text" id="dropzone-msg">Drag and drop file here or click to browse</p> |
||||
|
<p class="dropzone-hint" id="dropzone-hint">Allowed documents: PDF, Word, Excel, TXT</p> |
||||
|
<input type="file" id="file-chooser" class="file-input-hidden" onchange="onFileSelected()"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Selected File Banner --> |
||||
|
<div class="selected-file-banner" id="file-banner"> |
||||
|
<div class="selected-file-details"> |
||||
|
<i class="fa-solid fa-circle-check"></i> |
||||
|
<span id="file-name-display">filename.pdf</span> |
||||
|
<span id="file-size-display" style="opacity: 0.7; font-size: 0.8rem;">(0 MB)</span> |
||||
|
</div> |
||||
|
<button type="button" class="btn-remove-file" onclick="clearSelectedFile()" title="Remove file"> |
||||
|
<i class="fa-solid fa-circle-xmark"></i> |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Progress Bar --> |
||||
|
<div class="progress-container" id="progress-container"> |
||||
|
<div class="progress-text"> |
||||
|
<span id="upload-status">Uploading file...</span> |
||||
|
<span id="progress-percent">0%</span> |
||||
|
</div> |
||||
|
<div class="progress-bar-wrapper"> |
||||
|
<div class="progress-bar-fill" id="progress-fill"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Submit Button --> |
||||
|
<button type="submit" class="btn-submit-upload" id="submit-btn" disabled> |
||||
|
<i class="fa-solid fa-upload"></i> Start Upload |
||||
|
</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<!-- Footer Fragment --> |
||||
|
<th:block th:replace="~{fragments/footer :: footer}"></th:block> |
||||
|
|
||||
|
<script th:inline="javascript"> |
||||
|
const contextPath = /*[[@{/}]]*/ ''; |
||||
|
let selectedFile = null; |
||||
|
|
||||
|
document.addEventListener('DOMContentLoaded', () => { |
||||
|
loadExistingCategories(); |
||||
|
loadFileTypes(); |
||||
|
setupDragAndDrop(); |
||||
|
}); |
||||
|
|
||||
|
function loadExistingCategories() { |
||||
|
fetch(contextPath + 'gccservicedocuments/api/categories') |
||||
|
.then(res => res.json()) |
||||
|
.then(categories => { |
||||
|
const select = document.getElementById('category-select'); |
||||
|
// Add items before the last child option (+ Add Custom Category...) |
||||
|
categories.forEach(cat => { |
||||
|
const opt = document.createElement('option'); |
||||
|
opt.value = cat; |
||||
|
opt.textContent = cat; |
||||
|
select.insertBefore(opt, select.lastElementChild); |
||||
|
}); |
||||
|
}) |
||||
|
.catch(err => console.error("Error loading categories:", err)); |
||||
|
} |
||||
|
|
||||
|
function loadFileTypes() { |
||||
|
fetch(contextPath + 'gccservicedocuments/api/file-types') |
||||
|
.then(res => res.json()) |
||||
|
.then(fileTypes => { |
||||
|
const select = document.getElementById('type-select'); |
||||
|
select.innerHTML = '<option value="">- Select File Type -</option>'; |
||||
|
fileTypes.forEach(ft => { |
||||
|
const opt = document.createElement('option'); |
||||
|
opt.value = ft.id; |
||||
|
opt.textContent = ft.file_type_name; |
||||
|
opt.setAttribute('data-name', ft.file_type_name.toLowerCase()); |
||||
|
select.appendChild(opt); |
||||
|
}); |
||||
|
adjustFileAcceptance(); |
||||
|
}) |
||||
|
.catch(err => console.error("Error loading file types:", err)); |
||||
|
} |
||||
|
|
||||
|
function adjustFileAcceptance() { |
||||
|
const typeSelect = document.getElementById('type-select'); |
||||
|
const fileChooser = document.getElementById('file-chooser'); |
||||
|
const hint = document.getElementById('dropzone-hint'); |
||||
|
|
||||
|
const selectedOption = typeSelect.options[typeSelect.selectedIndex]; |
||||
|
const fileTypeName = selectedOption ? selectedOption.getAttribute('data-name') : ''; |
||||
|
|
||||
|
if (fileTypeName === 'video') { |
||||
|
fileChooser.accept = 'video/mp4,video/webm,video/ogg,video/quicktime'; |
||||
|
hint.textContent = 'Allowed videos: MP4, WebM, OGG, MOV'; |
||||
|
} else { |
||||
|
fileChooser.accept = '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt'; |
||||
|
hint.textContent = 'Allowed documents: PDF, Word, Excel, PowerPoint, TXT'; |
||||
|
} |
||||
|
|
||||
|
// If a file was already selected, clear it to prevent invalid file types from submitting |
||||
|
if (selectedFile) { |
||||
|
clearSelectedFile(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function toggleCustomCategoryInput() { |
||||
|
const select = document.getElementById('category-select'); |
||||
|
const customInput = document.getElementById('custom-category-input'); |
||||
|
|
||||
|
if (select.value === '__NEW__') { |
||||
|
customInput.style.display = 'block'; |
||||
|
customInput.required = true; |
||||
|
customInput.focus(); |
||||
|
} else { |
||||
|
customInput.style.display = 'none'; |
||||
|
customInput.required = false; |
||||
|
customInput.value = ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function setupDragAndDrop() { |
||||
|
const dropzone = document.getElementById('dropzone'); |
||||
|
|
||||
|
['dragenter', 'dragover'].forEach(eventName => { |
||||
|
dropzone.addEventListener(eventName, (e) => { |
||||
|
e.preventDefault(); |
||||
|
dropzone.classList.add('dragover'); |
||||
|
}, false); |
||||
|
}); |
||||
|
|
||||
|
['dragleave', 'drop'].forEach(eventName => { |
||||
|
dropzone.addEventListener(eventName, (e) => { |
||||
|
e.preventDefault(); |
||||
|
dropzone.classList.remove('dragover'); |
||||
|
}, false); |
||||
|
}); |
||||
|
|
||||
|
dropzone.addEventListener('drop', (e) => { |
||||
|
const dt = e.dataTransfer; |
||||
|
const files = dt.files; |
||||
|
if (files.length > 0) { |
||||
|
document.getElementById('file-chooser').files = files; |
||||
|
onFileSelected(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function validateFileExtension(file, fileTypeName) { |
||||
|
if (!fileTypeName) return false; |
||||
|
const fileName = file.name.toLowerCase(); |
||||
|
const dotIdx = fileName.lastIndexOf('.'); |
||||
|
if (dotIdx === -1) return false; |
||||
|
const extension = fileName.substring(dotIdx); |
||||
|
|
||||
|
if (fileTypeName === 'video') { |
||||
|
const allowed = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']; |
||||
|
return allowed.includes(extension); |
||||
|
} else if (fileTypeName === 'pdf') { |
||||
|
return extension === '.pdf'; |
||||
|
} else if (fileTypeName === 'word') { |
||||
|
return extension === '.doc' || extension === '.docx'; |
||||
|
} else if (fileTypeName === 'excel') { |
||||
|
return extension === '.xls' || extension === '.xlsx'; |
||||
|
} else if (fileTypeName === 'presentation') { |
||||
|
return extension === '.ppt' || extension === '.pptx'; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
function onFileSelected() { |
||||
|
const fileInput = document.getElementById('file-chooser'); |
||||
|
if (fileInput.files.length > 0) { |
||||
|
const file = fileInput.files[0]; |
||||
|
const typeSelect = document.getElementById('type-select'); |
||||
|
const selectedOption = typeSelect.options[typeSelect.selectedIndex]; |
||||
|
const fileTypeName = selectedOption ? selectedOption.getAttribute('data-name') : ''; |
||||
|
|
||||
|
if (!typeSelect.value) { |
||||
|
if (typeof Swal !== 'undefined') { |
||||
|
Swal.fire({ |
||||
|
title: 'Select File Type', |
||||
|
text: 'Please select a File Type first before choosing a file.', |
||||
|
icon: 'warning', |
||||
|
confirmButtonColor: '#0f5c78' |
||||
|
}); |
||||
|
} else { |
||||
|
alert("Please select a File Type first before choosing a file."); |
||||
|
} |
||||
|
fileInput.value = ''; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!validateFileExtension(file, fileTypeName)) { |
||||
|
const typeLabel = selectedOption.textContent; |
||||
|
if (typeof Swal !== 'undefined') { |
||||
|
Swal.fire({ |
||||
|
title: 'Invalid File Format', |
||||
|
text: `The selected file does not match the chosen File Type (${typeLabel}). Please choose a valid file.`, |
||||
|
icon: 'error', |
||||
|
confirmButtonColor: '#0f5c78' |
||||
|
}); |
||||
|
} else { |
||||
|
alert(`The selected file does not match the chosen File Type (${typeLabel}). Please choose a valid file.`); |
||||
|
} |
||||
|
fileInput.value = ''; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
selectedFile = file; |
||||
|
|
||||
|
// Show banner |
||||
|
document.getElementById('file-name-display').textContent = selectedFile.name; |
||||
|
document.getElementById('file-size-display').textContent = '(' + (selectedFile.size / (1024 * 1024)).toFixed(2) + ' MB)'; |
||||
|
|
||||
|
document.getElementById('dropzone').style.display = 'none'; |
||||
|
document.getElementById('file-banner').style.display = 'flex'; |
||||
|
document.getElementById('submit-btn').disabled = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function clearSelectedFile() { |
||||
|
document.getElementById('file-chooser').value = ''; |
||||
|
selectedFile = null; |
||||
|
document.getElementById('dropzone').style.display = 'block'; |
||||
|
document.getElementById('file-banner').style.display = 'none'; |
||||
|
document.getElementById('submit-btn').disabled = true; |
||||
|
} |
||||
|
|
||||
|
function handleUpload(event) { |
||||
|
event.preventDefault(); |
||||
|
|
||||
|
if (!selectedFile) { |
||||
|
alert("Please select a file."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const title = document.getElementById('title-input').value.trim(); |
||||
|
const type = document.getElementById('type-select').value; |
||||
|
const categorySelect = document.getElementById('category-select').value; |
||||
|
const customCategory = document.getElementById('custom-category-input').value.trim(); |
||||
|
|
||||
|
let category = categorySelect; |
||||
|
if (categorySelect === '__NEW__') { |
||||
|
if (!customCategory) { |
||||
|
alert("Please specify a custom category name."); |
||||
|
return; |
||||
|
} |
||||
|
category = customCategory; |
||||
|
} |
||||
|
|
||||
|
if (!category) { |
||||
|
alert("Please select or type a category."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// CSRF security token |
||||
|
const csrfMeta = document.querySelector('meta[name="_csrf"]'); |
||||
|
const csrfHeaderMeta = document.querySelector('meta[name="_csrf_header"]'); |
||||
|
|
||||
|
const formData = new FormData(); |
||||
|
formData.append('title', title); |
||||
|
formData.append('fileType', type); |
||||
|
formData.append('category', category); |
||||
|
formData.append('file', selectedFile); |
||||
|
|
||||
|
const xhr = new XMLHttpRequest(); |
||||
|
xhr.open('POST', contextPath + 'gccservicedocuments/api/upload', true); |
||||
|
|
||||
|
if (csrfMeta && csrfHeaderMeta) { |
||||
|
xhr.setRequestHeader(csrfHeaderMeta.content, csrfMeta.content); |
||||
|
} |
||||
|
|
||||
|
// Show progress elements |
||||
|
const progressContainer = document.getElementById('progress-container'); |
||||
|
const progressFill = document.getElementById('progress-fill'); |
||||
|
const progressPercent = document.getElementById('progress-percent'); |
||||
|
const submitBtn = document.getElementById('submit-btn'); |
||||
|
|
||||
|
progressContainer.style.display = 'block'; |
||||
|
submitBtn.disabled = true; |
||||
|
|
||||
|
xhr.upload.addEventListener('progress', (e) => { |
||||
|
if (e.lengthComputable) { |
||||
|
const percent = Math.round((e.loaded / e.total) * 100); |
||||
|
progressFill.style.width = percent + '%'; |
||||
|
progressPercent.textContent = percent + '%'; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
xhr.onload = function () { |
||||
|
if (xhr.status === 200) { |
||||
|
const response = JSON.parse(xhr.responseText); |
||||
|
if (response.success) { |
||||
|
alert(response.message || "Upload completed successfully!"); |
||||
|
// Redirect back to main page |
||||
|
window.location.href = contextPath + 'gccservicedocuments/view'; |
||||
|
} else { |
||||
|
alert("Upload failed: " + response.error); |
||||
|
progressContainer.style.display = 'none'; |
||||
|
submitBtn.disabled = false; |
||||
|
} |
||||
|
} else { |
||||
|
alert("An error occurred during file upload. Code: " + xhr.status); |
||||
|
progressContainer.style.display = 'none'; |
||||
|
submitBtn.disabled = false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
xhr.onerror = function () { |
||||
|
alert("Connection error occurred while uploading file."); |
||||
|
progressContainer.style.display = 'none'; |
||||
|
submitBtn.disabled = false; |
||||
|
}; |
||||
|
|
||||
|
xhr.send(formData); |
||||
|
} |
||||
|
</script> |
||||
|
</body> |
||||
|
|
||||
|
</html> |
||||
1115
src/main/resources/templates/gccservicedocuments/view.html
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue