|
- // 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 (<a>) 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 [
- <ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
- <GridActionsCellItem
- icon={<EditIcon sx={{fontSize: 25}}/>}
- label="Edit"
- className="textPrimary"
- onClick={handleEditClick(id)}
- color="edit"
- />
- </ThemeProvider>
- ]
- },
- },
-
- {
- id: 'templateName',
- field: 'templateName',
- headerName: 'Form Name',
- flex: 2,
- renderCell: (params) => {
- return (
- <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}>
- {params.value} {params.row.vNum}
- </div>
- );
- }
- },
- {
- field: 'upload1',
- type: 'actions',
- // Multi-line header
- headerName: (
- <div>
- Upload Sig.
- </div>
- ),
- 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
- ? <CheckCircleIcon sx={{fontSize: 25, color: 'success.main'}} /> // Green tick if uploaded
- : <UploadFileIcon sx={{fontSize: 25}}/>; // Upload icon if not uploaded
-
- const upload1Label = isUploaded1 ? "Update Signature" : "Upload Signature";
-
- // Define the actions
- const actions = [
- <ThemeProvider key="UploadSign1" theme={LIONER_BUTTON_THEME}>
- <GridActionsCellItem
- icon={upload1Icon} // Use the dynamic icon
- label={upload1Label} // Use the dynamic label
- className="textPrimary"
- onClick={handleUploadClick(id, templateName, formCode)} // Pass templateName here
- color="upload" // Use 'upload' color which will apply to the button
- />
- </ThemeProvider>
- ];
-
- // 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
- ? <CheckCircleIcon sx={{fontSize: 25, color: 'success.main'}} /> // Green tick if uploaded
- : <UploadFileIcon sx={{fontSize: 25}}/>; // Upload icon if not uploaded
-
- const upload2Label = isUploaded2 ? "Update 2" : "Upload 2";
- // >>> END CHANGES HERE <<<
-
- actions.push(
- <ThemeProvider key="UploadSign2" theme={LIONER_BUTTON_THEME}>
- <GridActionsCellItem
- icon={upload2Icon} // <<< USE DYNAMIC ICON <<<
- label={upload2Label} // <<< USE DYNAMIC LABEL <<<
- className="textPrimary"
- onClick={handleUpload2Click(id, templateName, formCode)} // Pass templateName here
- color="upload"
- />
- </ThemeProvider>
- );
- }
-
- return actions;
- },
- },
- {
- field: 'actions2',
- type: 'actions',
- headerName: 'Download',
- width: 100,
- cellClassName: 'actions',
- getActions: ({id}) => {
- return [
- <ThemeProvider key="DownloadFile" theme={LIONER_BUTTON_THEME}>
- <GridActionsCellItem
- icon={<FileDownloadIcon sx={{fontSize: 25}}/>}
- label="Download"
- className="textPrimary"
- onClick={handleDownloadClick(id)}
- color="download"
- />
- </ThemeProvider>
- ]
- },
- },
- {
- id: 'createDate',
- field: 'createDate',
- headerName: 'Create Datetime',
- flex: 1,
- sortComparator: dateComparator,
- renderCell: (params) => (
- <div>
- {getDateString(params.row.created, 'dd/MM/yyyy HH:mm:ss')}
- </div>
- ),
- },
- {
- id: 'version',
- field: 'version',
- headerName: 'Version',
- flex: 0.5,
- renderCell: (params) => {
- return (
- <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}>
- {params.value}
- </div>
- );
- }
- },
- {
- id: 'modified',
- field: 'modified',
- headerName: 'Modified Date',
- flex: 1,
- sortComparator: dateComparator,
- renderCell: (params) => (
- <div>
- {getDateString(params.row.modified, 'dd/MM/yyyy HH:mm:ss')}
- </div>
- ),
- },
- ];
-
- return (
- <div>
- <DataGrid
- rows={rows}
- columns={columns}
- // Increased height to accommodate the multi-line header
- columnHeaderHeight={70}
- editMode="row"
- rowModesModel={rowModesModel}
- getRowHeight={() => 'auto'}
- paginationModel={paginationModel}
- onPaginationModelChange={setPaginationModel}
- slots={{
- noRowsOverlay: () => (
- CustomNoRowsOverlay()
- )
- }}
- pageSizeOptions={[10]}
- autoHeight
- />
-
- {/* The Upload Dialog Box */}
- <Dialog
- open={isDialogOpen}
- onClose={handleCloseDialog}
- fullWidth
- maxWidth="sm"
- // Prevent closing when upload is active
- disableEscapeKeyDown={isUploading}
- TransitionProps={{ onExited: handleCloseDialog }}
- >
- <DialogTitle>
- {/* Dynamic Title based on currentUploadRow state */}
- **{getUploadDialogTitle(currentUploadRow.formCode, currentUploadRow.refType)}**
- </DialogTitle>
- <DialogContent dividers>
- <Box sx={{ mt: 2, textAlign: 'center' }}>
- {/* Button to trigger file selection */}
- <Button
- variant="contained"
- color="primary"
- startIcon={isUploading ? <CircularProgress size={20} color="inherit" /> : <UploadFileIcon />}
- onClick={handleChooseFile}
- disabled={isUploading}
- >
- {isUploading ? 'Uploading...' : 'Choose PDF File'}
- </Button>
-
- {/* Hidden File Input */}
- <input
- ref={fileInputRef}
- type="file"
- accept="application/pdf"
- onChange={handleFileChange}
- style={{ display: 'none' }}
- disabled={isUploading}
- />
-
- {/* Display the selected file name if a file is chosen */}
- {fileInputRef.current?.files[0] && (
- <Typography variant="body2" sx={{ mt: 1 }}>
- Selected: **{fileInputRef.current.files[0].name}**
- </Typography>
- )}
- </Box>
-
- </DialogContent>
- <DialogActions>
- <Button onClick={handleCloseDialog} color="inherit" disabled={isUploading}>
- Cancel
- </Button>
- </DialogActions>
- </Dialog>
- </div>
- );
- }
|