@@ -7,10 +7,10 @@ | |||||
``` | ``` | ||||
## 2. Config the library | ## 2. Config the library | ||||
- Navigate to "LIONER-frontend\node_modules\pspdfkit\dist" | |||||
- Copy the folder "pspdfkit-lib" | |||||
- Create a new folder "sdk" in "LIONER-frontend\public" | |||||
- Paste the "pspdfkit-lib" inside "sdk" folder | |||||
- Navigate to **"LIONER-frontend\node_modules\pspdfkit\dist"** | |||||
- Copy the folder **"pspdfkit-lib"** | |||||
- Create a new folder **"sdk"** in **"LIONER-frontend\public"** | |||||
- Paste the **"pspdfkit-lib"** inside **"sdk"** folder | |||||
## 3. Run the application | ## 3. Run the application | ||||
- Start the application by the following command: | - Start the application by the following command: | ||||
@@ -35,8 +35,8 @@ export default function ClientTable({recordList}) { | |||||
navigate('/client/maintain/'+ id); | navigate('/client/maintain/'+ id); | ||||
}; | }; | ||||
const handlePDFClick = () => () => { | |||||
navigate('/pdf'); | |||||
const handlePdfClick = (id) => () => { | |||||
navigate(`/pdf/${id}`); | |||||
}; | }; | ||||
const columns = [ | const columns = [ | ||||
@@ -63,7 +63,7 @@ export default function ClientTable({recordList}) { | |||||
icon={<PictureAsPdfIcon sx={{fontSize: 25}}/>} | icon={<PictureAsPdfIcon sx={{fontSize: 25}}/>} | ||||
label="Edit PDF" | label="Edit PDF" | ||||
className="textPrimary" | className="textPrimary" | ||||
onClick={handlePDFClick()} | |||||
onClick={handlePdfClick(id)} | |||||
color="edit" | color="edit" | ||||
// disabled={'true'} | // disabled={'true'} | ||||
// disabled={!ability.can('VIEW','DASHBOARD')} | // disabled={!ability.can('VIEW','DASHBOARD')} | ||||
@@ -1,61 +1,107 @@ | |||||
import React, { useEffect, useRef, useState } from 'react'; | import React, { useEffect, useRef, useState } from 'react'; | ||||
import { Button, Grid } from '@mui/material'; | import { Button, Grid } from '@mui/material'; | ||||
import { GeneralConfirmWindow } from "../../utils/CommonFunction"; | |||||
import { GeneralConfirmWindow, notifySaveSuccess } from "../../../utils/CommonFunction"; | |||||
import axios from 'axios'; | import axios from 'axios'; | ||||
import {apiPath} from "../../auth/utils"; | |||||
import {apiPath} from "../../../auth/utils"; | |||||
import { | import { | ||||
GET_PDF_TEMPLATE_PATH, | |||||
GET_PDF_PATH, | GET_PDF_PATH, | ||||
POST_PDF_PATH | POST_PDF_PATH | ||||
} from "../../utils/ApiPathConst"; | |||||
import {LIONER_BUTTON_THEME} from "../../themes/colorConst"; | |||||
} from "../../../utils/ApiPathConst"; | |||||
import {LIONER_BUTTON_THEME} from "../../../themes/colorConst"; | |||||
import {ThemeProvider} from "@emotion/react"; | import {ThemeProvider} from "@emotion/react"; | ||||
import {useNavigate} from "react-router-dom"; | import {useNavigate} from "react-router-dom"; | ||||
import {useForm} from "react-hook-form"; | |||||
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 { CollectionsBookmarkRounded } from '../../../../node_modules/@mui/icons-material/index'; | |||||
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 instanceRef = useRef(null); // Ref for the PSPDFKit instance | |||||
const [pdfLoaded, setPdfLoaded] = useState(false); | const [pdfLoaded, setPdfLoaded] = useState(false); | ||||
const [viewerLoaded, setViewerLoaded] = useState(false); | const [viewerLoaded, setViewerLoaded] = useState(false); | ||||
const [template,setTemplate] = useState(); | |||||
const [viewInstance,setViewInstance] = useState(); | const [viewInstance,setViewInstance] = useState(); | ||||
const [pdfBytes, setPdfBytes] = useState(); | |||||
const [record, setRecord] = useState(); | |||||
const navigate = useNavigate() | const navigate = useNavigate() | ||||
const params = useParams(); | |||||
const location = useLocation(); | |||||
const queryParams = new URLSearchParams(location.search); | |||||
const refId = queryParams.get("refId"); | |||||
const loadPdfTemplate = async () => { | |||||
// if (!pdfLoaded) { | |||||
axios.get(`${apiPath}${GET_PDF_PATH}`, { | |||||
responseType: 'arraybuffer', // Essential for binary data | |||||
}) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
setTemplate(response.data); | |||||
setPdfLoaded(true); | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
console.log(error); | |||||
return false; | |||||
}); | |||||
// } | |||||
const loadPdfForm = async (id, templateId = 0) => { | |||||
if (!pdfLoaded) { | |||||
if (id > 0) { | |||||
// axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | |||||
axios.get(`${apiPath}${GET_PDF_PATH}/${id}`, { | |||||
// responseType: 'arraybuffer', // Essential for binary data | |||||
}) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
// setRecord(rec => { | |||||
// const { ["blobValue"]: _, ...newData } = response.data; // Destructure to remove blobValue | |||||
// return newData; | |||||
// }); | |||||
const res = response.data; | |||||
setRecord({ // WIP - allow to update all record | |||||
id : res.id, | |||||
clientId: res.clientId, | |||||
templateId: res.templateId | |||||
}) | |||||
//Convert Base64 to binary data | |||||
const byteChar = atob(response.data.blobValue); | |||||
const byteNum = new Uint8Array(byteChar.length); | |||||
for (let i = 0; i < byteChar.length; i++) { | |||||
byteNum[i] = byteChar.charCodeAt(i); | |||||
} | |||||
setPdfBytes(byteNum); | |||||
setPdfLoaded(true); | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
console.log(error); | |||||
return false; | |||||
}); | |||||
} else { | |||||
axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, { | |||||
responseType: 'arraybuffer', // Essential for binary data | |||||
}) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
setPdfBytes(response.data); | |||||
setPdfLoaded(true); | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
console.log(error); | |||||
return false; | |||||
}); | |||||
} | |||||
} | |||||
}; | }; | ||||
const loadPdfViewer = async () => {console.log(`PDF loaded = ${pdfLoaded}`); | |||||
if (pdfLoaded && viewerRef.current) { | |||||
const loadPdfViewer = async () => { | |||||
if (pdfLoaded && viewerRef.current && !viewerLoaded) { | |||||
try { | try { | ||||
//New try | //New try | ||||
console.log(typeof(template)); | |||||
const pdfBlob = new Blob([template], { type: 'application/pdf' }); | |||||
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' }); | |||||
console.log(pdfBlob); | |||||
const pdfUrl = URL.createObjectURL(pdfBlob); | const pdfUrl = URL.createObjectURL(pdfBlob); | ||||
console.log('Template: '); | |||||
console.log(template.data); | |||||
console.log('URL: '); | |||||
console.log(pdfUrl); | |||||
console.log('Viewer: '); | |||||
console.log(viewerRef.current); | |||||
PSPDFKit.unload(viewerRef.current); | |||||
// 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 | await PSPDFKit.load({//click into load | ||||
container: viewerRef.current, | container: viewerRef.current, | ||||
// container: '#viewer', | // container: '#viewer', | ||||
@@ -76,6 +122,13 @@ function PDF() { | |||||
console.log('instance: '); | console.log('instance: '); | ||||
console.log(instance); | console.log(instance); | ||||
setViewerLoaded(true); | 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; | // instanceRef.current = instance; | ||||
@@ -95,32 +148,55 @@ function PDF() { | |||||
}; | }; | ||||
useEffect(() => { | useEffect(() => { | ||||
loadPdfTemplate(); | |||||
}, []); | |||||
if (params.id !== null) { | |||||
const pdfData = (params.id).split("T"); | |||||
if (pdfData[0] > 0) { // Existing Record | |||||
loadPdfForm(pdfData[0]); | |||||
} else { // New Record | |||||
setRecord({ | |||||
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 | |||||
} | |||||
} | |||||
}, [params.id]); | |||||
// useEffect(() => { | |||||
// if (record) { | |||||
// console.log(record); | |||||
// loadPdfForm(); | |||||
// } | |||||
// }, [record]); | |||||
useEffect(() => { | useEffect(() => { | ||||
loadPdfViewer(); | loadPdfViewer(); | ||||
}, [viewerRef, pdfLoaded]); | |||||
}, [viewerRef.current, pdfLoaded]); | |||||
const handleSavePdf = async () => { | const handleSavePdf = async () => { | ||||
if (viewInstance.current) {console.log("tetes"); | |||||
if (viewInstance) { | |||||
try { | try { | ||||
// Export the filled PDF from the SDK as an ArrayBuffer | // Export the filled PDF from the SDK as an ArrayBuffer | ||||
const arrayBuffer = await viewInstance.current.exportPDF(); | |||||
const arrayBuffer = await viewInstance.exportPDF(); | |||||
const filledPdfBlob = new Blob([arrayBuffer], { type: 'application/pdf' }); | const filledPdfBlob = new Blob([arrayBuffer], { type: 'application/pdf' }); | ||||
// Create FormData to send the file to Spring Boot | // Create FormData to send the file to Spring Boot | ||||
const formData = new FormData(); | const formData = new FormData(); | ||||
formData.append('file', filledPdfBlob, 'filled_form.pdf'); | 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 | // Send the filled PDF to your Spring Boot backend's save endpoint | ||||
const response = await axios.post(`${apiPath}${POST_PDF_PATH}`, formData, { | const response = await axios.post(`${apiPath}${POST_PDF_PATH}`, formData, { | ||||
headers: { | headers: { | ||||
'Content-Type': 'multipart/form-data' // Important for file uploads | 'Content-Type': 'multipart/form-data' // Important for file uploads | ||||
} | |||||
}, | |||||
}); | }); | ||||
console.log('PDF saved on server:', response.data); | console.log('PDF saved on server:', response.data); | ||||
alert('PDF saved successfully on server!'); | |||||
notifySaveSuccess(); | |||||
if (viewerLoaded) { | |||||
await PSPDFKit.unload(viewerRef.current); | |||||
} | |||||
navigate(`/pdf/${record.clientId}`); | |||||
} catch (error) { | } catch (error) { | ||||
console.error('Error saving PDF:', error); | console.error('Error saving PDF:', error); | ||||
alert('Failed to save PDF.'); | alert('Failed to save PDF.'); | ||||
@@ -146,8 +222,11 @@ function PDF() { | |||||
setIsWindowOpen(true); | setIsWindowOpen(true); | ||||
}; | }; | ||||
const handleBack = () => { | |||||
navigate('/client'); | |||||
const handleBack = async () => { | |||||
if (viewerLoaded) { | |||||
await PSPDFKit.unload(viewerRef.current); | |||||
} | |||||
navigate(`/pdf/${record.clientId}`); | |||||
}; | }; | ||||
return ( | return ( | ||||
@@ -162,7 +241,7 @@ function PDF() { | |||||
type="submit" | type="submit" | ||||
color="save" | color="save" | ||||
disabled={!viewerLoaded} | disabled={!viewerLoaded} | ||||
// onClick={handleSavePdf} | |||||
onClick={handleSavePdf} | |||||
> | > | ||||
Save | Save | ||||
</Button> | </Button> |
@@ -0,0 +1,312 @@ | |||||
// material-ui | |||||
import { | |||||
Button, | |||||
Typography, | |||||
Grid, TextField, InputLabel, CardActions | |||||
} from '@mui/material'; | |||||
import MainCard from "../../../components/MainCard"; | |||||
import {useForm} from "react-hook-form"; | |||||
import {useContext, useState} from "react"; | |||||
// import {useContext, useEffect, useState} from "react"; | |||||
import Autocomplete from '@mui/material/Autocomplete'; | |||||
import * as React from "react"; | |||||
// import axios from "axios"; | |||||
// import {apiPath, getUserData} from "../../../auth/utils"; | |||||
// import { | |||||
// GET_CONSULTANT_COMBO_LIST, GET_EVENT_EXPORT, | |||||
// GET_SUB_CONSULTANT_COMBO_LIST, POST_SEARCH_TEMPLATE_PATH, VALIDATE_TEMPLATE_NAME_PATH, | |||||
// } from "../../../utils/ApiPathConst"; | |||||
import {LIONER_BUTTON_THEME} from "../../../themes/colorConst"; | |||||
// import { | |||||
// GeneralCreateTemplateWindow, | |||||
// getComboValueByIdList, getComboValueByLabel, | |||||
// getIdList, isOptionEqualToValue, notifySaveSuccess, | |||||
// } from "../../../utils/CommonFunction"; | |||||
// import Qs from 'qs'; | |||||
import {useNavigate} from "react-router"; | |||||
import {ThemeProvider} from "@emotion/react"; | |||||
// import UploadContext from "../../../components/UploadProvider"; | |||||
// import {EVENT_REGION_COMBO, EVENT_TYPE_COMBO} from "../../../utils/ComboConst"; | |||||
import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | |||||
import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | |||||
import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | |||||
import dayjs from "dayjs"; | |||||
// import {isObjEmpty} from "../../../utils/Utils"; | |||||
// import {useLocation} from "react-router-dom"; | |||||
import AbilityContext from "../../../components/AbilityProvider"; | |||||
import {LIONER_FORM_THEME, CARD_MAX_WIDTH} from "../../../themes/themeConst"; | |||||
import Collapse from '@mui/material/Collapse'; | |||||
import {ExpandMore} from "@mui/icons-material"; | |||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | |||||
import { isNull } from 'lodash'; | |||||
// ==============================|| DASHBOARD - DEFAULT ||============================== // | |||||
// const EventSearchForm = ({applySearch, refTemplateData, isUpdating, | |||||
// setIsUpdating, getTemplateList,setExpanded,expanded,userConsultant}) => { | |||||
const PdfSearchForm = ({applySearch, setExpanded,expanded, clientId}) => { | |||||
const navigate = useNavigate() | |||||
const ability = useContext(AbilityContext); | |||||
const [createDateFrom, setCreateDateFrom] = useState(null); | |||||
const [createDateTo, setCreateDateTo] = useState(null); | |||||
const { setValue, reset, register, handleSubmit, getValues } = useForm() | |||||
const handleExpandClick = () => { | |||||
setExpanded(!expanded); | |||||
}; | |||||
const [createDateFromError, setCreateDateFromError] = React.useState(null); | |||||
const [createDateToError, setCreateDateToError] = React.useState(null); | |||||
const createDateFromErrorMessage = React.useMemo(() => { | |||||
switch (createDateFromError) { | |||||
case 'invalidDate': { | |||||
return "Invalid date"; | |||||
} | |||||
} | |||||
}, [createDateFromError]); | |||||
const createDateToErrorMessage = React.useMemo(() => { | |||||
switch (createDateToError) { | |||||
case 'invalidDate': { | |||||
return "Invalid date"; | |||||
} | |||||
} | |||||
}, [createDateToError]); | |||||
const createNewForm = () => { | |||||
navigate(`/pdf/maintain/-${clientId}T${1}`); | |||||
}; | |||||
const onSubmit = (data) => { | |||||
const criteria = { | |||||
...data, | |||||
createDateFrom: createDateFrom === null ? null : dayjs(createDateFrom).format('YYYY-MM-DD'), | |||||
createDateTo: createDateTo === null ? null : dayjs(createDateTo).format('YYYY-MM-DD'), | |||||
}; | |||||
applySearch(criteria); | |||||
}; | |||||
// function onExport(){ | |||||
// const data = getValues(); | |||||
// const temp = { | |||||
// ...data, | |||||
// fromDate: fromDate === null ? null : dayjs(fromDate).format('YYYY-MM-DD'), | |||||
// createDateTo: createDateTo === null ? null : dayjs(createDateTo).format('YYYY-MM-DD'), | |||||
// region: selectedRegion === null ? null : selectedRegion.label, | |||||
// type: selectedType === null ? null :selectedType.label, | |||||
// consultantIdList: getIdList(selectedConsultants), | |||||
// caseManagerIdList: getIdList(selectedCaseManager), | |||||
// }; | |||||
// setIsUploading(true); | |||||
// axios.get(`${apiPath}${GET_EVENT_EXPORT}`,{ | |||||
// responseType: 'blob', | |||||
// params: temp, | |||||
// paramsSerializer: function (params) { | |||||
// return Qs.stringify(params, { arrayFormat: 'repeat' }); | |||||
// }, | |||||
// }) | |||||
// .then((response) => { | |||||
// if (response.status === 200) { | |||||
// setIsUploading(false); | |||||
// const url = URL.createObjectURL(new Blob([response.data])); | |||||
// const link = document.createElement("a"); | |||||
// link.href = url; | |||||
// link.setAttribute("download", "event_export_"+ Date.now() + ".xlsx"); | |||||
// link.click(); | |||||
// } | |||||
// }) | |||||
// .catch(error => { | |||||
// setIsUploading(false); | |||||
// console.log(error); | |||||
// return false; | |||||
// }); | |||||
// } | |||||
function resetForm(){ | |||||
setCreateDateFrom(null); | |||||
setCreateDateTo(null); | |||||
reset(); | |||||
} | |||||
const handleBack = () => { | |||||
navigate('/client'); | |||||
}; | |||||
return ( | |||||
<MainCard xs={12} md={12} lg={12} | |||||
content={false} | |||||
sx={{width:CARD_MAX_WIDTH}} | |||||
> | |||||
<Grid item onClick={handleExpandClick}> | |||||
<CardActions disableSpacing> | |||||
{/*row 1*/} | |||||
<Grid item justifyContent="space-between" alignItems="center" sx={{mt:1,ml:2,mb:1}}> | |||||
<Typography variant="lionerSubLabel" > | |||||
Search Criteria(s) | |||||
</Typography> | |||||
</Grid> | |||||
<ExpandMore | |||||
expand={expanded.toString()} | |||||
onClick={handleExpandClick} | |||||
aria-expanded={expanded} | |||||
aria-label="show more" | |||||
style={{ marginLeft: 'auto' }} // Align the button to the right | |||||
> | |||||
<ExpandMoreIcon /> | |||||
</ExpandMore> | |||||
</CardActions> | |||||
</Grid> | |||||
<Collapse in={expanded} timeout="auto" unmountOnExit> | |||||
<form onSubmit={handleSubmit(onSubmit)}> | |||||
<ThemeProvider theme={LIONER_FORM_THEME}> | |||||
{/*row 2*/} | |||||
<Grid container alignItems={"flex-start"} > | |||||
<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}> | |||||
<InputLabel htmlFor="template">Form</InputLabel> | |||||
<TextField | |||||
fullWidth | |||||
{...register("templateId")} | |||||
id='templateId' | |||||
inputProps={{maxLength: 255}} | |||||
// label="Client Name" | |||||
autoComplete="off" | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}> | |||||
<InputLabel htmlFor="createDateFrom">Form Create Date</InputLabel> | |||||
<Grid container> | |||||
<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | |||||
<LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
<DemoItem> | |||||
<DatePicker | |||||
id="createDateFrom" | |||||
onError={(newError) => setCreateDateFromError(newError)} | |||||
slotProps={{ | |||||
field: { clearable: true }, | |||||
textField: { | |||||
helperText: createDateFromErrorMessage, | |||||
}, | |||||
}} | |||||
format="DD/MM/YYYY" | |||||
value={createDateFrom === null ? null : dayjs(createDateFrom)} | |||||
onChange={(newValue) => setCreateDateFrom(newValue)} | |||||
// label="From" | |||||
/> | |||||
</DemoItem > | |||||
</LocalizationProvider> | |||||
</Grid> | |||||
<Grid item xs={1.5} s={1.5} md={1.5} lg={1} sx={{mt:1.3, display: 'flex', justifyContent:"center", alignItems: 'flex-start'}}> | |||||
To | |||||
</Grid> | |||||
<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | |||||
<LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
<DemoItem components={['DatePicker']}> | |||||
<DatePicker | |||||
format="DD/MM/YYYY" | |||||
onError={(newError) => setCreateDateToError(newError)} | |||||
slotProps={{ | |||||
field: { clearable: true }, | |||||
textField: { | |||||
helperText: createDateToErrorMessage, | |||||
}, | |||||
}} | |||||
id="createDateTo" | |||||
//label="To Date" | |||||
value={createDateTo === null ? null : dayjs(createDateTo)} | |||||
onChange={(newValue) => setCreateDateTo(newValue)} | |||||
/> | |||||
</DemoItem > | |||||
</LocalizationProvider> | |||||
</Grid> | |||||
</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> | |||||
<TextField | |||||
fullWidth | |||||
{...register("remarks")} | |||||
inputProps={{maxLength: 500}} | |||||
id="remarks" | |||||
autoComplete="off" | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
{/*last row*/} | |||||
<Grid container maxWidth justifyContent="space-between" sx={{mt:1.5}}> | |||||
<ThemeProvider theme={LIONER_BUTTON_THEME}> | |||||
<Grid item> | |||||
<Grid container> | |||||
<Grid item sx={{ml:3, mr:1.5, mb:2}}> | |||||
<Button | |||||
variant="contained" | |||||
type="submit" | |||||
color="save" | |||||
disabled={createDateFromError || createDateToError} | |||||
onClick={applySearch} | |||||
> | |||||
Search | |||||
</Button> | |||||
</Grid> | |||||
<Grid item sx={{ml:{xs:1.5, md:1.5, lg:1.5}, mr:1.5, mb:2}}> | |||||
<Button | |||||
variant="contained" | |||||
color="cancel" | |||||
onClick={resetForm} | |||||
> | |||||
Reset | |||||
</Button> | |||||
</Grid> | |||||
<Grid item sx={{ml:{xs:1.5, md:1.5, lg:1.5}, mr:1.5, mb:2}}> | |||||
<Button | |||||
variant="contained" | |||||
color="cancel" | |||||
onClick={handleBack} | |||||
> | |||||
Back | |||||
</Button> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid item> | |||||
<Grid container> | |||||
{ability.can('EDIT','EVENT') ? | |||||
<Grid item sx={{ml:3, mr:3, mb:0.5}}> | |||||
<Button | |||||
variant="contained" | |||||
color="create" | |||||
onClick={createNewForm} | |||||
> | |||||
New Form | |||||
</Button> | |||||
</Grid> | |||||
: | |||||
<Grid/> | |||||
} | |||||
</Grid> | |||||
</Grid> | |||||
</ThemeProvider> | |||||
</Grid> | |||||
</ThemeProvider> | |||||
</form> | |||||
</Collapse> | |||||
</MainCard> | |||||
); | |||||
}; | |||||
export default PdfSearchForm; |
@@ -0,0 +1,220 @@ | |||||
// material-ui | |||||
import * as React from 'react'; | |||||
import { | |||||
DataGrid, | |||||
GridActionsCellItem, | |||||
} from "@mui/x-data-grid"; | |||||
import EditIcon from '@mui/icons-material/Edit'; | |||||
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; | |||||
import {useContext, useEffect} from "react"; | |||||
import {useNavigate} from "react-router-dom"; | |||||
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 ||============================== // | |||||
export default function PdfTable({recordList}) { | |||||
const [rows, setRows] = React.useState(recordList); | |||||
const [rowModesModel] = React.useState({}); | |||||
const navigate = useNavigate() | |||||
const ability = useContext(AbilityContext); | |||||
const [paginationModel, setPaginationModel] = React.useState({ | |||||
page: 0, | |||||
pageSize:10 | |||||
}); | |||||
useEffect(() => { | |||||
setPaginationModel({page:0,pageSize:10}); | |||||
setRows(recordList); | |||||
}, [recordList]); | |||||
const handleEditClick = (id) => () => { | |||||
navigate(`/pdf/maintain/${id}`); | |||||
}; | |||||
const columns = [ | |||||
{ | |||||
field: 'actions', | |||||
type: 'actions', | |||||
headerName: 'Actions', | |||||
// flex: 0.5, | |||||
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" | |||||
// disabled={'true'} | |||||
// disabled={!ability.can('VIEW','DASHBOARD')} | |||||
/> | |||||
</ThemeProvider> | |||||
] | |||||
}, | |||||
}, | |||||
// { | |||||
// id: 'title', | |||||
// field: 'title', | |||||
// headerName: 'Title', | |||||
// // sortComparator: dateComparator, | |||||
// flex: 0.75, | |||||
// renderCell: (params) => ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// // <div> | |||||
// // {getDateString(params.row.pdfFrom,false)} | |||||
// // </div> | |||||
// ), | |||||
// }, | |||||
{ | |||||
id: 'templateId', | |||||
field: 'templateId', | |||||
headerName: 'Form Name', | |||||
flex: 2, | |||||
renderCell: (params) => { | |||||
return ( | |||||
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
{params.value} | |||||
</div> | |||||
); | |||||
} | |||||
}, | |||||
{ | |||||
id: 'createDate', | |||||
field: 'createDate', | |||||
headerName: 'Create Date', | |||||
flex: 1, | |||||
sortComparator: dateComparator, | |||||
renderCell: (params) => ( | |||||
<div> | |||||
{getDateString(params.row.created, 'dd/MM/yyyy')} | |||||
</div> | |||||
), | |||||
}, | |||||
{ | |||||
id: 'modified', | |||||
field: 'modified', | |||||
headerName: 'Modified Date', | |||||
flex: 1, | |||||
sortComparator: dateComparator, | |||||
renderCell: (params) => ( | |||||
<div> | |||||
{getDateString(params.row.modified, 'dd/MM/yyyy')} | |||||
</div> | |||||
), | |||||
}, | |||||
// { | |||||
// id: 'lastname', | |||||
// field: 'lastname', | |||||
// headerName: 'Last Name', | |||||
// flex: 1.5, | |||||
// sortComparator: dateComparator, | |||||
// renderCell: (params) => ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// // <div> | |||||
// // {getDateString(params.row.startDate,false)} | |||||
// // </div> | |||||
// ), | |||||
// }, | |||||
// { | |||||
// id: 'firstname', | |||||
// field: 'firstname', | |||||
// headerName: 'First Name', | |||||
// // sortComparator: dateComparator, | |||||
// flex: 2, | |||||
// renderCell: (params) => ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// // <div> | |||||
// // {getDateString(params.row.applicationDeadline,false)} | |||||
// // </div> | |||||
// ), | |||||
// }, | |||||
// { | |||||
// id: 'email', | |||||
// field: 'email', | |||||
// headerName: 'Email', | |||||
// flex: 1.5, | |||||
// renderCell: (params) => { | |||||
// return ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// ); | |||||
// } | |||||
// }, | |||||
// { | |||||
// id: 'phone1', | |||||
// field: 'phone1', | |||||
// headerName: 'Phone No.', | |||||
// flex: 1, | |||||
// renderCell: (params) => { | |||||
// return ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// ); | |||||
// } | |||||
// }, | |||||
// { | |||||
// id: 'phone2', | |||||
// field: 'phone2', | |||||
// headerName: '2nd Phone No.', | |||||
// flex: 1, | |||||
// renderCell: (params) => { | |||||
// return ( | |||||
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
// {params.value} | |||||
// </div> | |||||
// ); | |||||
// } | |||||
// }, | |||||
{ | |||||
id: 'remarks', | |||||
field: 'remarks', | |||||
headerName: 'Remarks', | |||||
flex: 2, | |||||
renderCell: (params) => { | |||||
return ( | |||||
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}> | |||||
{params.value} | |||||
</div> | |||||
); | |||||
} | |||||
}, | |||||
]; | |||||
return ( | |||||
<DataGrid | |||||
rows={rows} | |||||
columns={columns} | |||||
columnHeaderHeight={45} | |||||
editMode="row" | |||||
//autoPageSize | |||||
rowModesModel={rowModesModel} | |||||
getRowHeight={() => 'auto'} | |||||
paginationModel={paginationModel} | |||||
onPaginationModelChange={setPaginationModel} | |||||
slots={{ | |||||
noRowsOverlay: () => ( | |||||
CustomNoRowsOverlay() | |||||
) | |||||
}} | |||||
pageSizeOptions={[10]} | |||||
autoHeight | |||||
/> | |||||
); | |||||
} |
@@ -0,0 +1,132 @@ | |||||
// material-ui | |||||
import { | |||||
Grid, Typography | |||||
} from '@mui/material'; | |||||
import MainCard from "../../../components/MainCard"; | |||||
import {useEffect, useState} from "react"; | |||||
import axios from "axios"; | |||||
import {apiPath} from "../../../auth/utils"; | |||||
import { | |||||
// GET_DIVISION_FROM_SUB_DIVISION, | |||||
GET_PDF_PATH, | |||||
// GET_SEARCH_TEMPLATE_COMBO_PATH, | |||||
// GET_SEARCH_TEMPLATE_PATH | |||||
} from "../../../utils/ApiPathConst"; | |||||
import * as React from "react"; | |||||
import LoadingComponent from "../../extra-pages/LoadingComponent"; | |||||
import PdfTable from "./PdfTable"; | |||||
import PdfSearchForm from "./PdfSearchForm"; | |||||
import Qs from "qs"; | |||||
// import Autocomplete from "@mui/material/Autocomplete"; | |||||
import {isObjEmpty} from "../../../utils/Utils"; | |||||
import {isFormEmpty} from "../../../utils/CommonFunction"; | |||||
// import UploadContext from "../../components/UploadProvider"; | |||||
// import {useLocation} from "react-router-dom"; | |||||
// import dayjs from "dayjs"; | |||||
import {LIONER_FORM_THEME, CARD_MAX_WIDTH} from "../../../themes/themeConst"; | |||||
import {ThemeProvider} from "@emotion/react"; | |||||
import {useParams} from "react-router-dom"; | |||||
// ==============================|| DASHBOARD - DEFAULT ||============================== // | |||||
const PdfSearchPage = () => { | |||||
const [onReady, setOnReady] = useState(false); | |||||
const [expanded, setExpanded] = React.useState(true); | |||||
const [record,setRecord] = useState([]); | |||||
const [searchCriteria, setSearchCriteria] = useState({}); | |||||
const params = useParams(); | |||||
function getPdfList() { | |||||
axios.get(`${apiPath}${GET_PDF_PATH}`, { | |||||
params: searchCriteria, | |||||
// params: isInit? temp : searchCriteria, | |||||
paramsSerializer: function (params) { | |||||
return Qs.stringify(params, { arrayFormat: 'repeat' }); | |||||
}, | |||||
} | |||||
) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
// if (!isFormEmpty(searchCriteria) && !isObjEmpty(response.data.records)) { | |||||
// setExpanded(false); | |||||
// } | |||||
setRecord(response.data.records); | |||||
setOnReady(true); | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
console.log(error); | |||||
return false; | |||||
}); | |||||
} | |||||
useEffect(() => { | |||||
setSearchCriteria({clientId: params.id, | |||||
...searchCriteria}); | |||||
}, [params.id]); | |||||
useEffect(() => { | |||||
if (!isObjEmpty(searchCriteria)) { | |||||
getPdfList(); | |||||
} | |||||
}, [searchCriteria]); | |||||
function applySearch(input) { | |||||
setSearchCriteria({clientId: params.id, | |||||
...input}); | |||||
} | |||||
return ( | |||||
<Grid container rowSpacing={3} columnSpacing={2.75} > | |||||
<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.25, display: 'flex', alignItems: 'center'}}> | |||||
<Typography variant="h4">Search PDF Form</Typography> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
{/* Search Form */} | |||||
<Grid item xs={12} md={12} lg={12}> | |||||
<PdfSearchForm | |||||
// isUpdating={isUpdating} | |||||
// setIsUpdating={setIsUpdating} | |||||
applySearch={applySearch} | |||||
// refTemplateData={refTemplateData} | |||||
// getTemplateList={getTemplateList} | |||||
setExpanded={setExpanded} | |||||
expanded={expanded} | |||||
clientId={params.id} | |||||
// userDivision={userDivision} | |||||
/> | |||||
</Grid> | |||||
{!onReady? <LoadingComponent/> : | |||||
// PDF Table | |||||
<Grid item xs={12} md={12} lg={12}> | |||||
<MainCard elevation={0} | |||||
content={false} | |||||
sx={{mt:{lg:-1.5}, width: CARD_MAX_WIDTH}} | |||||
> | |||||
<div style={{/*height: expanded? '46vh' : '75vh',*/ width: '100%'}}> | |||||
<PdfTable | |||||
recordList={record} | |||||
pageSize={10} | |||||
expanded={expanded} | |||||
clientId={params.id} | |||||
/> | |||||
</div> | |||||
</MainCard> | |||||
</Grid> | |||||
} | |||||
</ThemeProvider> | |||||
</Grid> | |||||
); | |||||
}; | |||||
export default PdfSearchPage; |
@@ -10,6 +10,8 @@ import AbilityContext from "../components/AbilityProvider"; | |||||
// render - login | // render - login | ||||
const ClientSearchPage = Loadable(lazy( () => import('pages/client/ClientSearchPage'))); | const ClientSearchPage = Loadable(lazy( () => import('pages/client/ClientSearchPage'))); | ||||
const ClientMaintainPage = Loadable(lazy( () => import('pages/client/ClientMaintainPage'))); | const ClientMaintainPage = Loadable(lazy( () => import('pages/client/ClientMaintainPage'))); | ||||
const PdfMaintainPage = Loadable(lazy(() => import('pages/pdf/PdfMaintainPage'))); | |||||
const PdfSearchPage = Loadable(lazy(() => import('pages/pdf/PdfSearchPage'))); | |||||
// ==============================|| AUTH ROUTING ||============================== // | // ==============================|| AUTH ROUTING ||============================== // | ||||
@@ -40,6 +42,26 @@ const ClientRoutes =() => { | |||||
) | ) | ||||
), | ), | ||||
}, | }, | ||||
{ | |||||
path: '/pdf/:id', | |||||
element: ( | |||||
handleRouteAbility( | |||||
ability.can('VIEW', 'DASHBOARD'), | |||||
<PdfSearchPage />, | |||||
<Navigate to="/" /> | |||||
) | |||||
), | |||||
}, | |||||
{ | |||||
path: '/pdf/maintain/:id', | |||||
element: ( | |||||
handleRouteAbility( | |||||
ability.can('VIEW', 'DASHBOARD'), | |||||
<PdfMaintainPage />, | |||||
<Navigate to="/" /> | |||||
) | |||||
), | |||||
}, | |||||
] | ] | ||||
}; | }; | ||||
}; | }; | ||||
@@ -10,7 +10,6 @@ import {Navigate} from "react-router"; | |||||
// render - dashboard | // render - dashboard | ||||
//const DashboardDefault = Loadable(lazy(() => import('pages/dashboard'))); | //const DashboardDefault = Loadable(lazy(() => import('pages/dashboard'))); | ||||
const LIONERDashboard = Loadable(lazy(() => import('pages/lionerdashboard'))); | const LIONERDashboard = Loadable(lazy(() => import('pages/lionerdashboard'))); | ||||
const PDF = Loadable(lazy(() => import('pages/pdf'))); | |||||
const ReminderPage = Loadable(lazy(() => import('pages/lionerReminderPage'))); | const ReminderPage = Loadable(lazy(() => import('pages/lionerReminderPage'))); | ||||
const TemplateSearchPage = Loadable(lazy(() => import('pages/lionerSearchPanel'))); | const TemplateSearchPage = Loadable(lazy(() => import('pages/lionerSearchPanel'))); | ||||
const TemplateMaintainPage = Loadable(lazy(() => import('pages/lionerMaintainSearchTemplatePage'))); | const TemplateMaintainPage = Loadable(lazy(() => import('pages/lionerMaintainSearchTemplatePage'))); | ||||
@@ -53,16 +52,7 @@ const MainRoutes = () => { | |||||
) | ) | ||||
), | ), | ||||
}, | }, | ||||
{ | |||||
path: '/pdf', | |||||
element: ( | |||||
handleRouteAbility( | |||||
ability.can('VIEW', 'DASHBOARD'), | |||||
<PDF />, | |||||
<Navigate to="/pdf" /> | |||||
) | |||||
), | |||||
}, | |||||
// { | // { | ||||
// path: '/reminder', | // path: '/reminder', | ||||
// element: ( | // element: ( | ||||