// material-ui import * as React from 'react'; import {apiPath} from "../../../auth/utils"; import { POST_SIG_UPLOAD1 } from "../../../utils/ApiPathConst"; import axios from 'axios'; import { DataGrid, GridActionsCellItem, } from "@mui/x-data-grid"; import EditIcon from '@mui/icons-material/Edit'; import UploadFileIcon from '@mui/icons-material/UploadFile'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import FileDownloadIcon from '@mui/icons-material/FileDownload'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, Box, CircularProgress, } from '@mui/material'; import {useContext, useEffect} from "react"; import {useNavigate} from "react-router-dom"; // Note: Assuming these utility functions/components are defined elsewhere import {CustomNoRowsOverlay, dateComparator, getDateString} from "../../../utils/CommonFunction"; import AbilityContext from "../../../components/AbilityProvider"; import {LIONER_BUTTON_THEME} from "../../../themes/colorConst"; import {ThemeProvider} from "@emotion/react"; // ==============================|| PDF TABLE ||============================== // // Define the structure for the row data stored in state const initialUploadState = { id: null, templateName: null, // This will be the formCode refType: null, // To differentiate between upload1 and upload2 if needed } export default function PdfTable({recordList}) { const [rows, setRows] = React.useState(recordList); const [rowModesModel] = React.useState({}); // State for Dialog visibility and Loading state const [isDialogOpen, setIsDialogOpen] = React.useState(false); // State to hold the ID, templateName, and refType for the current upload operation const [currentUploadRow, setCurrentUploadRow] = React.useState(initialUploadState); const [isUploading, setIsUploading] = React.useState(false); const navigate = useNavigate() const ability = useContext(AbilityContext); const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize:10 }); // Ref for the hidden file input const fileInputRef = React.useRef(null); useEffect(() => { setPaginationModel({page:0,pageSize:10}); setRows(recordList); }, [recordList]); const handleEditClick = (id) => () => { navigate(`/pdf/maintain/${id}`); }; /** * Opens the upload dialog and sets the current row details for Upload 1 */ const handleUploadClick = (id, templateName, formCode) => () => { setCurrentUploadRow({ id: id, templateName: templateName, formCode: formCode, refType: "upload1" }); setIsDialogOpen(true); }; /** * Opens the upload dialog and sets the current row details for Upload 2 */ const handleUpload2Click = (id, templateName, formCode) => () => { // Placeholder for Upload 2 console.log(`Uploading for row ID ${id} (Upload 2)`); setCurrentUploadRow({ id: id, templateName: templateName, formCode: formCode, refType: "upload2" // A different refType for a different upload logic/API }); setIsDialogOpen(true); }; const handleDownloadClick = (id) => () => { // 1. Construct the download URL with the ID query parameter const downloadUrl = `${apiPath}/pdf/download-ff/${id}`; // Use axios to fetch the PDF as a Blob axios.get(downloadUrl, { responseType: 'blob', // IMPORTANT: Tells axios to handle the response as binary data }) .then((response) => { // 2. Extract Filename from Content-Disposition Header const contentDisposition = response.headers['content-disposition']; let filename = `document-${id}.pdf`; // Fallback filename if (contentDisposition) { // Regex to find filename="name.pdf" or filename*=UTF-8''name.pdf // The server should be setting the filename header correctly. const filenameMatch = contentDisposition.match(/filename\*?=['"]?([^'"]+)/); if (filenameMatch && filenameMatch[1]) { // Decode URI component and remove extra quotes filename = decodeURIComponent(filenameMatch[1].replace(/\\"/g, '')); } } // 3. Create a temporary anchor tag () to trigger the download const blob = new Blob([response.data], { type: 'application/pdf' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.setAttribute('download', filename); document.body.appendChild(link); link.click(); // 4. Clean up document.body.removeChild(link); window.URL.revokeObjectURL(url); }) .catch((error) => { console.error(`Download failed for ID ${id}:`, error); // Handle error response (e.g., if the backend returns a 404 or a JSON error) alert('Failed to download the PDF file. Check server logs.'); }); }; const handleCloseDialog = () => { setIsDialogOpen(false); setCurrentUploadRow(initialUploadState); // Reset the current row if (fileInputRef.current) { fileInputRef.current.value = ""; // Clear the file input } }; // Function to generate the dynamic dialog title const getUploadDialogTitle = (formCode, refType) => { console.log("formCode:" + formCode + " refType:" + refType); if (refType === 'upload1') { switch (formCode) { case "IDA": return "Upload Page 15"; case "FNA": return "Upload Page 10"; case "HSBCFIN": return "Upload Page 11"; case "HSBCA31": return "Upload Page 28-29"; case "MLB03S": return "Upload Page 9"; case "MLFNA_EN": return "Upload Page 4"; case "MLFNA_CHI": return "Upload Page 4"; case "SLGII": return "Upload Page 13"; case "SLAPP": return "Upload Page 16"; case "SLFNA_EN": return "Upload Page 5"; case "SLFNA_CHI": return "Upload Page 5"; default: return "Unknown Form"; } }else if (refType === 'upload2') { switch (formCode) { case "MLB03S": return "Upload Page 13"; case "SLGII": return "Upload Page 15-16"; case "SLAPP": return "Upload Page 18-19"; default: return "Unknown Form"; } } // Handle other refTypes if needed, e.g., 'upload2' if (refType === 'upload2') { return `Upload Template 2 for ${formCode}`; } return "Upload File"; // Fallback }; // Function to handle file selection and API submission const handleFileChange = async (event) => { const file = event.target.files[0]; const { id, templateName, refType } = currentUploadRow; if (!file || !id || !refType) return; if (file.type !== "application/pdf") { alert("Please select a PDF file."); event.target.value = ""; return; } // The URL should potentially change based on refType if upload2 uses a different endpoint const uploadUrl = `${apiPath}${POST_SIG_UPLOAD1}`; // Assuming POST_SIG_UPLOAD1 is used for both for now // 1. Create FormData const formData = new FormData(); formData.append('file', file); formData.append('refId', id); formData.append('refType', refType); // Pass the refType to the backend setIsUploading(true); try { const response = await axios.post( uploadUrl, formData, { headers: { 'Content-Type': 'multipart/form-data', } } ); if (response.status === 200) { alert('Upload success'); console.log(`PDF file ${file.name} successfully uploaded for record ID: ${id} with refType: ${refType}!`); // --- START: Update local state to show the green tick --- const uploadedFileId = response.data?.fileId || 'temp-id-' + Date.now(); // Assume the response has a fileId or use a temp one setRows(prevRows => prevRows.map(row => { if (row.id === id) { // Update the relevant file ID field based on refType const updateField = refType === 'upload1' ? 'upload1FileId' : 'upload2FileId'; return { ...row, [updateField]: uploadedFileId // Set the file ID to trigger the icon }; } return row; }) ); // --- END: Update local state to show the green tick --- } else { throw new Error(`Upload failed with status: ${response.status}`); } } catch (error) { console.error('Upload error:', error); // Check if the error has a response and use its message if available const errorMessage = error.response?.data?.message || error.message; alert(`Error uploading file: ${errorMessage}`); } finally { // 3. Cleanup and close setIsUploading(false); handleCloseDialog(); } }; const handleChooseFile = () => { // Trigger the hidden file input click if (fileInputRef.current) { fileInputRef.current.click(); } }; const columns = [ { field: 'actions', type: 'actions', headerName: 'Edit', width: 100, cellClassName: 'actions', getActions: ({id}) => { return [ } label="Edit" className="textPrimary" onClick={handleEditClick(id)} color="edit" /> ] }, }, { id: 'templateName', field: 'templateName', headerName: 'Form Name', flex: 2, renderCell: (params) => { return (
{params.value} {params.row.vNum}
); } }, { field: 'upload1', type: 'actions', // Multi-line header headerName: (
Upload Sig.
), width: 100, cellClassName: 'actions', getActions: ({ id, row }) => { // Check if a file ID exists to determine if a file is present for Upload 1 const isUploaded1 = !!row.upload1FileId; const isUploaded2 = !!row.upload2FileId; // <<< ADD THIS LINE <<< const templateName = row.templateName; const formCode = row.formCode; // Determine the icon and label based on upload status for Upload 1 const upload1Icon = isUploaded1 ? // Green tick if uploaded : ; // Upload icon if not uploaded const upload1Label = isUploaded1 ? "Update Signature" : "Upload Signature"; // Define the actions const actions = [ ]; // Conditional rendering logic for Upload 2 if (row.formCode === "MLB03S" || row.formCode === "SLGII" || row.formCode === "SLAPP") { // Determine the icon and label based on upload status for Upload 2 <<< START CHANGES HERE <<< const upload2Icon = isUploaded2 ? // Green tick if uploaded : ; // Upload icon if not uploaded const upload2Label = isUploaded2 ? "Update 2" : "Upload 2"; // >>> END CHANGES HERE <<< actions.push( ); } return actions; }, }, { field: 'actions2', type: 'actions', headerName: 'Download', width: 100, cellClassName: 'actions', getActions: ({id}) => { return [ } label="Download" className="textPrimary" onClick={handleDownloadClick(id)} color="download" /> ] }, }, { id: 'createDate', field: 'createDate', headerName: 'Create Datetime', flex: 1, sortComparator: dateComparator, renderCell: (params) => (
{getDateString(params.row.created, 'dd/MM/yyyy HH:mm:ss')}
), }, { id: 'version', field: 'version', headerName: 'Version', flex: 0.5, renderCell: (params) => { return (
{params.value}
); } }, { id: 'modified', field: 'modified', headerName: 'Modified Date', flex: 1, sortComparator: dateComparator, renderCell: (params) => (
{getDateString(params.row.modified, 'dd/MM/yyyy HH:mm:ss')}
), }, ]; return (
'auto'} paginationModel={paginationModel} onPaginationModelChange={setPaginationModel} slots={{ noRowsOverlay: () => ( CustomNoRowsOverlay() ) }} pageSizeOptions={[10]} autoHeight /> {/* The Upload Dialog Box */} {/* Dynamic Title based on currentUploadRow state */} **{getUploadDialogTitle(currentUploadRow.formCode, currentUploadRow.refType)}** {/* Button to trigger file selection */} {/* Hidden File Input */} {/* Display the selected file name if a file is chosen */} {fileInputRef.current?.files[0] && ( Selected: **{fileInputRef.current.files[0].name}** )}
); }