@@ -68,7 +68,7 @@ const AutoLogoutProvider = ({ children }) => { | |||||
} = useIdleTimer({ | } = useIdleTimer({ | ||||
onIdle, | onIdle, | ||||
onActive, | onActive, | ||||
timeout: 10_000, | |||||
timeout: 60_000, | |||||
throttle: 500, | throttle: 500, | ||||
crossTab: true, | crossTab: true, | ||||
syncTimers: 200, | syncTimers: 200, | ||||
@@ -1,13 +1,17 @@ | |||||
// assets | // assets | ||||
import BoyIcon from '@mui/icons-material/Boy'; | import BoyIcon from '@mui/icons-material/Boy'; | ||||
import AssignmentIcon from '@mui/icons-material/Assignment'; | |||||
// icons | // icons | ||||
const ClientIcon = () => { | |||||
return ( | |||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginLeft: '-4px' }}> | |||||
<BoyIcon fontSize="medium"/> | |||||
</div> | |||||
); | |||||
const icons = { | |||||
ClientIcon : () => { return ( | |||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginLeft: '-4px' }}> | |||||
<BoyIcon fontSize="medium"/> | |||||
</div> | |||||
) | |||||
}, | |||||
AssignmentIcon, | |||||
}; | }; | ||||
// ==============================|| MENU ITEMS - DASHBOARD ||============================== // | // ==============================|| MENU ITEMS - DASHBOARD ||============================== // | ||||
@@ -19,14 +23,23 @@ const client = { | |||||
//ability:['SUPPRESS','REMINDER'], | //ability:['SUPPRESS','REMINDER'], | ||||
children: [ | children: [ | ||||
{ | { | ||||
id: 'Client', | |||||
id: 'client', | |||||
title: 'Client', | title: 'Client', | ||||
type: 'item', | type: 'item', | ||||
url: '/client', | url: '/client', | ||||
icon: ClientIcon, | |||||
icon: icons.ClientIcon, | |||||
breadcrumbs: false, | breadcrumbs: false, | ||||
ability:['VIEW','DASHBOARD'] | ability:['VIEW','DASHBOARD'] | ||||
}, | }, | ||||
// { | |||||
// id: 'template', | |||||
// title: 'Template', | |||||
// type: 'item', | |||||
// url: '/template', | |||||
// icon: icons.AssignmentIcon, | |||||
// breadcrumbs: false, | |||||
// ability:['VIEW','DASHBOARD'] | |||||
// }, | |||||
] | ] | ||||
}; | }; | ||||
@@ -32,7 +32,8 @@ const dashboard = { | |||||
id: 'lionerdashboard', | id: 'lionerdashboard', | ||||
title: <FormattedMessage id="Dashboard"/>, | title: <FormattedMessage id="Dashboard"/>, | ||||
type: 'item', | type: 'item', | ||||
url: '/lionerDashboard', | |||||
url: '/client', | |||||
// url: '/lionerDashboard', | |||||
icon: icons.SpeedIcon, | icon: icons.SpeedIcon, | ||||
breadcrumbs: false, | breadcrumbs: false, | ||||
ability:['VIEW','DASHBOARD'] | ability:['VIEW','DASHBOARD'] | ||||
@@ -12,7 +12,8 @@ import appreciation from "./appreciation"; | |||||
// ==============================|| MENU ITEMS ||============================== // | // ==============================|| MENU ITEMS ||============================== // | ||||
const menuItems = { | const menuItems = { | ||||
items: [dashboard, client, setting] | |||||
items: [client, setting] | |||||
// items: [dashboard, client, setting] | |||||
}; | }; | ||||
// pages, utilities, support, misc | // pages, utilities, support, misc | ||||
@@ -86,15 +86,15 @@ const setting = { | |||||
// breadcrumbs: false, | // breadcrumbs: false, | ||||
// ability:['MAINTAIN','CLIENT_DEPARTMENT'] | // ability:['MAINTAIN','CLIENT_DEPARTMENT'] | ||||
// }, | // }, | ||||
{ | |||||
id: 'userGroup', | |||||
title: 'User Group', | |||||
type: 'item', | |||||
url: '/usergroupSearchview', | |||||
icon: icons.UsergroupAddOutlined, | |||||
breadcrumbs: false, | |||||
ability:['MAINTAIN','USER_GROUP'] | |||||
}, | |||||
// { | |||||
// id: 'userGroup', | |||||
// title: 'User Group', | |||||
// type: 'item', | |||||
// url: '/usergroupSearchview', | |||||
// icon: icons.UsergroupAddOutlined, | |||||
// breadcrumbs: false, | |||||
// ability:['MAINTAIN','USER_GROUP'] | |||||
// }, | |||||
{ | { | ||||
id: 'user', | id: 'user', | ||||
title: 'User', | title: 'User', | ||||
@@ -1012,14 +1012,15 @@ const ClientForm = ({ refClientDetail, | |||||
</Grid> | </Grid> | ||||
<Grid item sx={{ml:{xs:1.5,md:1.5,lg:1.5}, mr:3, mb:1, mt:2}}> | <Grid item sx={{ml:{xs:1.5,md:1.5,lg:1.5}, mr:3, mb:1, mt:2}}> | ||||
<Button | |||||
{!isNewRecord && (<Button | |||||
variant="contained" | variant="contained" | ||||
color="delete" | color="delete" | ||||
disabled={isNewRecord || !ability.can('DELETE','EVENT')} | |||||
disabled={!ability.can('DELETE','EVENT')} | |||||
onClick={handleDeleteClick} | onClick={handleDeleteClick} | ||||
> | > | ||||
Delete | Delete | ||||
</Button> | </Button> | ||||
)} | |||||
<GeneralConfirmWindow | <GeneralConfirmWindow | ||||
isWindowOpen={isWindowOpen} | isWindowOpen={isWindowOpen} | ||||
title={"Attention"} | title={"Attention"} | ||||
@@ -91,15 +91,22 @@ export default function ClientTable({recordList}) { | |||||
id: 'fullname', | id: 'fullname', | ||||
field: 'fullname', | field: 'fullname', | ||||
headerName: 'Client Name', | headerName: 'Client Name', | ||||
flex: 2, | |||||
flex: 1.5, | |||||
renderCell: (params) => { | renderCell: (params) => { | ||||
return ( | return ( | ||||
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | ||||
{params.row.title ? `(${params.row.title})` : ''} {params.value} | |||||
{params.value} | |||||
{/* {params.row.title ? `(${params.row.title})` : ''} {params.value} */} | |||||
</div> | </div> | ||||
); | ); | ||||
} | } | ||||
}, | }, | ||||
{ | |||||
id: 'title', | |||||
field: 'title', | |||||
headerName: 'Title', | |||||
flex: 1.5, | |||||
}, | |||||
{ | { | ||||
id: 'joinDate', | id: 'joinDate', | ||||
field: 'joinDate', | field: 'joinDate', | ||||
@@ -1,8 +1,8 @@ | |||||
import React, { useEffect, useRef, useState } from 'react'; | import React, { useEffect, useRef, useState } from 'react'; | ||||
import { Button, Grid } from '@mui/material'; | |||||
import { Button, Grid, InputLabel, TextField } from '@mui/material'; | |||||
import { GeneralConfirmWindow, notifySaveSuccess } from "../../../utils/CommonFunction"; | import { GeneralConfirmWindow, notifySaveSuccess } from "../../../utils/CommonFunction"; | ||||
import axios from 'axios'; | import axios from 'axios'; | ||||
import {apiPath, appURL} from "../../../auth/utils"; | |||||
import {apiPath, adobeAPIKey} from "../../../auth/utils"; | |||||
import { | import { | ||||
GET_PDF_TEMPLATE_PATH, | GET_PDF_TEMPLATE_PATH, | ||||
GET_PDF_PATH, | GET_PDF_PATH, | ||||
@@ -16,14 +16,19 @@ import {useLocation, useParams} from "react-router-dom"; | |||||
// Import your chosen commercial PDF SDK (e.g., PSPDFKit) | // Import your chosen commercial PDF SDK (e.g., PSPDFKit) | ||||
import PSPDFKit from 'pspdfkit'; | import PSPDFKit from 'pspdfkit'; | ||||
import WebViewer from '@compdfkit_pdf_sdk/webviewer'; | |||||
import Nutrient from "@nutrient-sdk/viewer"; | |||||
import { CollectionsBookmarkRounded } from '../../../../node_modules/@mui/icons-material/index'; | import { CollectionsBookmarkRounded } from '../../../../node_modules/@mui/icons-material/index'; | ||||
import LoadingComponent from "../../extra-pages/LoadingComponent"; | |||||
import { fill } from 'lodash'; | |||||
function PDF() { | function PDF() { | ||||
const viewerRef = useRef(null); // Ref for the DOM element where PDF will render | const viewerRef = useRef(null); // Ref for the DOM element where PDF will render | ||||
const [pdfLoaded, setPdfLoaded] = useState(false); | const [pdfLoaded, setPdfLoaded] = useState(false); | ||||
const [viewerLoaded, setViewerLoaded] = useState(false); | const [viewerLoaded, setViewerLoaded] = useState(false); | ||||
const [viewInstance,setViewInstance] = useState(); | const [viewInstance,setViewInstance] = useState(); | ||||
const [pdfBytes, setPdfBytes] = useState(); | |||||
const [adobeDCView,setAdobeDCView] = useState(); | |||||
const [pdfUrl, setPdfUrl] = useState(); | |||||
const [record, setRecord] = useState(); | const [record, setRecord] = useState(); | ||||
const navigate = useNavigate() | const navigate = useNavigate() | ||||
@@ -32,7 +37,7 @@ function PDF() { | |||||
const queryParams = new URLSearchParams(location.search); | const queryParams = new URLSearchParams(location.search); | ||||
const refId = queryParams.get("refId"); | const refId = queryParams.get("refId"); | ||||
const loadPdfForm = async (id, templateId = 0) => { | |||||
const loadPdfForm = async (id, templateId = 0, clientId = 0) => { | |||||
if (!pdfLoaded) { | if (!pdfLoaded) { | ||||
if (id > 0) { | if (id > 0) { | ||||
// axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | // axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | ||||
@@ -60,7 +65,7 @@ function PDF() { | |||||
byteNum[i] = byteChar.charCodeAt(i); | byteNum[i] = byteChar.charCodeAt(i); | ||||
} | } | ||||
setPdfBytes(byteNum); | |||||
handlePdfUrl(byteNum); | |||||
setPdfLoaded(true); | setPdfLoaded(true); | ||||
} | } | ||||
}) | }) | ||||
@@ -71,11 +76,16 @@ function PDF() { | |||||
} else { | } else { | ||||
axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | ||||
// axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}/${templateId}`, { | |||||
responseType: 'arraybuffer', // Essential for binary data | responseType: 'arraybuffer', // Essential for binary data | ||||
params: { | |||||
templateId: templateId, | |||||
clientId: clientId, | |||||
}, | |||||
}) | }) | ||||
.then((response) => { | .then((response) => { | ||||
if (response.status === 200) { | if (response.status === 200) { | ||||
setPdfBytes(response.data); | |||||
handlePdfUrl(response.data); | |||||
setPdfLoaded(true); | setPdfLoaded(true); | ||||
} | } | ||||
}) | }) | ||||
@@ -87,66 +97,72 @@ function PDF() { | |||||
} | } | ||||
}; | }; | ||||
const handlePdfUrl = (pdfBytes) => { | |||||
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' }); | |||||
// const pdfFile = new File([pdfBlob], 'document.pdf', { type: 'application/pdf' }); | |||||
console.log(pdfBlob); | |||||
const pdfUrl = URL.createObjectURL(pdfBlob); | |||||
setPdfUrl(pdfUrl); | |||||
} | |||||
const loadPdfViewer = async () => { | const loadPdfViewer = async () => { | ||||
if (pdfLoaded && viewerRef.current && !viewerLoaded) { | |||||
if (pdfLoaded && viewerRef.current && !viewerLoaded && !adobeDCView) { | |||||
try { | try { | ||||
//New try | |||||
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' }); | |||||
console.log(pdfBlob); | |||||
const pdfUrl = URL.createObjectURL(pdfBlob); | |||||
// console.log('Template: '); | |||||
// console.log(record); | |||||
// console.log('URL: '); | |||||
// console.log(pdfUrl); | |||||
// console.log('Viewer: '); | |||||
// console.log(viewerRef.current); | |||||
// await PSPDFKit.unload(viewerRef.current); | |||||
await PSPDFKit.load({//click into load | |||||
container: viewerRef.current, | |||||
// container: '#viewer', | |||||
document: pdfUrl, | |||||
// baseUrl: `./sdk/`, // Path to SDK assets | |||||
baseUrl: `${appURL}/sdk/`, // Path to SDK | |||||
// baseUrl: `${window.location.protocol}//${window.location.host}/${import.meta.env.BASE_URL}`, | |||||
// baseUrl: `https://cdn.jsdelivr.net/npm/pspdfkit@VERSION/dist/`, // Path to SDK assets so this is work? | |||||
// baseUrl: `${process.env.PUBLIC_URL}/`, // Path to SDK assets | |||||
disableWebAssemblyStreaming: true, | |||||
initialViewState: new PSPDFKit.ViewState({ | |||||
sidebarMode: PSPDFKit.SidebarMode.THUMBNAILS | |||||
}) | |||||
}) | |||||
.then(instance => { | |||||
URL.revokeObjectURL(pdfUrl); | |||||
setViewInstance(instance); | |||||
console.log('instance: '); | |||||
console.log(instance); | |||||
setViewerLoaded(true); | |||||
// instance.addEventListener("formFields.load", (loadedFormFields) => { | |||||
// console.log("loaded fields:", loadedFormFields); | |||||
// }); | |||||
const formFieldValues = instance.getFormFieldValues(); | |||||
// console.log(formFieldValues); // => { textField: 'Text Value', checkBoxField: ['A', 'B'], buttonField: null }e.log(getFormFieldValues); | |||||
}); | |||||
// instanceRef.current = instance; | |||||
// 3. Clean up on component unmount | |||||
// return () => { | |||||
// if (instance) { | |||||
// instance.unload(); // Unload SDK instance | |||||
// } | |||||
// URL.revokeObjectURL(pdfUrl); // Revoke the Blob URL | |||||
// }; | |||||
loadAdobeSDK(); | |||||
} catch (error) { | } catch (error) { | ||||
console.error('Error loading PDF:', error); | console.error('Error loading PDF:', error); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
const loadAdobeSDK = async() => { | |||||
// const token = localStorage.getItem('accessToken'); | |||||
if (window.AdobeDC) { | |||||
const DCViewer = (new window.AdobeDC.View({ | |||||
clientId: `${adobeAPIKey}`, | |||||
divId: 'adobe-dc-view', | |||||
})); | |||||
setAdobeDCView(DCViewer); | |||||
await DCViewer.previewFile( | |||||
{ | |||||
content: { | |||||
location: { | |||||
url: pdfUrl, | |||||
// headers: [{ key: 'Authorization', value: `Bearer ${token}` }], | |||||
}, | |||||
}, | |||||
metaData: { fileName: 'document.pdf'/*templateName */ }, | |||||
}, | |||||
{ | |||||
embedMode: 'FULL_WINDOW', | |||||
showAnnotationTools: true, | |||||
enableFormFilling: true, | |||||
} | |||||
).catch(error => { | |||||
console.error('Preview error:', error); | |||||
setError('Failed to load PDF: ' + error.message); | |||||
}).then(instance => { | |||||
URL.revokeObjectURL(pdfUrl); | |||||
setViewInstance(instance); | |||||
console.log('Instance: ', instance); | |||||
setViewerLoaded(true); | |||||
}); | |||||
DCViewer.registerCallback( | |||||
window.AdobeDC.View.Enum.CallbackType.SAVE_API, | |||||
handleSavePdf, | |||||
{ autoSaveFrequency: 0, enableFormFilling: true } | |||||
); | |||||
} else { | |||||
console.error('AdobeDC not available'); | |||||
setError('Adobe SDK not loaded'); | |||||
} | |||||
}; | |||||
useEffect(() => { | useEffect(() => { | ||||
if (params.id !== null) { | if (params.id !== null) { | ||||
const pdfData = (params.id).split("T"); | const pdfData = (params.id).split("T"); | ||||
@@ -154,53 +170,61 @@ function PDF() { | |||||
if (pdfData[0] > 0) { // Existing Record | if (pdfData[0] > 0) { // Existing Record | ||||
loadPdfForm(pdfData[0]); | loadPdfForm(pdfData[0]); | ||||
} else { // New Record | } else { // New Record | ||||
const clientId = pdfData[0] * -1; | |||||
const templateId = pdfData[1] * 1; | |||||
setRecord({ | setRecord({ | ||||
id: -1, | id: -1, | ||||
clientId: pdfData[0] * -1, //If PDF ID is negative, convert it to client ID | |||||
templateId: pdfData[1],}); | |||||
loadPdfForm(-1, pdfData[1]); // Load new Template | |||||
clientId: clientId, //If PDF ID is negative, convert it to client ID | |||||
templateId: templateId}); | |||||
loadPdfForm(-1, templateId, clientId); // Load new Template | |||||
} | } | ||||
} | } | ||||
}, [params.id]); | }, [params.id]); | ||||
// useEffect(() => { | |||||
// if (record) { | |||||
// console.log(record); | |||||
// loadPdfForm(); | |||||
// } | |||||
// }, [record]); | |||||
useEffect(() => { // Update Save function callback after record is updated | |||||
console.log("Record Updated: ",record); | |||||
if (record && adobeDCView) { | |||||
adobeDCView.registerCallback( | |||||
window.AdobeDC.View.Enum.CallbackType.SAVE_API, | |||||
handleSavePdf, | |||||
{ autoSaveFrequency: 0, enableFormFilling: true } | |||||
); | |||||
} | |||||
}, [record]); | |||||
useEffect(() => { | useEffect(() => { | ||||
loadPdfViewer(); | loadPdfViewer(); | ||||
}, [viewerRef.current, pdfLoaded]); | }, [viewerRef.current, pdfLoaded]); | ||||
const handleSavePdf = async () => { | |||||
if (viewInstance) { | |||||
try { | |||||
// Export the filled PDF from the SDK as an ArrayBuffer | |||||
const arrayBuffer = await viewInstance.exportPDF(); | |||||
const filledPdfBlob = new Blob([arrayBuffer], { type: 'application/pdf' }); | |||||
// Create FormData to send the file to Spring Boot | |||||
const formData = new FormData(); | |||||
formData.append('file', filledPdfBlob, 'filled_form.pdf'); | |||||
formData.append('record', JSON.stringify(record)); | |||||
// Send the filled PDF to your Spring Boot backend's save endpoint | |||||
const response = await axios.post(`${apiPath}${POST_PDF_PATH}`, formData, { | |||||
headers: { | |||||
'Content-Type': 'multipart/form-data' // Important for file uploads | |||||
}, | |||||
}); | |||||
const handleSavePdf = async (metaData, content, options) => { | |||||
try { | |||||
const filledPdfBlob = new Blob([content], { type: 'application/pdf' }); | |||||
// Create FormData to send the file to Spring Boot | |||||
const formData = new FormData(); | |||||
formData.append('file', filledPdfBlob, 'filled_form.pdf'); | |||||
formData.append('record', JSON.stringify(record)); | |||||
// Send the filled PDF to your Spring Boot backend's save endpoint | |||||
await axios.post(`${apiPath}${POST_PDF_PATH}`, formData, { | |||||
headers: { | |||||
'Content-Type': 'multipart/form-data' // Important for file uploads | |||||
}, | |||||
}) | |||||
.then(response => { | |||||
console.log('PDF saved on server:', response.data); | console.log('PDF saved on server:', response.data); | ||||
notifySaveSuccess(); | |||||
if (viewerLoaded) { | |||||
await PSPDFKit.unload(viewerRef.current); | |||||
} | |||||
navigate(`/pdf/${record.clientId}`); | |||||
} catch (error) { | |||||
console.error('Error saving PDF:', error); | |||||
alert('Failed to save PDF.'); | |||||
} | |||||
setRecord({ | |||||
id: response.data.data.id, | |||||
clientId: record.clientId, | |||||
templateId: record.templateId | |||||
}); | |||||
notifySaveSuccess()}); | |||||
return { | |||||
code: window.AdobeDC.View.Enum.ApiResponseCode.SUCCESS, | |||||
data: { metaData } // Return metaData to prevent t.data undefined | |||||
}; | |||||
} catch (error) { | |||||
console.error('Error saving PDF:', error); | |||||
alert('Failed to save PDF.'); | |||||
return { code: window.AdobeDC.View.Enum.ApiResponseCode.FAIL }; | |||||
} | } | ||||
}; | }; | ||||
@@ -224,11 +248,22 @@ function PDF() { | |||||
const handleBack = async () => { | const handleBack = async () => { | ||||
if (viewerLoaded) { | if (viewerLoaded) { | ||||
await PSPDFKit.unload(viewerRef.current); | |||||
await Nutrient.unload(viewerRef.current); | |||||
} | } | ||||
navigate(`/pdf/${record.clientId}`); | navigate(`/pdf/${record.clientId}`); | ||||
}; | }; | ||||
const handleTest = () => { | |||||
// const element = document.getElementById('pdfViewer'); | |||||
// const inputFields = element.querySelectorAll('iframe')[0]; | |||||
// console.log(element); | |||||
// console.log(inputFields.contentDocument); | |||||
// inputFields.forEach(input => { | |||||
// console.log('Name:', input.name, 'Value:', input.value); | |||||
// }); | |||||
}; | |||||
return ( | return ( | ||||
<ThemeProvider theme={LIONER_BUTTON_THEME}> | <ThemeProvider theme={LIONER_BUTTON_THEME}> | ||||
<div className="pdf-form-page"> {/* This is your 'pdfForm' page */} | <div className="pdf-form-page"> {/* This is your 'pdfForm' page */} | ||||
@@ -236,15 +271,16 @@ function PDF() { | |||||
<Grid item> | <Grid item> | ||||
<Grid container> | <Grid container> | ||||
<Grid item sx={{ml:3, mr:1.5, mb:2}}> | <Grid item sx={{ml:3, mr:1.5, mb:2}}> | ||||
<Button | |||||
{/* <Button | |||||
variant="contained" | variant="contained" | ||||
type="submit" | type="submit" | ||||
color="save" | color="save" | ||||
disabled={!viewerLoaded} | |||||
onClick={handleSavePdf} | |||||
// disabled={!viewerLoaded} | |||||
onClick={handleTest} | |||||
// onClick={handleSavePdf} | |||||
> | > | ||||
Save | Save | ||||
</Button> | |||||
</Button> */} | |||||
</Grid> | </Grid> | ||||
<Grid item sx={{ml:{xs:1.5, md:1.5, lg:1.5}, mr:1.5, mb:2}}> | <Grid item sx={{ml:{xs:1.5, md:1.5, lg:1.5}, mr:1.5, mb:2}}> | ||||
<Button | <Button | ||||
@@ -252,7 +288,7 @@ function PDF() { | |||||
color="cancel" | color="cancel" | ||||
onClick={viewerLoaded ? handleBackClick : handleBack} | onClick={viewerLoaded ? handleBackClick : handleBack} | ||||
> | > | ||||
Cancel | |||||
Back | |||||
</Button> | </Button> | ||||
<GeneralConfirmWindow | <GeneralConfirmWindow | ||||
isWindowOpen={isWindowOpen} | isWindowOpen={isWindowOpen} | ||||
@@ -263,6 +299,18 @@ function PDF() { | |||||
onConfirmClose={handleBack} | onConfirmClose={handleBack} | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
{/* <Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}> | |||||
<InputLabel htmlFor="remarks">Remarks</InputLabel> | |||||
</Grid> | |||||
<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}> | |||||
<TextField | |||||
fullWidth | |||||
inputProps={{maxLength: 500}} | |||||
id="remarks" | |||||
autoComplete="off" | |||||
/> | |||||
</Grid> */} | |||||
</Grid> | </Grid> | ||||
</Grid> | </Grid> | ||||
{/* {pdfLoaded ? ( | {/* {pdfLoaded ? ( | ||||
@@ -278,18 +326,25 @@ function PDF() { | |||||
<p>Loading PDF viewer...</p> | <p>Loading PDF viewer...</p> | ||||
)} */} | )} */} | ||||
</header> | </header> | ||||
<div | |||||
{!pdfLoaded && (<LoadingComponent/>)} | |||||
<div id="adobe-dc-view" ref={viewerRef} style={{ | |||||
width: '100%', | |||||
height: 'calc(100vh - 180px)', // Adjust height based on header/footer | |||||
border: '1px solid #ccc', | |||||
}} | |||||
hidden={!pdfLoaded} | |||||
/> | |||||
{/* <div | |||||
// id={viewerRef} | // id={viewerRef} | ||||
ref={viewerRef} | ref={viewerRef} | ||||
// className="pdf-viewer-container" | // className="pdf-viewer-container" | ||||
style={{ | style={{ | ||||
width: '100%', | width: '100%', | ||||
height: 'calc(100vh - 180px)', // Adjust height based on header/footer | height: 'calc(100vh - 180px)', // Adjust height based on header/footer | ||||
border: '1px solid #ccc' | |||||
border: '1px solid #ccc', | |||||
}} | }} | ||||
> | > | ||||
{/* The PDF SDK will render the PDF viewer here */} | |||||
</div> | |||||
</div> */} | |||||
</div> | </div> | ||||
</ThemeProvider> | </ThemeProvider> | ||||
); | ); | ||||
@@ -41,6 +41,12 @@ import {ExpandMore} from "@mui/icons-material"; | |||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | ||||
import { isNull } from 'lodash'; | import { isNull } from 'lodash'; | ||||
import DialogTitle from '@mui/material/DialogTitle'; | |||||
import DialogContent from '@mui/material/DialogContent'; | |||||
import DialogContentText from '@mui/material/DialogContentText'; | |||||
import DialogActions from '@mui/material/DialogActions'; | |||||
import Dialog from '@mui/material/Dialog'; | |||||
// ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
// const EventSearchForm = ({applySearch, refTemplateData, isUpdating, | // const EventSearchForm = ({applySearch, refTemplateData, isUpdating, | ||||
@@ -50,6 +56,8 @@ const PdfSearchForm = ({applySearch, setExpanded,expanded, clientId}) => { | |||||
const navigate = useNavigate() | const navigate = useNavigate() | ||||
const ability = useContext(AbilityContext); | const ability = useContext(AbilityContext); | ||||
const [isWindowOpen, setIsWindowOpen] = useState(false); | |||||
const [createDateFrom, setCreateDateFrom] = useState(null); | const [createDateFrom, setCreateDateFrom] = useState(null); | ||||
const [createDateTo, setCreateDateTo] = useState(null); | const [createDateTo, setCreateDateTo] = useState(null); | ||||
@@ -77,25 +85,25 @@ const PdfSearchForm = ({applySearch, setExpanded,expanded, clientId}) => { | |||||
} | } | ||||
}, [createDateToError]); | }, [createDateToError]); | ||||
const createNewForm = () => { | |||||
navigate(`/pdf/maintain/-${clientId}T${1}`); | |||||
const createNewForm = (templateId) => { | |||||
navigate(`/pdf/maintain/-${clientId}T${templateId}`); | |||||
}; | }; | ||||
const createFormUpDown = () => { | |||||
navigate(`/pdf/form-up-down/-${clientId}T${1}`); | |||||
}; | |||||
// const createFormUpDown = () => { | |||||
// navigate(`/pdf/form-up-down/-${clientId}T${1}`); | |||||
// }; | |||||
const createFormIDA = () => { | |||||
navigate(`/pdf/newIDA/${clientId}`); | |||||
}; | |||||
// const createFormIDA = () => { | |||||
// navigate(`/pdf/newIDA/${clientId}`); | |||||
// }; | |||||
const createFormFNA = () => { | |||||
navigate(`/pdf/newFNA/${clientId}`); | |||||
}; | |||||
// const createFormFNA = () => { | |||||
// navigate(`/pdf/newFNA/${clientId}`); | |||||
// }; | |||||
const createFormHSBCFIN = () => { | |||||
navigate(`/pdf/newHSBCFIN/${clientId}`); | |||||
}; | |||||
// const createFormHSBCFIN = () => { | |||||
// navigate(`/pdf/newHSBCFIN/${clientId}`); | |||||
// }; | |||||
const onSubmit = (data) => { | const onSubmit = (data) => { | ||||
const criteria = { | const criteria = { | ||||
@@ -299,26 +307,90 @@ const PdfSearchForm = ({applySearch, setExpanded,expanded, clientId}) => { | |||||
</Grid> | </Grid> | ||||
</Grid> | </Grid> | ||||
<Grid container spacing={1}> | |||||
{/* <Grid item> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
onClick={createNewForm} | |||||
> | |||||
New | |||||
</Button> | |||||
<Grid item> | |||||
<Grid container> | |||||
{ability.can('EDIT','EVENT') ? | |||||
<Grid item sx={{ml:3, mr:3, mb:0.5}}> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
onClick={() => setIsWindowOpen(true)} | |||||
> | |||||
New Form | |||||
</Button> | |||||
<Dialog | |||||
open={isWindowOpen} | |||||
onClose={() => setIsWindowOpen(false)} | |||||
aria-labelledby="alert-dialog-title" | |||||
aria-describedby="alert-dialog-description" | |||||
PaperProps={{ | |||||
sx: { | |||||
maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '30vw' }, | |||||
maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' } | |||||
} | |||||
}} | |||||
> | |||||
<DialogTitle id="alert-dialog-title"> | |||||
<Typography variant="lionerBold" component="span"> | |||||
Select Template | |||||
</Typography> | |||||
</DialogTitle> | |||||
<DialogContent> | |||||
<DialogContentText id="alert-dialog-description"> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
sx={{mr: 1}} | |||||
onClick={() => createNewForm(1)} | |||||
> | |||||
Lioner IDA | |||||
</Button> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
sx={{mr: 1}} | |||||
onClick={() => createNewForm(2)} | |||||
> | |||||
Lioner FNA | |||||
</Button> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
sx={{mr: 1}} | |||||
onClick={() => createNewForm(3)} | |||||
> | |||||
HSBC FIN | |||||
</Button> | |||||
</DialogContentText> | |||||
</DialogContent> | |||||
<DialogActions> | |||||
<Button onClick={() => setIsWindowOpen(false)}> | |||||
<Typography variant="lionerSize" component="span"> | |||||
Cancel | |||||
</Typography> | |||||
</Button> | |||||
</DialogActions> | |||||
</Dialog> | |||||
{/* <Button | |||||
variant="contained" | |||||
color="create" | |||||
onClick={createFormUpDown} | |||||
> | |||||
New Form By Upload/Download | |||||
</Button> */} | |||||
</Grid> | |||||
: | |||||
<Grid/> | |||||
} | |||||
</Grid> | </Grid> | ||||
<Grid item> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
onClick={createFormUpDown} | |||||
> | |||||
New By Upload/Download | |||||
</Button> | |||||
</Grid> */} | |||||
<Grid item> | |||||
{/* <Grid item> | |||||
<Button | <Button | ||||
variant="contained" | variant="contained" | ||||
color="create" | color="create" | ||||
@@ -416,7 +488,7 @@ const PdfSearchForm = ({applySearch, setExpanded,expanded, clientId}) => { | |||||
> | > | ||||
New SL Saving | New SL Saving | ||||
</Button> | </Button> | ||||
</Grid> | |||||
</Grid> */} | |||||
</Grid> | </Grid> | ||||
</ThemeProvider> | </ThemeProvider> | ||||
</Grid> | </Grid> | ||||
@@ -85,9 +85,22 @@ export default function PdfTable({recordList}) { | |||||
headerName: 'Form Name', | headerName: 'Form Name', | ||||
flex: 2, | flex: 2, | ||||
renderCell: (params) => { | renderCell: (params) => { | ||||
const getFormLabel = (value) => { | |||||
switch (value) { | |||||
case 1: | |||||
return 'Lioner IDA'; | |||||
case 2: | |||||
return 'Lioner FNA'; | |||||
case 3: | |||||
return 'HSBC FIN'; | |||||
default: | |||||
return 'Form'; | |||||
} | |||||
}; | |||||
return ( | return ( | ||||
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | ||||
{params.value} | |||||
{getFormLabel(params.value)} | |||||
</div> | </div> | ||||
); | ); | ||||
} | } | ||||
@@ -203,9 +216,6 @@ export default function PdfTable({recordList}) { | |||||
return ( | return ( | ||||
<div> | <div> | ||||
<h2 style={{ marginBottom: '16px', color: '#333' }}> | |||||
Existing Forms | |||||
</h2> | |||||
<DataGrid | <DataGrid | ||||
rows={rows} | rows={rows} | ||||
columns={columns} | columns={columns} | ||||
@@ -9,6 +9,7 @@ import {apiPath} from "../../../auth/utils"; | |||||
import { | import { | ||||
// GET_DIVISION_FROM_SUB_DIVISION, | // GET_DIVISION_FROM_SUB_DIVISION, | ||||
GET_PDF_PATH, | GET_PDF_PATH, | ||||
GET_CLIENT_PATH, | |||||
// GET_SEARCH_TEMPLATE_COMBO_PATH, | // GET_SEARCH_TEMPLATE_COMBO_PATH, | ||||
// GET_SEARCH_TEMPLATE_PATH | // GET_SEARCH_TEMPLATE_PATH | ||||
} from "../../../utils/ApiPathConst"; | } from "../../../utils/ApiPathConst"; | ||||
@@ -33,6 +34,7 @@ import {useParams} from "react-router-dom"; | |||||
const PdfSearchPage = () => { | const PdfSearchPage = () => { | ||||
const [onReady, setOnReady] = useState(false); | const [onReady, setOnReady] = useState(false); | ||||
const [expanded, setExpanded] = React.useState(true); | const [expanded, setExpanded] = React.useState(true); | ||||
const [clientInfo,setClientInfo] = useState([]); | |||||
const [record,setRecord] = useState([]); | const [record,setRecord] = useState([]); | ||||
const [searchCriteria, setSearchCriteria] = useState({}); | const [searchCriteria, setSearchCriteria] = useState({}); | ||||
const params = useParams(); | const params = useParams(); | ||||
@@ -62,6 +64,21 @@ const PdfSearchPage = () => { | |||||
} | } | ||||
useEffect(() => { | useEffect(() => { | ||||
if (params.id) { | |||||
if (params.id > 0) {console.log("loggg"); | |||||
axios.get(`${apiPath}${GET_CLIENT_PATH}/${params.id}` | |||||
) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
setClientInfo(response.data); | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
console.log(error); | |||||
return false; | |||||
}); | |||||
} | |||||
} | |||||
setSearchCriteria({clientId: params.id, | setSearchCriteria({clientId: params.id, | ||||
...searchCriteria}); | ...searchCriteria}); | ||||
}, [params.id]); | }, [params.id]); | ||||
@@ -80,6 +97,15 @@ const PdfSearchPage = () => { | |||||
return ( | return ( | ||||
<Grid container rowSpacing={3} columnSpacing={2.75} > | <Grid container rowSpacing={3} columnSpacing={2.75} > | ||||
<ThemeProvider theme={LIONER_FORM_THEME}> | <ThemeProvider theme={LIONER_FORM_THEME}> | ||||
<Grid item xs={12} md={12} lg={12} > | |||||
<Grid container maxWidth justifyContent="space-between" sx={{mt:-2, width:CARD_MAX_WIDTH}} > | |||||
<Grid item xs={4} s={4} md={4} lg={4} | |||||
sx={{ mb: 2, display: 'flex', alignItems: 'center'}}> | |||||
<Typography variant="h2">{clientInfo.data? clientInfo.data.fullname:""}</Typography> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid item xs={12} md={12} lg={12} > | <Grid item xs={12} md={12} lg={12} > | ||||
<Grid container maxWidth justifyContent="space-between" sx={{mt:-2, width:CARD_MAX_WIDTH}} > | <Grid container maxWidth justifyContent="space-between" sx={{mt:-2, width:CARD_MAX_WIDTH}} > | ||||
<Grid item xs={4} s={4} md={4} lg={4} | <Grid item xs={4} s={4} md={4} lg={4} | ||||
@@ -104,12 +130,22 @@ const PdfSearchPage = () => { | |||||
/> | /> | ||||
</Grid> | </Grid> | ||||
<Grid item xs={12} md={12} lg={12} > | |||||
<Grid container maxWidth justifyContent="space-between" sx={{mt:-2, width:CARD_MAX_WIDTH}} > | |||||
<Grid item xs={4} s={4} md={4} lg={4} | |||||
sx={{ mb: -2.25, display: 'flex', alignItems: 'center'}}> | |||||
<Typography variant="h4">Existing Forms</Typography> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
{!onReady? <LoadingComponent/> : | {!onReady? <LoadingComponent/> : | ||||
// PDF Table | // PDF Table | ||||
<Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
<MainCard elevation={0} | <MainCard elevation={0} | ||||
content={false} | content={false} | ||||
sx={{mt:{lg:-1.5}, width: CARD_MAX_WIDTH}} | |||||
sx={{//mt:{lg:-1.5}, | |||||
width: CARD_MAX_WIDTH}} | |||||
> | > | ||||
<div style={{/*height: expanded? '46vh' : '75vh',*/ width: '100%'}}> | <div style={{/*height: expanded? '46vh' : '75vh',*/ width: '100%'}}> | ||||
@@ -15,6 +15,7 @@ const PdfMaintainPage = Loadable(lazy(() => import('pages/pdf/PdfMaintainPage')) | |||||
const PdfFormUpAndDown = Loadable(lazy(() => import('pages/pdf/PdfFormUpAndDown'))); | const PdfFormUpAndDown = Loadable(lazy(() => import('pages/pdf/PdfFormUpAndDown'))); | ||||
const PdfViewer = Loadable(lazy(() => import('pages/pdf/PdfViewer'))); | const PdfViewer = Loadable(lazy(() => import('pages/pdf/PdfViewer'))); | ||||
const PdfSearchPage = Loadable(lazy(() => import('pages/pdf/PdfSearchPage'))); | const PdfSearchPage = Loadable(lazy(() => import('pages/pdf/PdfSearchPage'))); | ||||
const TemplateSearchPage = Loadable(lazy(() => import('pages/pdf/TemplateSearchPage'))); | |||||
// ==============================|| AUTH ROUTING ||============================== // | // ==============================|| AUTH ROUTING ||============================== // | ||||
@@ -64,7 +65,17 @@ const ClientRoutes =() => { | |||||
<Navigate to="/" /> | <Navigate to="/" /> | ||||
) | ) | ||||
), | ), | ||||
}, | |||||
}, | |||||
{ | |||||
path: '/template/', | |||||
element: ( | |||||
handleRouteAbility( | |||||
ability.can('VIEW', 'DASHBOARD'), | |||||
<TemplateSearchPage />, | |||||
<Navigate to="/" /> | |||||
) | |||||
), | |||||
}, | |||||
{ | { | ||||
path: '/pdf/form-up-down/:id', | path: '/pdf/form-up-down/:id', | ||||
element: ( | element: ( | ||||
@@ -6,6 +6,7 @@ import MainLayout from 'layout/MainLayout'; | |||||
import AbilityContext from "../components/AbilityProvider"; | import AbilityContext from "../components/AbilityProvider"; | ||||
import {handleRouteAbility} from "../utils/CommonFunction"; | import {handleRouteAbility} from "../utils/CommonFunction"; | ||||
import {Navigate} from "react-router"; | import {Navigate} from "react-router"; | ||||
import ClientSearchPage from 'pages/client/ClientSearchPage/index'; | |||||
// render - dashboard | // render - dashboard | ||||
//const DashboardDefault = Loadable(lazy(() => import('pages/dashboard'))); | //const DashboardDefault = Loadable(lazy(() => import('pages/dashboard'))); | ||||
@@ -47,7 +48,8 @@ const MainRoutes = () => { | |||||
element: ( | element: ( | ||||
handleRouteAbility( | handleRouteAbility( | ||||
ability.can('VIEW', 'DASHBOARD'), | ability.can('VIEW', 'DASHBOARD'), | ||||
<LIONERDashboard />, | |||||
<ClientSearchPage />, | |||||
// <LIONERDashboard />, | |||||
<Navigate to="/client" /> | <Navigate to="/client" /> | ||||
) | ) | ||||
), | ), | ||||
@@ -878,6 +878,20 @@ export function UploadFileWindow({ isWindowOpen, title, video, onNormalClose, on | |||||
}); | }); | ||||
} | } | ||||
} | } | ||||
const handleDragOver = (e) => { | |||||
e.preventDefault(); | |||||
e.stopPropagation(); | |||||
}; | |||||
const handleDrop = (e) => { | |||||
e.preventDefault(); | |||||
e.stopPropagation(); | |||||
const droppedFiles = Array.from(e.dataTransfer.files); | |||||
console.log(droppedFiles); | |||||
// setFiles((prevFiles) => [...prevFiles, ...droppedFiles]); | |||||
}; | |||||
return ( | return ( | ||||
<Dialog | <Dialog | ||||
PaperProps={{ | PaperProps={{ | ||||
@@ -972,7 +986,57 @@ export function UploadFileWindow({ isWindowOpen, title, video, onNormalClose, on | |||||
</Grid> | </Grid> | ||||
</Grid> | </Grid> | ||||
) : ( | ) : ( | ||||
<Grid /> | |||||
<Grid container alignItems={'center'} sx={{ mt: 3 }}> | |||||
<Grid item xs={7} s={7} md={7} lg={7}> | |||||
<FormControl fullWidth required> | |||||
<Grid container alignItems={'flex-start'} sx={{ mt: 3 }}> | |||||
<Grid item xs={7} s={7} md={7} lg={8} sx={{display: 'flex', alignItems: 'flex-start' }}> | |||||
<MuiFileInput | |||||
inputProps={{ | |||||
accept: '.png, .jpeg, .jpg', | |||||
}} | |||||
InputProps={{ | |||||
padding: '7.5px 8px 7.5px 12px', | |||||
startAdornment: null, // Set startAdornment to null to disable it | |||||
}} | |||||
value={file} | |||||
onChange={handleFieldChange} | |||||
error={!!errors.error} | |||||
helperText={errors.error} | |||||
// disabled={video.mimetype !== 'video'} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4} s={4} md={4} lg={4} sx={{ display: 'flex', justifyContent: 'flex-end' }}> | |||||
<div | |||||
onDragOver={handleDragOver} | |||||
onDrop={handleDrop}> | |||||
<input | |||||
type="file" | |||||
accept=".png, .jpeg, .jpg" | |||||
style={{ display: 'none' }} | |||||
ref={fileInputRef} | |||||
onChange={handleChange} | |||||
/> | |||||
<Button | |||||
component="label" | |||||
variant="contained" | |||||
startIcon={<CloudUploadIcon />} | |||||
inputprops={{ | |||||
accept: '.png, .jpeg, .jpg', | |||||
startAdornment: <AttachFileIcon /> | |||||
}} | |||||
onClick={handleOpenFileSelect} | |||||
> | |||||
Upload file | |||||
</Button> | |||||
{/*Selected file: {file ? file.name : 'None'}*/} | |||||
</div> | |||||
</Grid> | |||||
</Grid> | |||||
</FormControl> | |||||
</Grid> | |||||
</Grid> | |||||
)} | )} | ||||
<Grid container alignItems={'flex-start'} sx={{ mt: 3 }}> | <Grid container alignItems={'flex-start'} sx={{ mt: 3 }}> | ||||