| @@ -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 }}> | ||||